PostgreSQL Source Code git master
Loading...
Searching...
No Matches
spgist_name_ops.c
Go to the documentation of this file.
1/*--------------------------------------------------------------------------
2 *
3 * spgist_name_ops.c
4 * Test opclass for SP-GiST
5 *
6 * This indexes input values of type "name", but the index storage is "text",
7 * with the same choices as made in the core SP-GiST text_ops opclass.
8 * Much of the code is identical to src/backend/access/spgist/spgtextproc.c,
9 * which see for a more detailed header comment.
10 *
11 * Unlike spgtextproc.c, we don't bother with collation-aware logic.
12 *
13 *
14 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
15 * Portions Copyright (c) 1994, Regents of the University of California
16 *
17 * IDENTIFICATION
18 * src/test/modules/spgist_name_ops/spgist_name_ops.c
19 *
20 * -------------------------------------------------------------------------
21 */
22#include "postgres.h"
23
24#include "access/spgist.h"
25#include "catalog/pg_type.h"
26#include "utils/datum.h"
27#include "varatt.h"
28
30
31
35{
36#ifdef NOT_USED
38#endif
40
41 cfg->prefixType = TEXTOID;
42 cfg->labelType = INT2OID;
43 cfg->leafType = TEXTOID;
44 cfg->canReturnData = true;
45 cfg->longValuesOK = true; /* suffixing will shorten long values */
47}
48
49/*
50 * Form a text datum from the given not-necessarily-null-terminated string,
51 * using short varlena header format if possible
52 */
53static Datum
54formTextDatum(const char *data, int datalen)
55{
56 char *p;
57
58 p = (char *) palloc(datalen + VARHDRSZ);
59
60 if (datalen + VARHDRSZ_SHORT <= VARATT_SHORT_MAX)
61 {
63 if (datalen)
64 memcpy(p + VARHDRSZ_SHORT, data, datalen);
65 }
66 else
67 {
68 SET_VARSIZE(p, datalen + VARHDRSZ);
69 memcpy(p + VARHDRSZ, data, datalen);
70 }
71
72 return PointerGetDatum(p);
73}
74
75/*
76 * Find the length of the common prefix of a and b
77 */
78static int
79commonPrefix(const char *a, const char *b, int lena, int lenb)
80{
81 int i = 0;
82
83 while (i < lena && i < lenb && *a == *b)
84 {
85 a++;
86 b++;
87 i++;
88 }
89
90 return i;
91}
92
93/*
94 * Binary search an array of int16 datums for a match to c
95 *
96 * On success, *i gets the match location; on failure, it gets where to insert
97 */
98static bool
99searchChar(const Datum *nodeLabels, int nNodes, int16 c, int *i)
100{
101 int StopLow = 0,
102 StopHigh = nNodes;
103
104 while (StopLow < StopHigh)
105 {
106 int StopMiddle = (StopLow + StopHigh) >> 1;
107 int16 middle = DatumGetInt16(nodeLabels[StopMiddle]);
108
109 if (c < middle)
111 else if (c > middle)
112 StopLow = StopMiddle + 1;
113 else
114 {
115 *i = StopMiddle;
116 return true;
117 }
118 }
119
120 *i = StopHigh;
121 return false;
122}
123
125Datum
127{
131 char *inStr = NameStr(*inName);
132 int inSize = strlen(inStr);
133 char *prefixStr = NULL;
134 int prefixSize = 0;
135 int commonLen = 0;
136 int16 nodeChar = 0;
137 int i = 0;
138
139 /* Check for prefix match, set nodeChar to first byte after prefix */
140 if (in->hasPrefix)
141 {
143
145 prefixSize = VARSIZE_ANY_EXHDR(prefixText);
146
148 prefixStr,
149 inSize - in->level,
150 prefixSize);
151
152 if (commonLen == prefixSize)
153 {
154 if (inSize - in->level > commonLen)
155 nodeChar = *(unsigned char *) (inStr + in->level + commonLen);
156 else
157 nodeChar = -1;
158 }
159 else
160 {
161 /* Must split tuple because incoming value doesn't match prefix */
163
164 if (commonLen == 0)
165 {
166 out->result.splitTuple.prefixHasPrefix = false;
167 }
168 else
169 {
173 }
178 Int16GetDatum(*(unsigned char *) (prefixStr + commonLen));
179
181
182 if (prefixSize - commonLen == 1)
183 {
185 }
186 else
187 {
191 prefixSize - commonLen - 1);
192 }
193
195 }
196 }
197 else if (inSize > in->level)
198 {
199 nodeChar = *(unsigned char *) (inStr + in->level);
200 }
201 else
202 {
203 nodeChar = -1;
204 }
205
206 /* Look up nodeChar in the node label array */
207 if (searchChar(in->nodeLabels, in->nNodes, nodeChar, &i))
208 {
209 /*
210 * Descend to existing node. (If in->allTheSame, the core code will
211 * ignore our nodeN specification here, but that's OK. We still have
212 * to provide the correct levelAdd and restDatum values, and those are
213 * the same regardless of which node gets chosen by core.)
214 */
215 int levelAdd;
216
218 out->result.matchNode.nodeN = i;
219 levelAdd = commonLen;
220 if (nodeChar >= 0)
221 levelAdd++;
222 out->result.matchNode.levelAdd = levelAdd;
223 if (inSize - in->level - levelAdd > 0)
225 formTextDatum(inStr + in->level + levelAdd,
226 inSize - in->level - levelAdd);
227 else
230 }
231 else if (in->allTheSame)
232 {
233 /*
234 * Can't use AddNode action, so split the tuple. The upper tuple has
235 * the same prefix as before and uses a dummy node label -2 for the
236 * lower tuple. The lower tuple has no prefix and the same node
237 * labels as the original tuple.
238 *
239 * Note: it might seem tempting to shorten the upper tuple's prefix,
240 * if it has one, then use its last byte as label for the lower tuple.
241 * But that doesn't win since we know the incoming value matches the
242 * whole prefix: we'd just end up splitting the lower tuple again.
243 */
252 }
253 else
254 {
255 /* Add a node for the not-previously-seen nodeChar value */
256 out->resultType = spgAddNode;
258 out->result.addNode.nodeN = i;
259 }
260
262}
263
264/* The picksplit function is identical to the core opclass, so just use that */
265
267Datum
269{
272 text *reconstructedValue;
274 int maxReconstrLen;
276 int prefixSize = 0;
277 int i;
278
279 /*
280 * Reconstruct values represented at this tuple, including parent data,
281 * prefix of this tuple if any, and the node label if it's non-dummy.
282 * in->level should be the length of the previously reconstructed value,
283 * and the number of bytes added here is prefixSize or prefixSize + 1.
284 *
285 * Recall that reconstructedValues are assumed to be the same type as leaf
286 * datums, so we must use "text" not "name" for them.
287 *
288 * Note: we assume that in->reconstructedValue isn't toasted and doesn't
289 * have a short varlena header. This is okay because it must have been
290 * created by a previous invocation of this routine, and we always emit
291 * long-format reconstructed values.
292 */
293 reconstructedValue = (text *) DatumGetPointer(in->reconstructedValue);
294 Assert(reconstructedValue == NULL ? in->level == 0 :
295 VARSIZE_ANY_EXHDR(reconstructedValue) == in->level);
296
297 maxReconstrLen = in->level + 1;
298 if (in->hasPrefix)
299 {
301 prefixSize = VARSIZE_ANY_EXHDR(prefixText);
302 maxReconstrLen += prefixSize;
303 }
304
307
308 if (in->level)
310 VARDATA(reconstructedValue),
311 in->level);
312 if (prefixSize)
313 memcpy(((char *) VARDATA(reconstrText)) + in->level,
315 prefixSize);
316 /* last byte of reconstrText will be filled in below */
317
318 /*
319 * Scan the child nodes. For each one, complete the reconstructed value
320 * and see if it's consistent with the query. If so, emit an entry into
321 * the output arrays.
322 */
323 out->nodeNumbers = palloc_array(int, in->nNodes);
324 out->levelAdds = palloc_array(int, in->nNodes);
326 out->nNodes = 0;
327
328 for (i = 0; i < in->nNodes; i++)
329 {
331 int thisLen;
332 bool res = true;
333 int j;
334
335 /* If nodeChar is a dummy value, don't include it in data */
336 if (nodeChar <= 0)
338 else
339 {
340 ((unsigned char *) VARDATA(reconstrText))[maxReconstrLen - 1] = nodeChar;
342 }
343
344 for (j = 0; j < in->nkeys; j++)
345 {
346 StrategyNumber strategy = in->scankeys[j].sk_strategy;
347 Name inName;
348 char *inStr;
349 int inSize;
350 int r;
351
353 inStr = NameStr(*inName);
355
357 Min(inSize, thisLen));
358
359 switch (strategy)
360 {
363 if (r > 0)
364 res = false;
365 break;
367 if (r != 0 || inSize < thisLen)
368 res = false;
369 break;
372 if (r < 0)
373 res = false;
374 break;
375 default:
376 elog(ERROR, "unrecognized strategy number: %d",
377 in->scankeys[j].sk_strategy);
378 break;
379 }
380
381 if (!res)
382 break; /* no need to consider remaining conditions */
383 }
384
385 if (res)
386 {
387 out->nodeNumbers[out->nNodes] = i;
388 out->levelAdds[out->nNodes] = thisLen - in->level;
390 out->reconstructedValues[out->nNodes] =
392 out->nNodes++;
393 }
394 }
395
397}
398
400Datum
402{
405 int level = in->level;
406 text *leafValue,
408 char *fullValue;
409 int fullLen;
410 bool res;
411 int j;
412
413 /* all tests are exact */
414 out->recheck = false;
415
416 leafValue = DatumGetTextPP(in->leafDatum);
417
418 /* As above, in->reconstructedValue isn't toasted or short. */
421
422 Assert(reconstrValue == NULL ? level == 0 :
424
425 /* Reconstruct the Name represented by this leaf tuple */
427 fullLen = level + VARSIZE_ANY_EXHDR(leafValue);
429 if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0)
430 {
433 }
434 else
435 {
436 if (level)
438 if (VARSIZE_ANY_EXHDR(leafValue) > 0)
439 memcpy(fullValue + level, VARDATA_ANY(leafValue),
440 VARSIZE_ANY_EXHDR(leafValue));
441 }
443
444 /* Perform the required comparison(s) */
445 res = true;
446 for (j = 0; j < in->nkeys; j++)
447 {
448 StrategyNumber strategy = in->scankeys[j].sk_strategy;
450 char *queryStr = NameStr(*queryName);
451 int queryLen = strlen(queryStr);
452 int r;
453
454 /* Non-collation-aware comparison */
456
457 if (r == 0)
458 {
459 if (queryLen > fullLen)
460 r = -1;
461 else if (queryLen < fullLen)
462 r = 1;
463 }
464
465 switch (strategy)
466 {
468 res = (r < 0);
469 break;
471 res = (r <= 0);
472 break;
474 res = (r == 0);
475 break;
477 res = (r >= 0);
478 break;
480 res = (r > 0);
481 break;
482 default:
483 elog(ERROR, "unrecognized strategy number: %d",
484 in->scankeys[j].sk_strategy);
485 res = false;
486 break;
487 }
488
489 if (!res)
490 break; /* no need to consider remaining conditions */
491 }
492
493 PG_RETURN_BOOL(res);
494}
495
497Datum
#define NameStr(name)
Definition c.h:765
#define Min(x, y)
Definition c.h:997
#define VARHDRSZ
Definition c.h:711
#define Assert(condition)
Definition c.h:873
int16_t int16
Definition c.h:541
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define PG_RETURN_VOID()
Definition fmgr.h:350
#define DatumGetTextPP(X)
Definition fmgr.h:293
#define PG_GETARG_POINTER(n)
Definition fmgr.h:277
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_GETARG_NAME(n)
Definition fmgr.h:279
#define PG_RETURN_DATUM(x)
Definition fmgr.h:354
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition fmgr.h:360
int b
Definition isn.c:74
int a
Definition isn.c:73
int j
Definition isn.c:78
int i
Definition isn.c:77
void * palloc0(Size size)
Definition mcxt.c:1417
void * palloc(Size size)
Definition mcxt.c:1387
#define NAMEDATALEN
const void * data
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
static Name DatumGetName(Datum X)
Definition postgres.h:390
static Datum Int16GetDatum(int16 X)
Definition postgres.h:182
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static int16 DatumGetInt16(Datum X)
Definition postgres.h:172
char * c
static int fb(int x)
@ spgMatchNode
Definition spgist.h:69
@ spgAddNode
Definition spgist.h:70
@ spgSplitTuple
Definition spgist.h:71
Datum spgist_name_compress(PG_FUNCTION_ARGS)
Datum spgist_name_config(PG_FUNCTION_ARGS)
static int commonPrefix(const char *a, const char *b, int lena, int lenb)
PG_MODULE_MAGIC
static Datum formTextDatum(const char *data, int datalen)
Datum spgist_name_leaf_consistent(PG_FUNCTION_ARGS)
Datum spgist_name_inner_consistent(PG_FUNCTION_ARGS)
Datum spgist_name_choose(PG_FUNCTION_ARGS)
static bool searchChar(const Datum *nodeLabels, int nNodes, int16 c, int *i)
uint16 StrategyNumber
Definition stratnum.h:22
#define BTGreaterStrategyNumber
Definition stratnum.h:33
#define BTLessStrategyNumber
Definition stratnum.h:29
#define BTEqualStrategyNumber
Definition stratnum.h:31
#define BTLessEqualStrategyNumber
Definition stratnum.h:30
#define BTGreaterEqualStrategyNumber
Definition stratnum.h:32
Datum sk_argument
Definition skey.h:72
StrategyNumber sk_strategy
Definition skey.h:68
Definition c.h:760
Datum * nodeLabels
Definition spgist.h:64
bool hasPrefix
Definition spgist.h:61
Datum prefixDatum
Definition spgist.h:62
int nNodes
Definition spgist.h:63
Datum datum
Definition spgist.h:55
int level
Definition spgist.h:57
bool allTheSame
Definition spgist.h:60
bool postfixHasPrefix
Definition spgist.h:101
int childNodeN
Definition spgist.h:98
spgChooseResultType resultType
Definition spgist.h:76
struct spgChooseOut::@54::@57 splitTuple
int levelAdd
Definition spgist.h:82
struct spgChooseOut::@54::@56 addNode
Datum nodeLabel
Definition spgist.h:87
Datum * prefixNodeLabels
Definition spgist.h:96
Datum postfixPrefixDatum
Definition spgist.h:102
Datum restDatum
Definition spgist.h:83
int prefixNNodes
Definition spgist.h:95
int nodeN
Definition spgist.h:81
union spgChooseOut::@54 result
Datum prefixPrefixDatum
Definition spgist.h:94
bool prefixHasPrefix
Definition spgist.h:93
struct spgChooseOut::@54::@55 matchNode
Oid leafType
Definition spgist.h:45
bool longValuesOK
Definition spgist.h:47
bool canReturnData
Definition spgist.h:46
Oid labelType
Definition spgist.h:44
Oid prefixType
Definition spgist.h:43
Datum reconstructedValue
Definition spgist.h:140
Datum * reconstructedValues
Definition spgist.h:159
Datum reconstructedValue
Definition spgist.h:175
Definition c.h:706
#define VARHDRSZ_SHORT
Definition varatt.h:278
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition varatt.h:472
static char * VARDATA(const void *PTR)
Definition varatt.h:305
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486
static void SET_VARSIZE_SHORT(void *PTR, Size len)
Definition varatt.h:439
#define VARATT_SHORT_MAX
Definition varatt.h:279
static void SET_VARSIZE(void *PTR, Size len)
Definition varatt.h:432