Skip to content

Commit 54b1cb7

Browse files
committed
Fix results of index-only scans on btree_gist char(N) indexes.
If contrib/btree_gist is used to make a GIST index on a char(N) (bpchar) column, and that column is retrieved via an index-only scan, what came out had all trailing spaces removed. Since that doesn't happen in any other kind of table scan, this is clearly a bug. The cause is that gbt_bpchar_compress() strips trailing spaces (using rtrim1) before a new index entry is made. That was probably a good idea when this code was first written, but since we invented index-only scans, it's not so good. One answer could be to mark this opclass as incapable of index-only scans. But to do so, we'd need an extension module version bump, followed by manual action by DBAs to install the updated version of btree_gist. And it's not really a desirable place to end up, anyway. Instead, let's fix the code by removing the unwanted space-stripping action and adjusting the opclass's comparison logic to ignore trailing spaces as bpchar normally does. This will not hinder cases that work today, since index searches with this logic will act the same whether trailing spaces are stored or not. It will not by itself fix the problem of getting space-stripped results from index-only scans, of course. Users who care about that can REINDEX affected indexes after installing this update, to immediately replace all improperly-truncated index entries. Otherwise, it can be expected that the index's behavior will change incrementally as old entries are replaced by new ones. Per report from Alexander Lakhin. Back-patch to all supported branches. Discussion: https://postgr.es/m/696c995b-b37f-5526-f45d-04abe713179f@gmail.com
1 parent f1e9085 commit 54b1cb7

File tree

3 files changed

+82
-34
lines changed

3 files changed

+82
-34
lines changed

contrib/btree_gist/btree_text.c

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,76 @@ static gbtree_vinfo tinfo =
9090
NULL
9191
};
9292

93+
/* bpchar needs its own comparison rules */
94+
95+
static bool
96+
gbt_bpchargt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo)
97+
{
98+
return DatumGetBool(DirectFunctionCall2Coll(bpchargt,
99+
collation,
100+
PointerGetDatum(a),
101+
PointerGetDatum(b)));
102+
}
103+
104+
static bool
105+
gbt_bpcharge(const void *a, const void *b, Oid collation, FmgrInfo *flinfo)
106+
{
107+
return DatumGetBool(DirectFunctionCall2Coll(bpcharge,
108+
collation,
109+
PointerGetDatum(a),
110+
PointerGetDatum(b)));
111+
}
112+
113+
static bool
114+
gbt_bpchareq(const void *a, const void *b, Oid collation, FmgrInfo *flinfo)
115+
{
116+
return DatumGetBool(DirectFunctionCall2Coll(bpchareq,
117+
collation,
118+
PointerGetDatum(a),
119+
PointerGetDatum(b)));
120+
}
121+
122+
static bool
123+
gbt_bpcharle(const void *a, const void *b, Oid collation, FmgrInfo *flinfo)
124+
{
125+
return DatumGetBool(DirectFunctionCall2Coll(bpcharle,
126+
collation,
127+
PointerGetDatum(a),
128+
PointerGetDatum(b)));
129+
}
130+
131+
static bool
132+
gbt_bpcharlt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo)
133+
{
134+
return DatumGetBool(DirectFunctionCall2Coll(bpcharlt,
135+
collation,
136+
PointerGetDatum(a),
137+
PointerGetDatum(b)));
138+
}
139+
140+
static int32
141+
gbt_bpcharcmp(const void *a, const void *b, Oid collation, FmgrInfo *flinfo)
142+
{
143+
return DatumGetInt32(DirectFunctionCall2Coll(bpcharcmp,
144+
collation,
145+
PointerGetDatum(a),
146+
PointerGetDatum(b)));
147+
}
148+
149+
static gbtree_vinfo bptinfo =
150+
{
151+
gbt_t_bpchar,
152+
0,
153+
false,
154+
gbt_bpchargt,
155+
gbt_bpcharge,
156+
gbt_bpchareq,
157+
gbt_bpcharle,
158+
gbt_bpcharlt,
159+
gbt_bpcharcmp,
160+
NULL
161+
};
162+
93163

94164
/**************************************************
95165
* Text ops
@@ -112,29 +182,8 @@ gbt_text_compress(PG_FUNCTION_ARGS)
112182
Datum
113183
gbt_bpchar_compress(PG_FUNCTION_ARGS)
114184
{
115-
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
116-
GISTENTRY *retval;
117-
118-
if (tinfo.eml == 0)
119-
{
120-
tinfo.eml = pg_database_encoding_max_length();
121-
}
122-
123-
if (entry->leafkey)
124-
{
125-
126-
Datum d = DirectFunctionCall1(rtrim1, entry->key);
127-
GISTENTRY trim;
128-
129-
gistentryinit(trim, d,
130-
entry->rel, entry->page,
131-
entry->offset, true);
132-
retval = gbt_var_compress(&trim, &tinfo);
133-
}
134-
else
135-
retval = entry;
136-
137-
PG_RETURN_POINTER(retval);
185+
/* This should never have been distinct from gbt_text_compress */
186+
return gbt_text_compress(fcinfo);
138187
}
139188

140189

@@ -179,18 +228,17 @@ gbt_bpchar_consistent(PG_FUNCTION_ARGS)
179228
bool retval;
180229
GBT_VARKEY *key = (GBT_VARKEY *) DatumGetPointer(entry->key);
181230
GBT_VARKEY_R r = gbt_var_key_readable(key);
182-
void *trim = (void *) DatumGetPointer(DirectFunctionCall1(rtrim1, PointerGetDatum(query)));
183231

184232
/* All cases served by this function are exact */
185233
*recheck = false;
186234

187-
if (tinfo.eml == 0)
235+
if (bptinfo.eml == 0)
188236
{
189-
tinfo.eml = pg_database_encoding_max_length();
237+
bptinfo.eml = pg_database_encoding_max_length();
190238
}
191239

192-
retval = gbt_var_consistent(&r, trim, strategy, PG_GET_COLLATION(),
193-
GIST_LEAF(entry), &tinfo, fcinfo->flinfo);
240+
retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(),
241+
GIST_LEAF(entry), &bptinfo, fcinfo->flinfo);
194242
PG_RETURN_BOOL(retval);
195243
}
196244

contrib/btree_gist/expected/char.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ SELECT * FROM chartmp WHERE a BETWEEN '31a' AND '31c';
7575
(2 rows)
7676

7777
SELECT * FROM chartmp WHERE a BETWEEN '31a' AND '31c';
78-
a
79-
------
80-
31b0
78+
a
79+
----------------------------------
80+
31b0
8181
(1 row)
8282

contrib/btree_gist/expected/char_1.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ SELECT * FROM chartmp WHERE a BETWEEN '31a' AND '31c';
7575
(2 rows)
7676

7777
SELECT * FROM chartmp WHERE a BETWEEN '31a' AND '31c';
78-
a
79-
------
80-
31b0
78+
a
79+
----------------------------------
80+
31b0
8181
(1 row)
8282

0 commit comments

Comments
 (0)