2010-09-20 22:08:53 +02:00
|
|
|
/* contrib/ltree/ltree.h */
|
2006-03-11 05:38:42 +01:00
|
|
|
|
2002-07-30 18:40:34 +02:00
|
|
|
#ifndef __LTREE_H__
|
|
|
|
#define __LTREE_H__
|
|
|
|
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "fmgr.h"
|
2008-06-30 20:30:48 +02:00
|
|
|
#include "tsearch/ts_locale.h"
|
2014-02-17 15:33:31 +01:00
|
|
|
#include "utils/memutils.h"
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2020-03-30 01:14:15 +02:00
|
|
|
|
|
|
|
/* ltree */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We want the maximum length of a label to be encoding-independent, so
|
|
|
|
* set it somewhat arbitrarily at 255 characters (not bytes), while using
|
|
|
|
* uint16 fields to hold the byte length.
|
|
|
|
*/
|
|
|
|
#define LTREE_LABEL_MAX_CHARS 255
|
|
|
|
|
2021-07-29 02:01:23 +02:00
|
|
|
/*
|
|
|
|
* LOWER_NODE used to be defined in the Makefile via the compile flags.
|
|
|
|
* However the MSVC build scripts neglected to do the same which resulted in
|
|
|
|
* MSVC builds not using LOWER_NODE. Since then, the MSVC scripts have been
|
|
|
|
* modified to look for -D compile flags in Makefiles, so here, in order to
|
|
|
|
* get the historic behavior of LOWER_NODE not being defined on MSVC, we only
|
|
|
|
* define it when not building in that environment. This is important as we
|
2022-04-11 10:49:41 +02:00
|
|
|
* want to maintain the same LOWER_NODE behavior after a pg_upgrade.
|
2021-07-29 02:01:23 +02:00
|
|
|
*/
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
#define LOWER_NODE
|
|
|
|
#endif
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
2020-03-30 01:14:15 +02:00
|
|
|
uint16 len; /* label string length in bytes */
|
2015-02-20 06:11:42 +01:00
|
|
|
char name[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} ltree_level;
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2008-06-30 20:30:48 +02:00
|
|
|
#define LEVEL_HDRSIZE (offsetof(ltree_level,name))
|
2002-08-04 07:02:50 +02:00
|
|
|
#define LEVEL_NEXT(x) ( (ltree_level*)( ((char*)(x)) + MAXALIGN(((ltree_level*)(x))->len + LEVEL_HDRSIZE) ) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
2007-02-28 23:44:38 +01:00
|
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
2020-03-30 01:14:15 +02:00
|
|
|
uint16 numlevel; /* number of labels */
|
|
|
|
/* Array of maxalign'd ltree_level structs follows: */
|
2015-02-20 06:11:42 +01:00
|
|
|
char data[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} ltree;
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2008-06-30 20:30:48 +02:00
|
|
|
#define LTREE_HDRSIZE MAXALIGN( offsetof(ltree, data) )
|
2002-08-04 07:02:50 +02:00
|
|
|
#define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
|
2020-03-28 22:09:51 +01:00
|
|
|
#define LTREE_MAX_LEVELS PG_UINT16_MAX /* ltree.numlevel is uint16 */
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* lquery */
|
|
|
|
|
2020-03-30 01:14:15 +02:00
|
|
|
/* lquery_variant: one branch of some OR'ed alternatives */
|
2002-09-04 22:31:48 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
2020-03-30 01:14:15 +02:00
|
|
|
int32 val; /* CRC of label string */
|
|
|
|
uint16 len; /* label string length in bytes */
|
2020-03-28 23:31:05 +01:00
|
|
|
uint8 flag; /* see LVAR_xxx flags below */
|
2015-02-20 06:11:42 +01:00
|
|
|
char name[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} lquery_variant;
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2020-03-30 01:14:15 +02:00
|
|
|
/*
|
|
|
|
* Note: these macros contain too many MAXALIGN calls and so will sometimes
|
|
|
|
* overestimate the space needed for an lquery_variant. However, we can't
|
|
|
|
* change it without breaking on-disk compatibility for lquery.
|
|
|
|
*/
|
2008-06-30 20:30:48 +02:00
|
|
|
#define LVAR_HDRSIZE MAXALIGN(offsetof(lquery_variant, name))
|
2002-08-04 07:02:50 +02:00
|
|
|
#define LVAR_NEXT(x) ( (lquery_variant*)( ((char*)(x)) + MAXALIGN(((lquery_variant*)(x))->len) + LVAR_HDRSIZE ) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2020-03-30 01:14:15 +02:00
|
|
|
#define LVAR_ANYEND 0x01 /* '*' flag: prefix match */
|
|
|
|
#define LVAR_INCASE 0x02 /* '@' flag: case-insensitive match */
|
|
|
|
#define LVAR_SUBLEXEME 0x04 /* '%' flag: word-wise match */
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2020-03-30 01:14:15 +02:00
|
|
|
/*
|
|
|
|
* In an lquery_level, "flag" contains the union of the variants' flags
|
|
|
|
* along with possible LQL_xxx flags; so those bit sets can't overlap.
|
Fix lquery's NOT handling, and add ability to quantify non-'*' items.
The existing implementation of the ltree ~ lquery match operator is
sufficiently complex and undocumented that it's hard to tell exactly
what it does. But one thing it clearly gets wrong is the combination
of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*'
should, by any ordinary understanding of regular expression behavior,
match any ltree that has at least one label that's not "foo". As best
we can tell by experimentation, what it's actually matching is any
ltree in which *no* label is "foo". That's surprising, and not at all
what the documentation says.
Now, that's arguably a useful behavior, so if we rewrite to fix the
bug we should provide some other way to get it. To do so, add the
ability to attach lquery quantifiers to non-'*' items as well as '*'s.
Then the pattern '!foo{,}' expresses "any ltree in which no label is
foo". For backwards compatibility, the default quantifier for non-'*'
items has to be "{1}", although the default for '*' items is '{,}'.
I wouldn't have done it like that in a green field, but it's not
totally horrible.
Armed with that, rewrite checkCond() from scratch. Treating '*' and
non-'*' items alike makes it simpler, not more complicated, so that
the function actually gets a lot shorter than it was.
Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very
ancient bug report from M. Palm
Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
|
|
|
*
|
|
|
|
* "low" and "high" are nominally the minimum and maximum number of matches.
|
|
|
|
* However, for backwards compatibility with pre-v13 on-disk lqueries,
|
|
|
|
* non-'*' levels (those with numvar > 0) only have valid low/high if the
|
|
|
|
* LQL_COUNT flag is set; otherwise those fields are zero, but the behavior
|
|
|
|
* is as if they were both 1.
|
2020-03-30 01:14:15 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
2020-03-28 23:31:05 +01:00
|
|
|
uint16 totallen; /* total length of this level, in bytes */
|
2020-03-30 01:14:15 +02:00
|
|
|
uint16 flag; /* see LQL_xxx and LVAR_xxx flags */
|
2020-03-28 23:31:05 +01:00
|
|
|
uint16 numvar; /* number of variants; 0 means '*' */
|
Fix lquery's NOT handling, and add ability to quantify non-'*' items.
The existing implementation of the ltree ~ lquery match operator is
sufficiently complex and undocumented that it's hard to tell exactly
what it does. But one thing it clearly gets wrong is the combination
of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*'
should, by any ordinary understanding of regular expression behavior,
match any ltree that has at least one label that's not "foo". As best
we can tell by experimentation, what it's actually matching is any
ltree in which *no* label is "foo". That's surprising, and not at all
what the documentation says.
Now, that's arguably a useful behavior, so if we rewrite to fix the
bug we should provide some other way to get it. To do so, add the
ability to attach lquery quantifiers to non-'*' items as well as '*'s.
Then the pattern '!foo{,}' expresses "any ltree in which no label is
foo". For backwards compatibility, the default quantifier for non-'*'
items has to be "{1}", although the default for '*' items is '{,}'.
I wouldn't have done it like that in a green field, but it's not
totally horrible.
Armed with that, rewrite checkCond() from scratch. Treating '*' and
non-'*' items alike makes it simpler, not more complicated, so that
the function actually gets a lot shorter than it was.
Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very
ancient bug report from M. Palm
Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
|
|
|
uint16 low; /* minimum repeat count */
|
|
|
|
uint16 high; /* maximum repeat count */
|
2020-03-28 23:31:05 +01:00
|
|
|
/* Array of maxalign'd lquery_variant structs follows: */
|
2015-02-20 06:11:42 +01:00
|
|
|
char variants[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} lquery_level;
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2008-06-30 20:30:48 +02:00
|
|
|
#define LQL_HDRSIZE MAXALIGN( offsetof(lquery_level,variants) )
|
2002-09-04 22:31:48 +02:00
|
|
|
#define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + MAXALIGN(((lquery_level*)(x))->totallen) ) )
|
2002-08-04 07:02:50 +02:00
|
|
|
#define LQL_FIRST(x) ( (lquery_variant*)( ((char*)(x))+LQL_HDRSIZE ) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2020-03-30 01:14:15 +02:00
|
|
|
#define LQL_NOT 0x10 /* level has '!' (NOT) prefix */
|
Fix lquery's NOT handling, and add ability to quantify non-'*' items.
The existing implementation of the ltree ~ lquery match operator is
sufficiently complex and undocumented that it's hard to tell exactly
what it does. But one thing it clearly gets wrong is the combination
of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*'
should, by any ordinary understanding of regular expression behavior,
match any ltree that has at least one label that's not "foo". As best
we can tell by experimentation, what it's actually matching is any
ltree in which *no* label is "foo". That's surprising, and not at all
what the documentation says.
Now, that's arguably a useful behavior, so if we rewrite to fix the
bug we should provide some other way to get it. To do so, add the
ability to attach lquery quantifiers to non-'*' items as well as '*'s.
Then the pattern '!foo{,}' expresses "any ltree in which no label is
foo". For backwards compatibility, the default quantifier for non-'*'
items has to be "{1}", although the default for '*' items is '{,}'.
I wouldn't have done it like that in a green field, but it's not
totally horrible.
Armed with that, rewrite checkCond() from scratch. Treating '*' and
non-'*' items alike makes it simpler, not more complicated, so that
the function actually gets a lot shorter than it was.
Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very
ancient bug report from M. Palm
Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
|
|
|
#define LQL_COUNT 0x20 /* level is non-'*' and has repeat counts */
|
2020-03-28 23:31:05 +01:00
|
|
|
|
2002-07-30 18:40:34 +02:00
|
|
|
#ifdef LOWER_NODE
|
2006-03-01 07:30:32 +01:00
|
|
|
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEME ) ) == 0 )
|
2002-07-30 18:40:34 +02:00
|
|
|
#else
|
2006-03-01 07:30:32 +01:00
|
|
|
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEME | LVAR_INCASE ) ) == 0 )
|
2002-07-30 18:40:34 +02:00
|
|
|
#endif
|
2002-09-04 22:31:48 +02:00
|
|
|
#define LQL_CANLOOKSIGN(x) FLG_CANLOOKSIGN( ((lquery_level*)(x))->flag )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
2007-02-28 23:44:38 +01:00
|
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
2020-03-28 23:31:05 +01:00
|
|
|
uint16 numlevel; /* number of lquery_levels */
|
2020-03-30 01:14:15 +02:00
|
|
|
uint16 firstgood; /* number of leading simple-match levels */
|
2020-03-28 23:31:05 +01:00
|
|
|
uint16 flag; /* see LQUERY_xxx flags below */
|
|
|
|
/* Array of maxalign'd lquery_level structs follows: */
|
2015-02-20 06:11:42 +01:00
|
|
|
char data[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} lquery;
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2008-06-30 20:30:48 +02:00
|
|
|
#define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) )
|
2002-08-04 07:02:50 +02:00
|
|
|
#define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
|
2020-03-28 22:09:51 +01:00
|
|
|
#define LQUERY_MAX_LEVELS PG_UINT16_MAX /* lquery.numlevel is uint16 */
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
#define LQUERY_HASNOT 0x01
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
#define ISALNUM(x) ( t_isalpha(x) || t_isdigit(x) || ( pg_mblen(x) == 1 && t_iseq((x), '_') ) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/* full text query */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* item in polish notation with back link
|
|
|
|
* to left operand
|
|
|
|
*/
|
|
|
|
typedef struct ITEM
|
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
int16 type;
|
|
|
|
int16 left;
|
|
|
|
int32 val;
|
2002-07-30 18:40:34 +02:00
|
|
|
uint8 flag;
|
2002-09-04 22:31:48 +02:00
|
|
|
/* user-friendly value */
|
|
|
|
uint8 length;
|
|
|
|
uint16 distance;
|
2009-06-11 16:49:15 +02:00
|
|
|
} ITEM;
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
*Storage:
|
2002-09-04 22:31:48 +02:00
|
|
|
* (len)(size)(array of ITEM)(array of operand in user-friendly form)
|
2002-07-30 18:40:34 +02:00
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
2007-02-28 23:44:38 +01:00
|
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 size;
|
2015-02-20 06:11:42 +01:00
|
|
|
char data[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} ltxtquery;
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2012-06-25 00:51:46 +02:00
|
|
|
#define HDRSIZEQT MAXALIGN(VARHDRSZ + sizeof(int32))
|
2005-05-25 23:40:43 +02:00
|
|
|
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) )
|
2014-02-17 15:33:31 +01:00
|
|
|
#define LTXTQUERY_TOO_BIG(size,lenofoperand) \
|
|
|
|
((size) > (MaxAllocSize - HDRSIZEQT - (lenofoperand)) / sizeof(ITEM))
|
2002-07-30 18:40:34 +02:00
|
|
|
#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
|
2002-09-04 22:31:48 +02:00
|
|
|
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
#define ISOPERATOR(x) ( (x)=='!' || (x)=='&' || (x)=='|' || (x)=='(' || (x)==')' )
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
#define END 0
|
|
|
|
#define ERR 1
|
|
|
|
#define VAL 2
|
|
|
|
#define OPR 3
|
|
|
|
#define OPEN 4
|
|
|
|
#define CLOSE 5
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
|
|
|
#define VALTRUE 6 /* for stop words */
|
2002-09-04 22:31:48 +02:00
|
|
|
#define VALFALSE 7
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* use in array iterator */
|
2022-07-18 03:50:14 +02:00
|
|
|
PGDLLEXPORT Datum ltree_isparent(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltree_risparent(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltq_regex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltq_rregex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum lt_q_regex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum lt_q_rregex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltxtq_exec(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltxtq_rexec(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _ltq_regex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _ltq_rregex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _lt_q_regex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _lt_q_rregex(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _ltxtq_exec(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _ltxtq_rexec(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _ltree_isparent(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum _ltree_risparent(PG_FUNCTION_ARGS);
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/* Concatenation functions */
|
2022-07-18 03:50:14 +02:00
|
|
|
PGDLLEXPORT Datum ltree_addltree(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltree_addtext(PG_FUNCTION_ARGS);
|
|
|
|
PGDLLEXPORT Datum ltree_textadd(PG_FUNCTION_ARGS);
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/* Util function */
|
2022-07-18 03:50:14 +02:00
|
|
|
PGDLLEXPORT Datum ltree_in(PG_FUNCTION_ARGS);
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2019-05-22 19:04:48 +02:00
|
|
|
bool ltree_execute(ITEM *curitem, void *checkval,
|
|
|
|
bool calcnot, bool (*chkcond) (void *checkval, ITEM *val));
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
int ltree_compare(const ltree *a, const ltree *b);
|
|
|
|
bool inner_isparent(const ltree *c, const ltree *p);
|
2019-05-22 19:04:48 +02:00
|
|
|
bool compare_subnode(ltree_level *t, char *q, int len,
|
|
|
|
int (*cmpptr) (const char *, const char *, size_t), bool anyend);
|
2009-06-11 16:49:15 +02:00
|
|
|
ltree *lca_inner(ltree **a, int len);
|
|
|
|
int ltree_strncasecmp(const char *a, const char *b, size_t s);
|
2002-07-30 18:40:34 +02:00
|
|
|
|
2017-09-18 21:21:23 +02:00
|
|
|
/* fmgr macros for ltree objects */
|
|
|
|
#define DatumGetLtreeP(X) ((ltree *) PG_DETOAST_DATUM(X))
|
|
|
|
#define DatumGetLtreePCopy(X) ((ltree *) PG_DETOAST_DATUM_COPY(X))
|
|
|
|
#define PG_GETARG_LTREE_P(n) DatumGetLtreeP(PG_GETARG_DATUM(n))
|
|
|
|
#define PG_GETARG_LTREE_P_COPY(n) DatumGetLtreePCopy(PG_GETARG_DATUM(n))
|
|
|
|
|
|
|
|
#define DatumGetLqueryP(X) ((lquery *) PG_DETOAST_DATUM(X))
|
|
|
|
#define DatumGetLqueryPCopy(X) ((lquery *) PG_DETOAST_DATUM_COPY(X))
|
|
|
|
#define PG_GETARG_LQUERY_P(n) DatumGetLqueryP(PG_GETARG_DATUM(n))
|
|
|
|
#define PG_GETARG_LQUERY_P_COPY(n) DatumGetLqueryPCopy(PG_GETARG_DATUM(n))
|
|
|
|
|
|
|
|
#define DatumGetLtxtqueryP(X) ((ltxtquery *) PG_DETOAST_DATUM(X))
|
|
|
|
#define DatumGetLtxtqueryPCopy(X) ((ltxtquery *) PG_DETOAST_DATUM_COPY(X))
|
|
|
|
#define PG_GETARG_LTXTQUERY_P(n) DatumGetLtxtqueryP(PG_GETARG_DATUM(n))
|
|
|
|
#define PG_GETARG_LTXTQUERY_P_COPY(n) DatumGetLtxtqueryPCopy(PG_GETARG_DATUM(n))
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/* GiST support for ltree */
|
|
|
|
|
|
|
|
#define BITBYTE 8
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
|
2022-03-16 09:41:18 +01:00
|
|
|
#define LTREE_SIGLEN_DEFAULT (2 * sizeof(int32))
|
|
|
|
#define LTREE_SIGLEN_MAX GISTMaxIndexKeySize
|
|
|
|
#define LTREE_GET_SIGLEN() (PG_HAS_OPCLASS_OPTIONS() ? \
|
|
|
|
((LtreeGistOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
|
|
|
|
LTREE_SIGLEN_DEFAULT)
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
|
2002-07-30 18:40:34 +02:00
|
|
|
typedef unsigned char *BITVECP;
|
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define LOOPBYTE(siglen) \
|
|
|
|
for(i = 0; i < (siglen); i++)
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
|
|
|
|
#define GETBITBYTE(x,i) ( ((unsigned char)(x)) >> i & 0x01 )
|
|
|
|
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
|
|
|
|
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
|
|
|
|
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
|
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
|
|
|
|
#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* type of index key for ltree. Tree are combined B-Tree and R-Tree
|
|
|
|
* Storage:
|
2002-09-04 22:31:48 +02:00
|
|
|
* Leaf pages
|
2002-07-30 18:40:34 +02:00
|
|
|
* (len)(flag)(ltree)
|
|
|
|
* Non-Leaf
|
2002-09-04 22:31:48 +02:00
|
|
|
* (len)(flag)(sign)(left_ltree)(right_ltree)
|
2002-07-30 18:40:34 +02:00
|
|
|
* ALLTRUE: (len)(flag)(left_ltree)(right_ltree)
|
2002-09-04 22:31:48 +02:00
|
|
|
*
|
2002-07-30 18:40:34 +02:00
|
|
|
*/
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
2007-02-28 23:44:38 +01:00
|
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
2002-09-04 22:31:48 +02:00
|
|
|
uint32 flag;
|
2015-02-20 06:11:42 +01:00
|
|
|
char data[FLEXIBLE_ARRAY_MEMBER];
|
2009-06-11 16:49:15 +02:00
|
|
|
} ltree_gist;
|
2002-09-04 22:31:48 +02:00
|
|
|
|
|
|
|
#define LTG_ONENODE 0x01
|
|
|
|
#define LTG_ALLTRUE 0x02
|
|
|
|
#define LTG_NORIGHT 0x04
|
|
|
|
|
2007-02-28 23:44:38 +01:00
|
|
|
#define LTG_HDRSIZE MAXALIGN(VARHDRSZ + sizeof(uint32))
|
2002-09-04 22:31:48 +02:00
|
|
|
#define LTG_SIGN(x) ( (BITVECP)( ((char*)(x))+LTG_HDRSIZE ) )
|
|
|
|
#define LTG_NODE(x) ( (ltree*)( ((char*)(x))+LTG_HDRSIZE ) )
|
|
|
|
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
|
|
|
|
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
|
|
|
|
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define LTG_LNODE(x, siglen) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
|
|
|
|
#define LTG_RENODE(x, siglen) ( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
|
|
|
|
#define LTG_RNODE(x, siglen) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
|
|
|
|
#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
|
2002-07-30 18:40:34 +02:00
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
|
2020-05-14 19:06:38 +02:00
|
|
|
ltree *left, ltree *right);
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/* GiST support for ltree[] */
|
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define LTREE_ASIGLEN_DEFAULT (7 * sizeof(int32))
|
|
|
|
#define LTREE_ASIGLEN_MAX GISTMaxIndexKeySize
|
|
|
|
#define LTREE_GET_ASIGLEN() (PG_HAS_OPCLASS_OPTIONS() ? \
|
|
|
|
((LtreeGistOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
|
|
|
|
LTREE_ASIGLEN_DEFAULT)
|
|
|
|
#define ASIGLENBIT(siglen) ((siglen) * BITBYTE)
|
|
|
|
|
|
|
|
#define ALOOPBYTE(siglen) \
|
|
|
|
for (i = 0; i < (siglen); i++)
|
2002-07-30 18:40:34 +02:00
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
|
|
|
|
#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
|
2002-07-30 18:40:34 +02:00
|
|
|
|
Implement operator class parameters
PostgreSQL provides set of template index access methods, where opclasses have
much freedom in the semantics of indexing. These index AMs are GiST, GIN,
SP-GiST and BRIN. There opclasses define representation of keys, operations on
them and supported search strategies. So, it's natural that opclasses may be
faced some tradeoffs, which require user-side decision. This commit implements
opclass parameters allowing users to set some values, which tell opclass how to
index the particular dataset.
This commit doesn't introduce new storage in system catalog. Instead it uses
pg_attribute.attoptions, which is used for table column storage options but
unused for index attributes.
In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions. Options
are set to fn_expr as the constant bytea expression. It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.
This commit comes with some examples of opclass options usage. We parametrize
signature length in GiST. That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and
gist_hstore_ops. Also we parametrize maximum number of integer ranges for
gist__int_ops. However, the main future usage of this feature is expected
to be json, where users would be able to specify which way to index particular
json parts.
Catversion is bumped.
Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
|
|
|
/* gist_ltree_ops and gist__ltree_ops opclass options */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
|
|
|
int siglen; /* signature length in bytes */
|
|
|
|
} LtreeGistOptions;
|
2002-07-30 18:40:34 +02:00
|
|
|
|
|
|
|
/* type of key is the same to ltree_gist */
|
|
|
|
|
|
|
|
#endif
|