Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync Fork from Upstream Repo #29

Merged
merged 4 commits into from Jan 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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