Skip to content

Commit 8c153fc

Browse files
committed
Fix GIN's shimTriConsistentFn to not corrupt its input.
Commit 0f21db3 made an assumption that GIN triConsistentFns would not modify their input entryRes[] arrays. But in fact, the "shim" triConsistentFn that we use for opclasses that don't supply their own did exactly that, potentially leading to wrong answers from a GIN index search. Through bad luck, none of the test cases that we have for such opclasses exposed the bug. One response to this could be that the assumption of consistency check functions not modifying entryRes[] arrays is a bad one, but it still seems reasonable to me. Notably, shimTriConsistentFn is itself assuming that with respect to the underlying boolean consistentFn, so it's sure being self-centered in supposing that it gets to do so. Fortunately, it's quite simple to fix shimTriConsistentFn to restore the entry-time state of entryRes[], so let's do that instead. This issue doesn't affect any core GIN opclasses, since they all supply their own triConsistentFns. It does affect contrib modules btree_gin, hstore, and intarray. Along the way, I (tgl) noticed that shimTriConsistentFn failed to pick up on a "recheck" flag returned by its first call to the boolean consistentFn. This may be only a latent problem, since it would be unlikely for a consistentFn to set recheck for the all-false case and not any other cases. (Indeed, none of our contrib modules do that.) Nonetheless, it's formally wrong. Reported-by: Vinod Sridharan <vsridh90@gmail.com> Author: Vinod Sridharan <vsridh90@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CAFMdLD7XzsXfi1+DpTqTgrD8XU0i2C99KuF=5VHLWjx4C1pkcg@mail.gmail.com Backpatch-through: 13
1 parent 3339847 commit 8c153fc

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

contrib/intarray/expected/_int.out

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,12 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
492492
6344
493493
(1 row)
494494

495+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
496+
count
497+
-------
498+
12
499+
(1 row)
500+
495501
SET enable_seqscan = off; -- not all of these would use index by default
496502
CREATE INDEX text_idx on test__int using gist ( a gist__int_ops );
497503
SELECT count(*) from test__int WHERE a && '{23,50}';
@@ -566,6 +572,12 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
566572
6344
567573
(1 row)
568574

575+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
576+
count
577+
-------
578+
12
579+
(1 row)
580+
569581
INSERT INTO test__int SELECT array(SELECT x FROM generate_series(1, 1001) x); -- should fail
570582
ERROR: input array is too big (199 maximum allowed, 1001 current), use gist__intbig_ops opclass instead
571583
DROP INDEX text_idx;
@@ -648,6 +660,12 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
648660
6344
649661
(1 row)
650662

663+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
664+
count
665+
-------
666+
12
667+
(1 row)
668+
651669
DROP INDEX text_idx;
652670
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 0));
653671
ERROR: value 0 out of bounds for option "siglen"
@@ -728,6 +746,12 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
728746
6344
729747
(1 row)
730748

749+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
750+
count
751+
-------
752+
12
753+
(1 row)
754+
731755
DROP INDEX text_idx;
732756
CREATE INDEX text_idx on test__int using gist ( a gist__intbig_ops );
733757
SELECT count(*) from test__int WHERE a && '{23,50}';
@@ -802,6 +826,12 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
802826
6344
803827
(1 row)
804828

829+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
830+
count
831+
-------
832+
12
833+
(1 row)
834+
805835
DROP INDEX text_idx;
806836
CREATE INDEX text_idx on test__int using gin ( a gin__int_ops );
807837
SELECT count(*) from test__int WHERE a && '{23,50}';
@@ -876,6 +906,12 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
876906
6344
877907
(1 row)
878908

909+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
910+
count
911+
-------
912+
12
913+
(1 row)
914+
879915
DROP INDEX text_idx;
880916
-- Repeat the same queries with an extended data set. The data set is the
881917
-- same that we used before, except that each element in the array is
@@ -968,4 +1004,10 @@ SELECT count(*) from more__int WHERE a @@ '!20 & !21';
9681004
6344
9691005
(1 row)
9701006

1007+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
1008+
count
1009+
-------
1010+
12
1011+
(1 row)
1012+
9711013
RESET enable_seqscan;

contrib/intarray/sql/_int.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
107107
SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
108108
SELECT count(*) from test__int WHERE a @@ '20 | !21';
109109
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
110+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
110111

111112
SET enable_seqscan = off; -- not all of these would use index by default
112113

@@ -124,6 +125,7 @@ SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
124125
SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
125126
SELECT count(*) from test__int WHERE a @@ '20 | !21';
126127
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
128+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
127129

128130
INSERT INTO test__int SELECT array(SELECT x FROM generate_series(1, 1001) x); -- should fail
129131

@@ -144,6 +146,7 @@ SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
144146
SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
145147
SELECT count(*) from test__int WHERE a @@ '20 | !21';
146148
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
149+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
147150

148151
DROP INDEX text_idx;
149152
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 0));
@@ -162,6 +165,7 @@ SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
162165
SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
163166
SELECT count(*) from test__int WHERE a @@ '20 | !21';
164167
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
168+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
165169

166170
DROP INDEX text_idx;
167171
CREATE INDEX text_idx on test__int using gist ( a gist__intbig_ops );
@@ -178,6 +182,7 @@ SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
178182
SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
179183
SELECT count(*) from test__int WHERE a @@ '20 | !21';
180184
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
185+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
181186

182187
DROP INDEX text_idx;
183188
CREATE INDEX text_idx on test__int using gin ( a gin__int_ops );
@@ -194,6 +199,7 @@ SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
194199
SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
195200
SELECT count(*) from test__int WHERE a @@ '20 | !21';
196201
SELECT count(*) from test__int WHERE a @@ '!20 & !21';
202+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
197203

198204
DROP INDEX text_idx;
199205

@@ -229,6 +235,7 @@ SELECT count(*) from more__int WHERE a @> '{20,23}' or a @> '{50,68}';
229235
SELECT count(*) from more__int WHERE a @@ '(20&23)|(50&68)';
230236
SELECT count(*) from more__int WHERE a @@ '20 | !21';
231237
SELECT count(*) from more__int WHERE a @@ '!20 & !21';
238+
SELECT count(*) from test__int WHERE a @@ '!2733 & (2738 | 254)';
232239

233240

234241
RESET enable_seqscan;

src/backend/access/gin/ginlogic.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ shimBoolConsistentFn(GinScanKey key)
140140
* every combination is O(n^2), so this is only feasible for a small number of
141141
* MAYBE inputs.
142142
*
143-
* NB: This function modifies the key->entryRes array!
143+
* NB: This function modifies the key->entryRes array. For now that's okay
144+
* so long as we restore the entry-time contents before returning. This may
145+
* need revisiting if we ever invent multithreaded GIN scans, though.
144146
*/
145147
static GinTernaryValue
146148
shimTriConsistentFn(GinScanKey key)
@@ -149,7 +151,7 @@ shimTriConsistentFn(GinScanKey key)
149151
int maybeEntries[MAX_MAYBE_ENTRIES];
150152
int i;
151153
bool boolResult;
152-
bool recheck = false;
154+
bool recheck;
153155
GinTernaryValue curResult;
154156

155157
/*
@@ -169,8 +171,8 @@ shimTriConsistentFn(GinScanKey key)
169171
}
170172

171173
/*
172-
* If none of the inputs were MAYBE, so we can just call consistent
173-
* function as is.
174+
* If none of the inputs were MAYBE, we can just call the consistent
175+
* function as-is.
174176
*/
175177
if (nmaybe == 0)
176178
return directBoolConsistentFn(key);
@@ -179,6 +181,7 @@ shimTriConsistentFn(GinScanKey key)
179181
for (i = 0; i < nmaybe; i++)
180182
key->entryRes[maybeEntries[i]] = GIN_FALSE;
181183
curResult = directBoolConsistentFn(key);
184+
recheck = key->recheckCurItem;
182185

183186
for (;;)
184187
{
@@ -200,13 +203,20 @@ shimTriConsistentFn(GinScanKey key)
200203
recheck |= key->recheckCurItem;
201204

202205
if (curResult != boolResult)
203-
return GIN_MAYBE;
206+
{
207+
curResult = GIN_MAYBE;
208+
break;
209+
}
204210
}
205211

206212
/* TRUE with recheck is taken to mean MAYBE */
207213
if (curResult == GIN_TRUE && recheck)
208214
curResult = GIN_MAYBE;
209215

216+
/* We must restore the original state of the entryRes array */
217+
for (i = 0; i < nmaybe; i++)
218+
key->entryRes[maybeEntries[i]] = GIN_MAYBE;
219+
210220
return curResult;
211221
}
212222

0 commit comments

Comments
 (0)