Skip to content

Commit

Permalink
Merge pull request #29 from postgres/master
Browse files Browse the repository at this point in the history
Sync Fork from Upstream Repo
  • Loading branch information
sthagen committed Jan 18, 2020
2 parents 2dde6f1 + 4b754d6 commit 5c346f3
Show file tree
Hide file tree
Showing 17 changed files with 734 additions and 147 deletions.
101 changes: 101 additions & 0 deletions contrib/pg_trgm/expected/pg_trgm.out
Expand Up @@ -3498,6 +3498,107 @@ select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
1000
(1 row)

-- check handling of indexquals that generate no searchable conditions
explain (costs off)
select count(*) from test_trgm where t like '%99%' and t like '%qwerty%';
QUERY PLAN
-----------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on test_trgm
Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text))
-> Bitmap Index Scan on trgm_idx
Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text))
(5 rows)

select count(*) from test_trgm where t like '%99%' and t like '%qwerty%';
count
-------
19
(1 row)

explain (costs off)
select count(*) from test_trgm where t like '%99%' and t like '%qw%';
QUERY PLAN
-------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on test_trgm
Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text))
-> Bitmap Index Scan on trgm_idx
Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text))
(5 rows)

select count(*) from test_trgm where t like '%99%' and t like '%qw%';
count
-------
19
(1 row)

-- ensure that pending-list items are handled correctly, too
create temp table t_test_trgm(t text COLLATE "C");
create index t_trgm_idx on t_test_trgm using gin (t gin_trgm_ops);
insert into t_test_trgm values ('qwerty99'), ('qwerty01');
explain (costs off)
select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%';
QUERY PLAN
-----------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on t_test_trgm
Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text))
-> Bitmap Index Scan on t_trgm_idx
Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qwerty%'::text))
(5 rows)

select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%';
count
-------
1
(1 row)

explain (costs off)
select count(*) from t_test_trgm where t like '%99%' and t like '%qw%';
QUERY PLAN
-------------------------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on t_test_trgm
Recheck Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text))
-> Bitmap Index Scan on t_trgm_idx
Index Cond: ((t ~~ '%99%'::text) AND (t ~~ '%qw%'::text))
(5 rows)

select count(*) from t_test_trgm where t like '%99%' and t like '%qw%';
count
-------
1
(1 row)

-- run the same queries with sequential scan to check the results
set enable_bitmapscan=off;
set enable_seqscan=on;
select count(*) from test_trgm where t like '%99%' and t like '%qwerty%';
count
-------
19
(1 row)

select count(*) from test_trgm where t like '%99%' and t like '%qw%';
count
-------
19
(1 row)

select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%';
count
-------
1
(1 row)

select count(*) from t_test_trgm where t like '%99%' and t like '%qw%';
count
-------
1
(1 row)

reset enable_bitmapscan;
create table test2(t text COLLATE "C");
insert into test2 values ('abcdef');
insert into test2 values ('quark');
Expand Down
27 changes: 27 additions & 0 deletions contrib/pg_trgm/sql/pg_trgm.sql
Expand Up @@ -55,6 +55,33 @@ select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu098
select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';

-- check handling of indexquals that generate no searchable conditions
explain (costs off)
select count(*) from test_trgm where t like '%99%' and t like '%qwerty%';
select count(*) from test_trgm where t like '%99%' and t like '%qwerty%';
explain (costs off)
select count(*) from test_trgm where t like '%99%' and t like '%qw%';
select count(*) from test_trgm where t like '%99%' and t like '%qw%';
-- ensure that pending-list items are handled correctly, too
create temp table t_test_trgm(t text COLLATE "C");
create index t_trgm_idx on t_test_trgm using gin (t gin_trgm_ops);
insert into t_test_trgm values ('qwerty99'), ('qwerty01');
explain (costs off)
select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%';
select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%';
explain (costs off)
select count(*) from t_test_trgm where t like '%99%' and t like '%qw%';
select count(*) from t_test_trgm where t like '%99%' and t like '%qw%';

-- run the same queries with sequential scan to check the results
set enable_bitmapscan=off;
set enable_seqscan=on;
select count(*) from test_trgm where t like '%99%' and t like '%qwerty%';
select count(*) from test_trgm where t like '%99%' and t like '%qw%';
select count(*) from t_test_trgm where t like '%99%' and t like '%qwerty%';
select count(*) from t_test_trgm where t like '%99%' and t like '%qw%';
reset enable_bitmapscan;

create table test2(t text COLLATE "C");
insert into test2 values ('abcdef');
insert into test2 values ('quark');
Expand Down
84 changes: 64 additions & 20 deletions src/backend/access/gin/ginget.c
Expand Up @@ -528,8 +528,20 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
* order, until the consistent function says that none of the remaining
* entries can form a match, without any items from the required set. The
* rest go to the additional set.
*
* Exclude-only scan keys are known to have no required entries.
*/
if (key->nentries > 1)
if (key->excludeOnly)
{
MemoryContextSwitchTo(so->keyCtx);

key->nrequired = 0;
key->nadditional = key->nentries;
key->additionalEntries = palloc(key->nadditional * sizeof(GinScanEntry));
for (i = 0; i < key->nadditional; i++)
key->additionalEntries[i] = key->scanEntry[i];
}
else if (key->nentries > 1)
{
MemoryContextSwitchTo(so->tempCtx);

Expand Down Expand Up @@ -1008,37 +1020,52 @@ keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key,
minItem = entry->curItem;
}

if (allFinished)
if (allFinished && !key->excludeOnly)
{
/* all entries are finished */
key->isFinished = true;
return;
}

/*
* Ok, we now know that there are no matches < minItem.
*
* If minItem is lossy, it means that there were no exact items on the
* page among requiredEntries, because lossy pointers sort after exact
* items. However, there might be exact items for the same page among
* additionalEntries, so we mustn't advance past them.
*/
if (ItemPointerIsLossyPage(&minItem))
if (!key->excludeOnly)
{
if (GinItemPointerGetBlockNumber(&advancePast) <
GinItemPointerGetBlockNumber(&minItem))
/*
* For a normal scan key, we now know there are no matches < minItem.
*
* If minItem is lossy, it means that there were no exact items on the
* page among requiredEntries, because lossy pointers sort after exact
* items. However, there might be exact items for the same page among
* additionalEntries, so we mustn't advance past them.
*/
if (ItemPointerIsLossyPage(&minItem))
{
if (GinItemPointerGetBlockNumber(&advancePast) <
GinItemPointerGetBlockNumber(&minItem))
{
ItemPointerSet(&advancePast,
GinItemPointerGetBlockNumber(&minItem),
InvalidOffsetNumber);
}
}
else
{
Assert(GinItemPointerGetOffsetNumber(&minItem) > 0);
ItemPointerSet(&advancePast,
GinItemPointerGetBlockNumber(&minItem),
InvalidOffsetNumber);
OffsetNumberPrev(GinItemPointerGetOffsetNumber(&minItem)));
}
}
else
{
Assert(GinItemPointerGetOffsetNumber(&minItem) > 0);
ItemPointerSet(&advancePast,
GinItemPointerGetBlockNumber(&minItem),
OffsetNumberPrev(GinItemPointerGetOffsetNumber(&minItem)));
/*
* excludeOnly scan keys don't have any entries that are necessarily
* present in matching items. So, we consider the item just after
* advancePast.
*/
Assert(key->nrequired == 0);
ItemPointerSet(&minItem,
GinItemPointerGetBlockNumber(&advancePast),
OffsetNumberNext(GinItemPointerGetOffsetNumber(&advancePast)));
}

/*
Expand Down Expand Up @@ -1266,6 +1293,20 @@ scanGetItem(IndexScanDesc scan, ItemPointerData advancePast,
{
GinScanKey key = so->keys + i;

/*
* If we're considering a lossy page, skip excludeOnly keys, They
* can't exclude the whole page anyway.
*/
if (ItemPointerIsLossyPage(item) && key->excludeOnly)
{
/*
* ginNewScanKey() should never mark the first key as
* excludeOnly.
*/
Assert(i > 0);
continue;
}

/* Fetch the next item for this key that is > advancePast. */
keyGetItem(&so->ginstate, so->tempCtx, key, advancePast,
scan->xs_snapshot);
Expand Down Expand Up @@ -1736,11 +1777,14 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
}

/*
* Now return "true" if all scan keys have at least one matching datum
* All scan keys except excludeOnly require at least one entry to match.
* excludeOnly keys are an exception, because their implied
* GIN_CAT_EMPTY_QUERY scanEntry always matches. So return "true" if all
* non-excludeOnly scan keys have at least one match.
*/
for (i = 0; i < so->nkeys; i++)
{
if (pos->hasMatchKey[i] == false)
if (pos->hasMatchKey[i] == false && !so->keys[i].excludeOnly)
return false;
}

Expand Down

0 comments on commit 5c346f3

Please sign in to comment.