Add ltree data type to contrib, from Teodor Sigaev and Oleg Bartunov.

This commit is contained in:
Bruce Momjian 2002-07-30 16:40:34 +00:00
parent 210e64fe08
commit 1dedbf2da5
22 changed files with 14333 additions and 1 deletions

View File

@ -1,4 +1,4 @@
# $Header: /cvsroot/pgsql/contrib/Makefile,v 1.33 2002/07/30 16:32:20 momjian Exp $
# $Header: /cvsroot/pgsql/contrib/Makefile,v 1.34 2002/07/30 16:40:34 momjian Exp $
subdir = contrib
top_builddir = ..
@ -20,6 +20,7 @@ WANTED_DIRS = \
intarray \
isbn_issn \
lo \
ltree \
miscutil \
noupdate \
oid2name \

View File

@ -95,6 +95,10 @@ lo -
Large Object maintenance
by Peter Mount <peter@retep.org.uk>
ltree -
Tree-like data structures
by Teodor Sigaev <teodor@stack.net> and Oleg Bartunov <oleg@sai.msu.su>
mSQL-interface -
mSQL API translation library
by Aldrin Leal <aldrin@americasnet.com>

13
contrib/ltree/Makefile Normal file
View File

@ -0,0 +1,13 @@
subdir = contrib/tree
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
PG_CPPFLAGS = -DLOWER_NODE
MODULE_big = ltree
OBJS = ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
ltxtquery_io.o ltxtquery_op.o ltree_gist.o _ltree_gist.o
DATA_built = ltree.sql
DOCS = README.ltree
REGRESS = ltree
include $(top_srcdir)/contrib/contrib-global.mk

465
contrib/ltree/README.ltree Normal file
View File

@ -0,0 +1,465 @@
contrib/ltree module
ltree - is a PostgreSQL contrib module which contains implementation of data
types, indexed access methods and queries for data organized as a tree-like
structures.
This module will works for PostgreSQL version 7.3.
(patch for 7.2 version is provided, see INSTALLATION)
-------------------------------------------------------------------------------
All work was done by Teodor Sigaev (teodor@stack.net) and Oleg Bartunov
(oleg@sai.msu.su). See http://www.sai.msu.su/~megera/postgres/gist for
additional information. Authors would like to thank Eugeny Rodichev for helpful
discussions. Comments and bug reports are welcome.
-------------------------------------------------------------------------------
LEGAL NOTICES: This module is released under BSD license (as PostgreSQL
itself). This work was done in framework of Russian Scientific Network and
partially supported by Russian Foundation for Basic Research and Stack Group.
-------------------------------------------------------------------------------
MOTIVATION
This is a placeholder for introduction to the problem. Hope, people reading
this document doesn't need it too much :-)
DEFINITIONS
A label of a node is a sequence of one or more words separated by blank
character '_' and containing letters and digits ( for example, [a-zA-Z0-9] for
C locale). The length of a label is limited by 256 bytes.
Example: 'Countries', 'Personal_Services'
A label path of a node is a sequence of one or more dot-separated labels
l1.l2...ln, represents path from root to the node. The length of a label path
is limited by 65Kb, but size <= 2Kb is preferrable. We consider it's not a
strict limitation ( maximal size of label path for DMOZ catalogue - http://
www.dmoz.org, is about 240 bytes !)
Example: 'Top.Countries.Europe.Russia'
We introduce several datatypes:
ltree
- is a datatype for label path.
ltree[]
- is a datatype for arrays of ltree.
lquery
- is a path expression that has regular expression in the label path and
used for ltree matching. Star symbol (*) is used to specify any number of
labels (levels) and could be used at the beginning and the end of lquery,
for example, '*.Europe.*'.
The following quantifiers are recognized for '*' (like in Perl):
{n} Match exactly n levels
{n,} Match at least n levels
{n,m} Match at least n but not more than m levels
{,m} Match at maximum m levels (eq. to {0,m})
It is possible to use several modifiers at the end of a label:
@ Do case-insensitive label matching
* Do prefix matching for a label
% Don't account word separator '_' in label matching, that is
'Russian%' would match 'Russian_nations', but not 'Russian'
lquery could contains logical '!' (NOT) at the beginning of the label and '
|' (OR) to specify possible alternatives for label matching.
Example of lquery:
Top.*{0,2}.sport*@.!football|tennis.Russ*|Spain
a) b) c) d) e)
A label path should
+ a) begins from a node with label 'Top'
+ b) and following zero or 2 labels until
+ c) a node with label beginning from case-insensitive prefix 'sport'
+ d) following node with label not matched 'football' or 'tennis' and
+ e) ends on node with label beginning from 'Russ' or strictly matched
'Spain'.
ltxtquery
- is a datatype for label searching (like type 'query' for full text
searching, see contrib/tsearch). It's possible to use modifiers @,%,* at
the end of word. The meaning of modifiers are the same as for lquery.
Example: 'Europe & Russia*@ & !Transportation'
Search paths contain words 'Europe' and 'Russia*' (case-insensitive) and
not 'Transportation'. Notice, the order of words as they appear in label
path is not important !
OPERATIONS
The following operations are defined for type ltree:
<,>,<=,>=,=, <>
- have their usual meanings. Comparison is doing in the order of direct
tree traversing, children of a node are sorted lexicographic.
ltree @> ltree
- returns TRUE if left argument is an ancestor of right argument (or
equal).
ltree <@ ltree
- returns TRUE if left argument is a descendant of right argument (or
equal).
ltree ~ lquery, lquery ~ ltree
- return TRUE if node represented by ltree satisfies lquery.
ltree @ ltxtquery, ltxtquery @ ltree
- return TRUE if node represented by ltree satisfies ltxtquery.
ltree || ltree, ltree || text, text || ltree
- return concatenated ltree.
Operations for arrays of ltree (ltree[]):
ltree[] @> ltree, ltree <@ ltree[]
- returns TRUE if array ltree[] contains an ancestor of ltree.
ltree @> ltree[], ltree[] <@ ltree
- returns TRUE if array ltree[] contains a descendant of ltree.
ltree[] ~ lquery, lquery ~ ltree[]
- returns TRUE if array ltree[] contains label paths matched lquery.
ltree[] @ ltxtquery, ltxtquery @ ltree[]
- returns TRUE if array ltree[] contains label paths matched ltxtquery
(full text search).
ltree[] ?@> ltree, ltree ?<@ ltree[], ltree[] ?~ lquery, ltree[] ?@ ltxtquery
- returns first element of array ltree[] satisfies corresponding condition
and NULL in vice versa.
REMARK
Operations <@, @>, @ and ~ have analogues - ^<@, ^@>, ^@, ^~, which doesn't use
indices !
INDICES
Various indices could be created to speed up execution of operations:
* B-tree index over ltree:
<, <=, =, =>, >
* GiST index over ltree:
<, <=, =, =>, >, @>, <@, @, ~
Example:
create index path_gist_idx on test using gist_ltree_ops (path);
* GiST index over ltree[]:
ltree[]<@ ltree, ltree @> ltree[], @, ~.
Example:
create index path_gist_idx on test using gist__ltree_ops (array_path);
Notices: This index is lossy.
FUNCTIONS
ltree subltree
ltree subltree(ltree, start, end)
returns subpath of ltree from start (inclusive) until the end.
# select subltree('Top.Child1.Child2',1,2);
subltree
--------
Child1
ltree subpath
ltree subpath(ltree, OFFSET,LEN)
ltree subpath(ltree, OFFSET)
returns subpath of ltree from OFFSET (inclusive) with length LEN.
If OFFSET is negative returns subpath starts that far from the end
of the path. If LENGTH is omitted, returns everything to the end
of the path. If LENGTH is negative, leaves that many labels off
the end of the path.
# select subpath('Top.Child1.Child2',1,2);
subpath
-------
Child1.Child2
# select subpath('Top.Child1.Child2',-2,1);
subpath
---------
Child1
int4 nlevel
int4 nlevel(ltree) - returns level of the node.
# select nlevel('Top.Child1.Child2');
nlevel
--------
3
Note, that arguments start, end, OFFSET, LEN have meaning of level of the node
!
INSTALLATION
cd contrib/ltree
make
make install
make installcheck
for 7.2 one needs to apply patch ( patch < patch.72) before installation !
EXAMPLE OF USAGE
createdb ltreetest
psql ltreetest < /usr/local/pgsql/share/contrib/ltree.sql
psql ltreetest < ltreetest.sql
Now, we have a database ltreetest populated with a data describing hierarchy
shown below:
TOP
/ | \
Science Hobbies Collections
/ | \
Astronomy Amateurs_Astronomy Pictures
/ \ |
Astrophysics Cosmology Astronomy
/ | \
Galaxies Stars Astronauts
Inheritance:
ltreetest=# select path from test where path <@ 'Top.Science';
path
------------------------------------
Top.Science
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
(4 rows)
Matching:
ltreetest=# select path from test where path ~ '*.Astronomy.*';
path
-----------------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
Top.Collections.Pictures.Astronomy
Top.Collections.Pictures.Astronomy.Stars
Top.Collections.Pictures.Astronomy.Galaxies
Top.Collections.Pictures.Astronomy.Astronauts
(7 rows)
ltreetest=# select path from test where path ~ '*.!pictures@.*.Astronomy.*';
path
------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
(3 rows)
Full text search:
ltreetest=# select path from test where path @ 'Astro*% & !pictures@';
path
------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
Top.Hobbies.Amateurs_Astronomy
(4 rows)
ltreetest=# select path from test where path @ 'Astro* & !pictures@';
path
------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
(3 rows)
Using Functions:
ltreetest=# select subpath(path,0,2)||'Space'||subpath(path,2) from test where path <@ 'Top.Science.Astronomy';
?column?
------------------------------------------
Top.Science.Space.Astronomy
Top.Science.Space.Astronomy.Astrophysics
Top.Science.Space.Astronomy.Cosmology
(3 rows)
We could create SQL-function:
CREATE FUNCTION ins_label(ltree, int4, text) RETURNS ltree
AS 'select subpath($1,0,$2) || $3 || subpath($1,$2);'
LANGUAGE SQL WITH (ISCACHABLE);
and previous select could be rewritten as:
ltreetest=# select ins_label(path,2,'Space') from test where path <@ 'Top.Science.Astronomy';
ins_label
------------------------------------------
Top.Science.Space.Astronomy
Top.Science.Space.Astronomy.Astrophysics
Top.Science.Space.Astronomy.Cosmology
(3 rows)
Or with another arguments:
CREATE FUNCTION ins_label(ltree, ltree, text) RETURNS ltree
AS 'select subpath($1,0,nlevel($2)) || $3 || subpath($1,nlevel($2));'
LANGUAGE SQL WITH (ISCACHABLE);
ltreetest=# select ins_label(path,'Top.Science'::ltree,'Space') from test where path <@ 'Top.Science.Astronomy';
ins_label
------------------------------------------
Top.Science.Space.Astronomy
Top.Science.Space.Astronomy.Astrophysics
Top.Science.Space.Astronomy.Cosmology
(3 rows)
ADDITIONAL DATA
To get more feeling from our ltree module you could download
dmozltree-eng.sql.gz (about 3Mb tar.gz archive containing 300,274 nodes),
available from http://www.sai.msu.su/~megera/postgres/gist/ltree/
dmozltree-eng.sql.gz, which is DMOZ catalogue, prepared for use with ltree.
Setup your test database (dmoz), load ltree module and issue command:
zcat dmozltree-eng.sql.gz| psql dmoz
Data will be loaded into database dmoz and all indices will be created.
BENCHMARKS
All runs were performed on my IBM ThinkPad T21 (256 MB RAM, 750Mhz) using DMOZ
data, containing 300,274 nodes (see above for download link). We used some
basic queries typical for walking through catalog.
QUERIES
* Q0: Count all rows (sort of base time for comparison)
select count(*) from dmoz;
count
--------
300274
(1 row)
* Q1: Get direct children (without inheritance)
select path from dmoz where path ~ 'Top.Adult.Arts.Animation.*{1}';
path
-----------------------------------
Top.Adult.Arts.Animation.Cartoons
Top.Adult.Arts.Animation.Anime
(2 rows)
* Q2: The same as Q1 but with counting of successors
select path as parentpath , (select count(*)-1 from dmoz where path <@
p.path) as count from dmoz p where path ~ 'Top.Adult.Arts.Animation.*{1}';
parentpath | count
-----------------------------------+-------
Top.Adult.Arts.Animation.Cartoons | 2
Top.Adult.Arts.Animation.Anime | 61
(2 rows)
* Q3: Get all parents
select path from dmoz where path @> 'Top.Adult.Arts.Animation' order by
path asc;
path
--------------------------
Top
Top.Adult
Top.Adult.Arts
Top.Adult.Arts.Animation
(4 rows)
* Q4: Get all parents with counting of children
select path, (select count(*)-1 from dmoz where path <@ p.path) as count
from dmoz p where path @> 'Top.Adult.Arts.Animation' order by path asc;
path | count
--------------------------+--------
Top | 300273
Top.Adult | 4913
Top.Adult.Arts | 339
Top.Adult.Arts.Animation | 65
(4 rows)
* Q5: Get all children with levels
select path, nlevel(path) - nlevel('Top.Adult.Arts.Animation') as level
from dmoz where path ~ 'Top.Adult.Arts.Animation.*{1,2}' order by path asc;
path | level
------------------------------------------------+-------
Top.Adult.Arts.Animation.Anime | 1
Top.Adult.Arts.Animation.Anime.Fan_Works | 2
Top.Adult.Arts.Animation.Anime.Games | 2
Top.Adult.Arts.Animation.Anime.Genres | 2
Top.Adult.Arts.Animation.Anime.Image_Galleries | 2
Top.Adult.Arts.Animation.Anime.Multimedia | 2
Top.Adult.Arts.Animation.Anime.Resources | 2
Top.Adult.Arts.Animation.Anime.Titles | 2
Top.Adult.Arts.Animation.Cartoons | 1
Top.Adult.Arts.Animation.Cartoons.AVS | 2
Top.Adult.Arts.Animation.Cartoons.Members | 2
(11 rows)
Timings
+---------------------------------------------+
|Query|Rows|Time (ms) index|Time (ms) no index|
|-----+----+---------------+------------------|
| Q0| 1| NA| 1453.44|
|-----+----+---------------+------------------|
| Q1| 2| 0.49| 1001.54|
|-----+----+---------------+------------------|
| Q2| 2| 1.48| 3009.39|
|-----+----+---------------+------------------|
| Q3| 4| 0.55| 906.98|
|-----+----+---------------+------------------|
| Q4| 4| 24385.07| 4951.91|
|-----+----+---------------+------------------|
| Q5| 11| 0.85| 1003.23|
+---------------------------------------------+
Timings without indices were obtained using operations which doesn't use
indices (see above)
Remarks
We didn't run full-scale tests, also we didn't present (yet) data for
operations with arrays of ltree (ltree[]) and full text searching. We'll
appreciate your input. So far, below some (rather obvious) results:
* Indices does help execution of queries
* Q4 performs bad because one needs to read almost all data from the HDD
CHANGES
July 13, 2002
Initial release.
TODO
* Testing on 64-bit platforms. There are several known problems with byte
alignment;
* Better documentation;
* We plan (probably) to improve regular expressions processing using
non-deterministic automata;
* Some sort of XML support;
* Better full text searching;
SOME BACKGROUNDS
The approach we use for ltree is much like one we used in our other GiST based
contrib modules (intarray, tsearch, tree, btree_gist, rtree_gist). Theoretical
background is available in papers referenced from our GiST development page
(http://www.sai.msu.su/~megera/postgres/gist).
A hierarchical data structure (tree) is a set of nodes. Each node has a
signature (LPS) of a fixed size, which is a hashed label path of that node.
Traversing a tree we could *certainly* prune branches if
LQS (bitwise AND) LPS != LQS
where LQS is a signature of lquery or ltxtquery, obtained in the same way as
LPS.
ltree[]:
For array of ltree LPS is a bitwise OR-ed signatures of *ALL* children
reachable from that node. Signatures are stored in RD-tree, implemented using
GiST, which provides indexed access.
ltree:
For ltree we store LPS in a B-tree, implemented using GiST. Each node entry is
represented by (left_bound, signature, right_bound), so that we could speedup
operations <, <=, =, =>, > using left_bound, right_bound and prune branches of
a tree using signature.
-------------------------------------------------------------------------------
We ask people who find the module useful to send us a postcards to:
Moscow, 119899, Universitetski pr.13, Moscow State University, Sternberg
Astronomical Institute, Russia
For: Bartunov O.S.
and
Moscow, Bratislavskaya str.23, appt. 18, Russia
For: Sigaev F.G.

549
contrib/ltree/_ltree_gist.c Normal file
View File

@ -0,0 +1,549 @@
/*
* GiST support for ltree[]
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include "access/gist.h"
#include "access/rtree.h"
#include "access/nbtree.h"
#include "utils/array.h"
#include "crc32.h"
PG_FUNCTION_INFO_V1( _ltree_compress );
Datum _ltree_compress(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( _ltree_same );
Datum _ltree_same(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( _ltree_union );
Datum _ltree_union(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( _ltree_penalty );
Datum _ltree_penalty(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( _ltree_picksplit );
Datum _ltree_picksplit(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( _ltree_consistent );
Datum _ltree_consistent(PG_FUNCTION_ARGS);
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer(((GISTENTRY *) VARDATA(vec))[(pos)].key))
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
#define SUMBIT(val) ( \
GETBITBYTE(val,0) + \
GETBITBYTE(val,1) + \
GETBITBYTE(val,2) + \
GETBITBYTE(val,3) + \
GETBITBYTE(val,4) + \
GETBITBYTE(val,5) + \
GETBITBYTE(val,6) + \
GETBITBYTE(val,7) \
)
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
static void
hashing(BITVECP sign, ltree *t) {
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
int hash;
while(tlen > 0) {
hash = crc32_sz( cur->name, cur->len );
AHASH( sign, hash );
cur = LEVEL_NEXT(cur);
tlen--;
}
}
Datum
_ltree_compress(PG_FUNCTION_ARGS) {
GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
GISTENTRY *retval = entry;
if ( entry->leafkey ) { /* ltree */
ltree_gist *key;
ArrayType *val = (ArrayType*)DatumGetPointer(PG_DETOAST_DATUM(entry->key));
int4 len = LTG_HDRSIZE + ASIGLEN;
int num=ArrayGetNItems( ARR_NDIM(val), ARR_DIMS(val) );
ltree *item = (ltree*)ARR_DATA_PTR(val);
if ( ARR_NDIM(val) != 1 )
elog(ERROR,"Dimension of array != 1");
key = (ltree_gist*)palloc( len );
key->len = len;
key->flag = 0;
MemSet( LTG_SIGN(key), 0, sizeof(ASIGLEN) );
while( num>0 ) {
hashing(LTG_SIGN(key), item);
num--;
item = NEXTVAL(item);
}
if ( PointerGetDatum(val) != entry->key )
pfree(val);
retval = (GISTENTRY*)palloc( sizeof(GISTENTRY) );
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
entry->offset, key->len, FALSE);
} else {
int4 i,len;
ltree_gist *key;
BITVECP sign = LTG_SIGN(DatumGetPointer( entry->key ) );
ALOOPBYTE(
if ( sign[i] != 0xff )
PG_RETURN_POINTER(retval);
);
len = LTG_HDRSIZE;
key = (ltree_gist*)palloc( len );
key->len = len;
key->flag = LTG_ALLTRUE;
retval = (GISTENTRY*)palloc( sizeof(GISTENTRY) );
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
entry->offset, key->len, FALSE);
}
PG_RETURN_POINTER(retval);
}
Datum
_ltree_same(PG_FUNCTION_ARGS) {
ltree_gist* a=(ltree_gist*)PG_GETARG_POINTER(0);
ltree_gist* b=(ltree_gist*)PG_GETARG_POINTER(1);
bool *result = (bool *)PG_GETARG_POINTER(2);
if ( LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b) ) {
*result = true;
} else if ( LTG_ISALLTRUE(a) ) {
*result = false;
} else if ( LTG_ISALLTRUE(b) ) {
*result = false;
} else {
int4 i;
BITVECP sa=LTG_SIGN(a), sb=LTG_SIGN(b);
*result = true;
ALOOPBYTE(
if ( sa[i] != sb[i] ) {
*result = false;
break;
}
);
}
PG_RETURN_POINTER(result);
}
static int4
unionkey( BITVECP sbase, ltree_gist *add ) {
int4 i;
BITVECP sadd = LTG_SIGN( add );
if ( LTG_ISALLTRUE(add) )
return 1;
ALOOPBYTE(
sbase[i] |= sadd[i];
);
return 0;
}
Datum
_ltree_union(PG_FUNCTION_ARGS) {
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
ABITVEC base;
int4 len = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
int4 i;
int4 flag = 0;
ltree_gist *result;
MemSet( (void*)base, 0, sizeof(ABITVEC) );
for(i=0;i<len;i++) {
if ( unionkey( base, GETENTRY(entryvec, i) ) ) {
flag = LTG_ALLTRUE;
break;
}
}
len = LTG_HDRSIZE + ( ( flag & LTG_ALLTRUE ) ? 0 : ASIGLEN );
result = (ltree_gist*)palloc( len );
*size = result->len = len;
result->flag = flag;
if ( ! LTG_ISALLTRUE(result) )
memcpy((void*)LTG_SIGN(result), (void*)base, sizeof( ABITVEC ) );
PG_RETURN_POINTER(result);
}
static int4
sizebitvec( BITVECP sign ) {
int4 size=0, i;
ALOOPBYTE(
size += SUMBIT(*(char*)sign);
sign = (BITVECP) ( ((char*)sign) + 1 );
);
return size;
}
Datum
_ltree_penalty(PG_FUNCTION_ARGS) {
ltree_gist *origval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(0) )->key );
ltree_gist *newval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(1) )->key );
float *penalty = (float *) PG_GETARG_POINTER(2);
BITVECP orig = LTG_SIGN(origval);
if ( LTG_ISALLTRUE(origval) ) {
*penalty = 0.0;
PG_RETURN_POINTER( penalty );
}
if ( LTG_ISALLTRUE(newval) ) {
*penalty = (float) (ASIGLENBIT - sizebitvec( orig ) );
} else {
unsigned char valtmp;
BITVECP nval = LTG_SIGN(newval);
int4 i, unionsize=0;
ALOOPBYTE(
valtmp = nval[i] | orig[i];
unionsize += SUMBIT(valtmp) - SUMBIT(orig[i]);
);
*penalty = (float)unionsize;
}
PG_RETURN_POINTER( penalty );
}
typedef struct {
OffsetNumber pos;
int4 cost;
} SPLITCOST;
static int
comparecost( const void *a, const void *b ) {
return ((SPLITCOST*)a)->cost - ((SPLITCOST*)b)->cost;
}
Datum
_ltree_picksplit(PG_FUNCTION_ARGS) {
bytea *entryvec = (bytea*) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1);
OffsetNumber k,j;
ltree_gist *datum_l, *datum_r;
ABITVEC union_l, union_r;
bool firsttime = true;
int4 size_alpha,size_beta,sizeu,sizei;
int4 size_waste, waste = 0.0;
int4 size_l, size_r;
int4 nbytes;
OffsetNumber seed_1=0, seed_2=0;
OffsetNumber *left, *right;
OffsetNumber maxoff;
BITVECP ptra, ptrb, ptrc;
int i;
unsigned char valtmp;
SPLITCOST *costvector;
ltree_gist *_k, *_j;
maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 2;
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
v->spl_left = (OffsetNumber *) palloc(nbytes);
v->spl_right = (OffsetNumber *) palloc(nbytes);
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) {
_k = GETENTRY(entryvec,k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) {
_j = GETENTRY(entryvec,j);
if ( LTG_ISALLTRUE(_k) || LTG_ISALLTRUE(_j) ) {
sizeu = ASIGLENBIT;
if ( LTG_ISALLTRUE(_k) && LTG_ISALLTRUE(_j) )
sizei = ASIGLENBIT;
else
sizei = ( LTG_ISALLTRUE(_k) ) ?
sizebitvec( LTG_SIGN(_j) ) : sizebitvec( LTG_SIGN(_k) );
} else {
sizeu = sizei = 0;
ptra = LTG_SIGN(_j);
ptrb = LTG_SIGN(_k);
/* critical section for bench !!! */
#define COUNT(pos) do { \
if ( GETBITBYTE(*(char*)ptra,pos) ) { \
sizeu++; \
if ( GETBITBYTE(*(char*)ptrb, pos) ) \
sizei++; \
} else if ( GETBITBYTE(*(char*)ptrb, pos) ) \
sizeu++; \
} while(0)
ALOOPBYTE(
COUNT(0);
COUNT(1);
COUNT(2);
COUNT(3);
COUNT(4);
COUNT(5);
COUNT(6);
COUNT(7);
ptra = (BITVECP) ( ((char*)ptra) + 1 );
ptrb = (BITVECP) ( ((char*)ptrb) + 1 );
);
}
size_waste = sizeu - sizei;
if (size_waste > waste || firsttime) {
waste = size_waste;
seed_1 = k;
seed_2 = j;
firsttime = false;
}
}
}
left = v->spl_left;
v->spl_nleft = 0;
right = v->spl_right;
v->spl_nright = 0;
if ( seed_1 == 0 || seed_2 == 0 ) {
seed_1 = 1;
seed_2 = 2;
}
/* form initial .. */
if ( LTG_ISALLTRUE(GETENTRY(entryvec,seed_1)) ) {
datum_l = (ltree_gist*)palloc( LTG_HDRSIZE );
datum_l->len = LTG_HDRSIZE; datum_l->flag = LTG_ALLTRUE;
size_l = ASIGLENBIT;
} else {
datum_l = (ltree_gist*)palloc( LTG_HDRSIZE + ASIGLEN );
datum_l->len = LTG_HDRSIZE + ASIGLEN; datum_l->flag = 0;
memcpy((void*)LTG_SIGN(datum_l), (void*)LTG_SIGN(GETENTRY(entryvec,seed_1)), sizeof(ABITVEC));
size_l = sizebitvec( LTG_SIGN(datum_l) );
}
if ( LTG_ISALLTRUE(GETENTRY(entryvec,seed_2)) ) {
datum_r = (ltree_gist*)palloc( LTG_HDRSIZE );
datum_r->len = LTG_HDRSIZE; datum_r->flag = LTG_ALLTRUE;
size_r = ASIGLENBIT;
} else {
datum_r = (ltree_gist*)palloc( LTG_HDRSIZE + ASIGLEN );
datum_r->len = LTG_HDRSIZE + ASIGLEN; datum_r->flag = 0;
memcpy((void*)LTG_SIGN(datum_r), (void*)LTG_SIGN(GETENTRY(entryvec,seed_2)), sizeof(ABITVEC));
size_r = sizebitvec( LTG_SIGN(datum_r) );
}
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
costvector=(SPLITCOST*)palloc( sizeof(SPLITCOST)*maxoff );
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) {
costvector[j-1].pos = j;
_j = GETENTRY(entryvec,j);
if ( LTG_ISALLTRUE(_j) ) {
size_alpha = ASIGLENBIT - size_l;
size_beta = ASIGLENBIT - size_r;
} else {
ptra = LTG_SIGN( datum_l );
ptrb = LTG_SIGN( datum_r );
ptrc = LTG_SIGN( _j );
size_beta = size_alpha = 0;
if ( LTG_ISALLTRUE(datum_l) ) {
if ( !LTG_ISALLTRUE(datum_r) ) {
ALOOPBIT(
if ( GETBIT(ptrc,i) && ! GETBIT(ptrb,i) )
size_beta++;
);
}
} else if ( LTG_ISALLTRUE(datum_r) ) {
if ( !LTG_ISALLTRUE(datum_l) ) {
ALOOPBIT(
if ( GETBIT(ptrc,i) && ! GETBIT(ptra,i) )
size_alpha++;
);
}
} else {
ALOOPBIT(
if ( GETBIT(ptrc,i) && ! GETBIT(ptra,i) )
size_alpha++;
if ( GETBIT(ptrc,i) && ! GETBIT(ptrb,i) )
size_beta++;
);
}
}
costvector[j-1].cost = abs( size_alpha - size_beta );
}
qsort( (void*)costvector, maxoff, sizeof(SPLITCOST), comparecost );
for (k = 0; k < maxoff; k++) {
j = costvector[k].pos;
_j = GETENTRY(entryvec,j);
if ( j == seed_1 ) {
*left++ = j;
v->spl_nleft++;
continue;
} else if ( j == seed_2 ) {
*right++ = j;
v->spl_nright++;
continue;
}
if ( LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j) ) {
size_alpha = ASIGLENBIT;
} else {
ptra = LTG_SIGN(_j);
ptrb = LTG_SIGN(datum_l);
size_alpha = 0;
ALOOPBYTE(
valtmp = union_l[i] = ptra[i] | ptrb[i];
size_alpha += SUMBIT( valtmp );
);
}
if ( LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j) ) {
size_beta = ASIGLENBIT;
} else {
ptra = LTG_SIGN(_j);
ptrb = LTG_SIGN(datum_r);
size_beta = 0;
ALOOPBYTE(
valtmp = union_r[i] = ptra[i] | ptrb[i];
size_beta += SUMBIT( valtmp );
);
}
if (size_alpha - size_l < size_beta - size_r + WISH_F(v->spl_nleft, v->spl_nright, 0.1)) {
if ( ! LTG_ISALLTRUE( datum_l ) ) {
if ( size_alpha == ASIGLENBIT ) {
if ( size_alpha != size_l )
MemSet( (void*)LTG_SIGN(datum_l),0xff, sizeof(ABITVEC));
} else
memcpy( (void*)LTG_SIGN(datum_l), (void*)union_l, sizeof(ABITVEC) );
}
size_l = size_alpha;
*left++ = j;
v->spl_nleft++;
} else {
if ( ! LTG_ISALLTRUE( datum_r ) ) {
if ( size_beta == ASIGLENBIT ) {
if ( size_beta != size_r )
MemSet( (void*)LTG_SIGN(datum_r),0xff, sizeof(ABITVEC));
} else
memcpy( (void*)LTG_SIGN(datum_r), (void*)union_r, sizeof(ABITVEC) );
}
size_r = size_beta;
*right++ = j;
v->spl_nright++;
}
}
*right = *left = FirstOffsetNumber;
pfree(costvector);
v->spl_ldatum = PointerGetDatum(datum_l);
v->spl_rdatum = PointerGetDatum(datum_r);
PG_RETURN_POINTER( v );
}
static bool
gist_te(ltree_gist *key, ltree* query) {
ltree_level *curq = LTREE_FIRST(query);
BITVECP sign = LTG_SIGN(key);
int qlen = query->numlevel;
unsigned int hv;
if ( LTG_ISALLTRUE(key) )
return true;
while( qlen>0 ) {
hv = crc32_sz(curq->name,curq->len);
if ( ! GETBIT( sign, AHASHVAL(hv) ) )
return false;
curq = LEVEL_NEXT(curq);
qlen--;
}
return true;
}
static bool
checkcondition_bit(void *checkval, ITEM* val ) {
return ( FLG_CANLOOKSIGN(val->flag) ) ? GETBIT( checkval, AHASHVAL( val->val ) ) : true;
}
static bool
gist_qtxt(ltree_gist *key, ltxtquery* query) {
if ( LTG_ISALLTRUE(key) )
return true;
return execute(
GETQUERY(query),
(void*)LTG_SIGN(key), false,
checkcondition_bit
);
}
static bool
gist_qe(ltree_gist *key, lquery* query) {
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
int qlen = query->numlevel;
if ( LTG_ISALLTRUE(key) )
return true;
while( qlen>0 ) {
if ( curq->numvar && LQL_CANLOOKSIGN(curq) ) {
bool isexist=false;
int vlen = curq->numvar;
lquery_variant *curv = LQL_FIRST(curq);
while( vlen>0 ) {
if ( GETBIT( sign, AHASHVAL( curv->val ) ) ) {
isexist=true;
break;
}
curv = LVAR_NEXT(curv);
vlen--;
}
if ( !isexist )
return false;
}
curq = LQL_NEXT(curq);
qlen--;
}
return true;
}
Datum
_ltree_consistent(PG_FUNCTION_ARGS) {
GISTENTRY *entry = (GISTENTRY*)PG_GETARG_POINTER(0);
char *query = (char*)DatumGetPointer( PG_DETOAST_DATUM(PG_GETARG_DATUM(1)) );
ltree_gist *key = (ltree_gist*)DatumGetPointer( entry->key );
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
bool res = false;
#ifndef assert_enabled
#define assert_enabled 0
#endif
switch( strategy ) {
case 10:
case 11:
res = gist_te(key, (ltree*)query);
break;
case 12:
case 13:
res = gist_qe(key, (lquery*)query);
break;
case 14:
case 15:
res = gist_qtxt(key, (ltxtquery*)query);
break;
default:
elog(ERROR,"Unknown StrategyNumber: %d", strategy);
}
PG_RETURN_BOOL(res);
}

212
contrib/ltree/_ltree_op.c Normal file
View File

@ -0,0 +1,212 @@
/*
* op function for ltree[]
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
#include "utils/array.h"
PG_FUNCTION_INFO_V1(_ltree_isparent);
PG_FUNCTION_INFO_V1(_ltree_r_isparent);
PG_FUNCTION_INFO_V1(_ltree_risparent);
PG_FUNCTION_INFO_V1(_ltree_r_risparent);
PG_FUNCTION_INFO_V1(_ltq_regex);
PG_FUNCTION_INFO_V1(_ltq_rregex);
PG_FUNCTION_INFO_V1(_ltxtq_exec);
PG_FUNCTION_INFO_V1(_ltxtq_rexec);
Datum _ltree_r_isparent(PG_FUNCTION_ARGS);
Datum _ltree_r_risparent(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(_ltree_extract_isparent);
PG_FUNCTION_INFO_V1(_ltree_extract_risparent);
PG_FUNCTION_INFO_V1(_ltq_extract_regex);
PG_FUNCTION_INFO_V1(_ltxtq_extract_exec);
Datum _ltree_extract_isparent(PG_FUNCTION_ARGS);
Datum _ltree_extract_risparent(PG_FUNCTION_ARGS);
Datum _ltq_extract_regex(PG_FUNCTION_ARGS);
Datum _ltxtq_extract_exec(PG_FUNCTION_ARGS);
typedef Datum (*PGCALL2)(PG_FUNCTION_ARGS);
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
static bool
array_iterator( ArrayType *la, PGCALL2 callback, void* param, ltree ** found) {
int num=ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la));
ltree *item = (ltree*)ARR_DATA_PTR(la);
if ( ARR_NDIM(la) !=1 )
elog(ERROR,"Dimension of array != 1");
if ( found )
*found=NULL;
while( num>0 ) {
if ( DatumGetBool( DirectFunctionCall2( callback,
PointerGetDatum(item), PointerGetDatum(param) ) ) ) {
if ( found )
*found = item;
return true;
}
num--;
item = NEXTVAL(item);
}
return false;
}
Datum
_ltree_isparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
bool res = array_iterator( la, ltree_isparent, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltree_r_isparent(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltree_isparent,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltree_risparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
bool res = array_iterator( la, ltree_risparent, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltree_r_risparent(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltree_risparent,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltq_regex(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
lquery *query = PG_GETARG_LQUERY(1);
bool res = array_iterator( la, ltq_regex, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltq_rregex(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltq_regex,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltxtq_exec(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
bool res = array_iterator( la, ltxtq_exec, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltxtq_rexec(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltxtq_exec,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltree_extract_isparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
ltree *found,*item;
if ( !array_iterator( la, ltree_isparent, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
Datum
_ltree_extract_risparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
ltree *found,*item;
if ( !array_iterator( la, ltree_risparent, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
Datum
_ltq_extract_regex(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
lquery *query = PG_GETARG_LQUERY(1);
ltree *found,*item;
if ( !array_iterator( la, ltq_regex, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
Datum
_ltxtq_extract_exec(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
ltree *found,*item;
if ( !array_iterator( la, ltxtq_exec, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}

110
contrib/ltree/crc32.c Normal file
View File

@ -0,0 +1,110 @@
/* Both POSIX and CRC32 checksums */
#include <sys/types.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef LOWER_NODE
#include <ctype.h>
#define TOLOWER(x) tolower(x)
#else
#define TOLOWER(x) (x)
#endif
#include "crc32.h"
/*
* This code implements the AUTODIN II polynomial
* The variable corresponding to the macro argument "crc" should
* be an unsigned long.
* Oroginal code by Spencer Garrett <srg@quick.com>
*/
#define _CRC32_(crc, ch) (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff])
/* generated using the AUTODIN II polynomial
* x^32 + x^26 + x^23 + x^22 + x^16 +
* x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
*/
static const unsigned int crc32tab[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
unsigned int
crc32_sz(char *buf, int size)
{
unsigned int crc = ~0;
char *p;
int len,
nr;
len = 0;
nr = size;
for (len += nr, p = buf; nr--; ++p)
_CRC32_(crc, TOLOWER(*p));
return ~crc;
}

10
contrib/ltree/crc32.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _CRC32_H
#define _CRC32_H
/* Returns crc32 of data block */
extern unsigned int crc32_sz(char *buf, int size);
/* Returns crc32 of null-terminated string */
#define crc32(buf) crc32_sz((buf),strlen(buf))
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

240
contrib/ltree/lquery_op.c Normal file
View File

@ -0,0 +1,240 @@
/*
* op function for ltree and lquery
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
PG_FUNCTION_INFO_V1(ltq_regex);
PG_FUNCTION_INFO_V1(ltq_rregex);
typedef struct {
lquery_level *q;
int nq;
ltree_level *t;
int nt;
int posq;
int post;
} FieldNot;
static char *
getlexem(char *start, char *end, int *len) {
char *ptr;
while( start<end && *start == '_' )
start++;
ptr = start;
if ( ptr == end )
return NULL;
while( ptr < end && *ptr != '_')
ptr++;
*len = ptr - start;
return start;
}
bool
compare_subnode( ltree_level *t, char *qn, int len, int (*cmpptr)(const char *,const char *,size_t), bool anyend ) {
char *endt = t->name + t->len;
char *endq = qn + len;
char *tn;
int lent,lenq;
bool isok;
while( (qn=getlexem(qn,endq,&lenq)) != NULL ) {
tn=t->name;
isok = false;
while( (tn=getlexem(tn,endt,&lent)) != NULL ) {
if (
(
lent == lenq ||
( lent > lenq && anyend )
) &&
(*cmpptr)(qn,tn,lenq) == 0 ) {
isok = true;
break;
}
tn += lent;
}
if ( !isok )
return false;
qn += lenq;
}
return true;
}
static bool
checkLevel( lquery_level *curq, ltree_level *curt ) {
int (*cmpptr)(const char *,const char *,size_t);
lquery_variant *curvar = LQL_FIRST(curq);
int i;
for(i=0;i<curq->numvar;i++) {
cmpptr = ( curvar->flag & LVAR_INCASE ) ? strncasecmp : strncmp;
if ( curvar->flag & LVAR_SUBLEXEM ) {
if ( compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND) ) )
return true;
} else if (
(
curvar->len == curt->len ||
( curt->len > curvar->len && (curvar->flag & LVAR_ANYEND) )
) &&
(*cmpptr)( curvar->name, curt->name, curvar->len) == 0 ) {
return true;
}
curvar = LVAR_NEXT(curvar);
}
return false;
}
/*
void
printFieldNot(FieldNot *fn ) {
while(fn->q) {
elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
fn++;
}
}
*/
static bool
checkCond( lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr ) {
uint32 low_pos=0,high_pos=0,cur_tpos=0;
int tlen = tree_numlevel, qlen = query_numlevel;
int isok;
lquery_level *prevq=NULL;
ltree_level *prevt=NULL;
while( tlen >0 && qlen>0 ) {
if ( curq->numvar ) {
prevt = curt;
while ( cur_tpos < low_pos ) {
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if ( tlen==0 )
return false;
if ( ptr && ptr->q )
ptr->nt++;
}
if ( ptr && curq->flag & LQL_NOT ) {
if ( !(prevq && prevq->numvar == 0) )
prevq = curq;
if ( ptr->q == NULL ) {
ptr->t = prevt;
ptr->q = prevq;
ptr->nt=1;
ptr->nq=1 + ( (prevq==curq) ? 0 : 1 );
ptr->posq = query_numlevel - qlen - ( (prevq==curq) ? 0 : 1 );
ptr->post = cur_tpos;
} else {
ptr->nt++;
ptr->nq++;
}
if ( qlen == 1 && ptr->q->numvar==0 )
ptr->nt = tree_numlevel - ptr->post;
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if ( high_pos < cur_tpos )
high_pos++;
} else {
isok = false;
while( cur_tpos <= high_pos && tlen > 0 && !isok) {
isok = checkLevel(curq, curt);
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if ( !isok && ptr )
ptr->nt++;
}
if ( !isok )
return false;
if (ptr && ptr->q) {
if ( checkCond(ptr->q,ptr->nq,ptr->t,ptr->nt,NULL) )
return false;
ptr->q = NULL;
}
low_pos=cur_tpos; high_pos=cur_tpos;
}
} else {
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
if ( ptr && ptr->q ) {
ptr->nq++;
if ( qlen==1 )
ptr->nt = tree_numlevel - ptr->post;
}
}
prevq = curq;
curq = LQL_NEXT(curq);
qlen--;
}
if ( low_pos > tree_numlevel || tree_numlevel > high_pos )
return false;
while( qlen>0 ) {
if ( curq->numvar ) {
if ( ! (curq->flag & LQL_NOT) )
return false;
} else {
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
}
curq = LQL_NEXT(curq);
qlen--;
}
if ( low_pos > tree_numlevel || tree_numlevel > high_pos )
return false;
if ( ptr && ptr->q && checkCond(ptr->q,ptr->nq,ptr->t,ptr->nt,NULL) )
return false;
return true;
}
Datum
ltq_regex(PG_FUNCTION_ARGS) {
ltree *tree = PG_GETARG_LTREE(0);
lquery *query = PG_GETARG_LQUERY(1);
bool res= false;
if ( query->flag & LQUERY_HASNOT ) {
FieldNot fn;
fn.q=NULL;
res = checkCond( LQUERY_FIRST(query), query->numlevel,
LTREE_FIRST(tree), tree->numlevel, &fn );
} else {
res = checkCond( LQUERY_FIRST(query), query->numlevel,
LTREE_FIRST(tree), tree->numlevel, NULL );
}
PG_FREE_IF_COPY(tree,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
ltq_rregex(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( ltq_regex,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}

251
contrib/ltree/ltree.h Normal file
View File

@ -0,0 +1,251 @@
#ifndef __LTREE_H__
#define __LTREE_H__
#include "postgres.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "utils/builtins.h"
typedef struct {
uint8 len;
char name[1];
} ltree_level;
#define LEVEL_HDRSIZE (sizeof(uint8))
#define LEVEL_NEXT(x) ( (ltree_level*)( ((char*)(x)) + ((ltree_level*)(x))->len + LEVEL_HDRSIZE ) )
typedef struct {
int32 len;
uint16 numlevel;
char data[1];
} ltree;
#define LTREE_HDRSIZE ( sizeof(int32) + sizeof(uint16) )
#define LTREE_FIRST(x) ( (ltree_level*)( ((ltree*)(x))->data ) )
/* lquery */
typedef struct {
int4 val;
uint8 len;
uint8 flag;
char name[1];
} lquery_variant;
#define LVAR_HDRSIZE (sizeof(uint8)*2 + sizeof(int4))
#define LVAR_NEXT(x) ( (lquery_variant*)( ((char*)(x)) + ((lquery_variant*)(x))->len + LVAR_HDRSIZE ) )
#define LVAR_ANYEND 0x01
#define LVAR_INCASE 0x02
#define LVAR_SUBLEXEM 0x04
typedef struct {
uint16 totallen;
uint16 flag;
uint16 numvar;
uint16 low;
uint16 high;
char variants[1];
} lquery_level;
#define LQL_HDRSIZE ( sizeof(uint16)*5 )
#define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + ((lquery_level*)(x))->totallen ) )
#define LQL_FIRST(x) ( (lquery_variant*)( ((lquery_level*)(x))->variants ) )
#define LQL_NOT 0x10
#ifdef LOWER_NODE
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEM ) ) == 0 )
#else
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEM | LVAR_INCASE ) ) == 0 )
#endif
#define LQL_CANLOOKSIGN(x) FLG_CANLOOKSIGN( ((lquery_level*)(x))->flag )
typedef struct {
int32 len;
uint16 numlevel;
uint16 firstgood;
uint16 flag;
char data[1];
} lquery;
#define LQUERY_HDRSIZE ( sizeof(int32) + 3*sizeof(uint16) )
#define LQUERY_FIRST(x) ( (lquery_level*)( ((lquery*)(x))->data ) )
#define LQUERY_HASNOT 0x01
#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
#define min(a,b) ((a) <= (b) ? (a) : (b))
#endif
#ifndef abs
#define abs(a) ((a) < (0) ? -(a) : (a))
#endif
#define ISALNUM(x) ( isalnum(x) || (x) == '_' )
/* full text query */
/*
* item in polish notation with back link
* to left operand
*/
typedef struct ITEM
{
int2 type;
int2 left;
int4 val;
uint8 flag;
/* user-friendly value */
uint8 length;
uint16 distance;
} ITEM;
/*
*Storage:
* (len)(size)(array of ITEM)(array of operand in user-friendly form)
*/
typedef struct
{
int4 len;
int4 size;
char data[1];
} ltxtquery;
#define HDRSIZEQT ( 2*sizeof(int4) )
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + size * sizeof(ITEM) + lenofoperand )
#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
#define ISOPERATOR(x) ( (x)=='!' || (x)=='&' || (x)=='|' || (x)=='(' || (x)==')' )
#define END 0
#define ERR 1
#define VAL 2
#define OPR 3
#define OPEN 4
#define CLOSE 5
#define VALTRUE 6 /* for stop words */
#define VALFALSE 7
/* use in array iterator */
Datum ltree_isparent(PG_FUNCTION_ARGS);
Datum ltree_risparent(PG_FUNCTION_ARGS);
Datum ltq_regex(PG_FUNCTION_ARGS);
Datum ltq_rregex(PG_FUNCTION_ARGS);
Datum ltxtq_exec(PG_FUNCTION_ARGS);
Datum ltxtq_rexec(PG_FUNCTION_ARGS);
Datum _ltq_regex(PG_FUNCTION_ARGS);
Datum _ltq_rregex(PG_FUNCTION_ARGS);
Datum _ltxtq_exec(PG_FUNCTION_ARGS);
Datum _ltxtq_rexec(PG_FUNCTION_ARGS);
Datum _ltree_isparent(PG_FUNCTION_ARGS);
Datum _ltree_risparent(PG_FUNCTION_ARGS);
/* Concatenation functions */
Datum ltree_addltree(PG_FUNCTION_ARGS);
Datum ltree_addtext(PG_FUNCTION_ARGS);
Datum ltree_textadd(PG_FUNCTION_ARGS);
/* Util function */
Datum ltree_in(PG_FUNCTION_ARGS);
bool execute(ITEM * curitem, void *checkval,
bool calcnot, bool (*chkcond) (void *checkval, ITEM * val));
int ltree_compare(const ltree *a, const ltree *b);
bool inner_isparent(const ltree *c, const ltree *p);
bool compare_subnode( ltree_level *t, char *q, int len,
int (*cmpptr)(const char *,const char *,size_t), bool anyend );
#define PG_GETARG_LTREE(x) ((ltree*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
#define PG_GETARG_LQUERY(x) ((lquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
#define PG_GETARG_LTXTQUERY(x) ((ltxtquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
/* GiST support for ltree */
#define BITBYTE 8
#define SIGLENINT 8
#define SIGLEN ( sizeof(int4)*SIGLENINT )
#define SIGLENBIT (SIGLEN*BITBYTE)
typedef unsigned char BITVEC[SIGLEN];
typedef unsigned char *BITVECP;
#define LOOPBYTE(a) \
for(i=0;i<SIGLEN;i++) {\
a;\
}
#define LOOPBIT(a) \
for(i=0;i<SIGLENBIT;i++) {\
a;\
}
#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 )
#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
/*
* type of index key for ltree. Tree are combined B-Tree and R-Tree
* Storage:
* Leaf pages
* (len)(flag)(ltree)
* Non-Leaf
* (len)(flag)(sign)(left_ltree)(right_ltree)
* ALLTRUE: (len)(flag)(left_ltree)(right_ltree)
*
*/
typedef struct {
int4 len;
uint32 flag;
char data[1];
} ltree_gist;
#define LTG_ONENODE 0x01
#define LTG_ALLTRUE 0x02
#define LTG_NORIGHT 0x04
#define LTG_HDRSIZE ( sizeof(int4) + sizeof(uint32) )
#define LTG_SIGN(x) ( (BITVECP)( ((ltree_gist*)(x))->data ) )
#define LTG_NODE(x) ( (ltree*)( ((ltree_gist*)(x))->data ) )
#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 )
#define LTG_LNODE(x) ( (ltree*)( ( (char*)( ((ltree_gist*)(x))->data ) ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
#define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + LTG_LNODE(x)->len ) )
#define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
/* GiST support for ltree[] */
#define ASIGLENINT (2*SIGLENINT)
#define ASIGLEN (sizeof(int4)*ASIGLENINT)
#define ASIGLENBIT (ASIGLEN*BITBYTE)
typedef unsigned char ABITVEC[ASIGLEN];
#define ALOOPBYTE(a) \
for(i=0;i<ASIGLEN;i++) {\
a;\
}
#define ALOOPBIT(a) \
for(i=0;i<ASIGLENBIT;i++) {\
a;\
}
#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
/* type of key is the same to ltree_gist */
#endif

849
contrib/ltree/ltree.sql.in Normal file
View File

@ -0,0 +1,849 @@
BEGIN;
CREATE FUNCTION ltree_in(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE FUNCTION ltree_out(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE TYPE ltree (
internallength = -1,
input = ltree_in,
output = ltree_out,
storage = extended
);
--Compare function for ltree
CREATE FUNCTION ltree_cmp(ltree,ltree)
RETURNS int4
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_lt(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_le(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_eq(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_ge(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_gt(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_ne(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR < (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_lt,
COMMUTATOR = '>', NEGATOR = '>=',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR <= (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_le,
COMMUTATOR = '>=', NEGATOR = '>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR >= (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_ge,
COMMUTATOR = '<=', NEGATOR = '<',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR > (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_gt,
COMMUTATOR = '<', NEGATOR = '<=',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR = (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_eq,
COMMUTATOR = '=', NEGATOR = '<>',
RESTRICT = eqsel, JOIN = eqjoinsel,
SORT1 = '<', SORT2 = '<'
);
CREATE OPERATOR <> (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_ne,
COMMUTATOR = '<>', NEGATOR = '=',
RESTRICT = neqsel, JOIN = neqjoinsel
);
--util functions
CREATE FUNCTION subltree(ltree,int4,int4)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION subpath(ltree,int4,int4)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION subpath(ltree,int4)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION nlevel(ltree)
RETURNS int4
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_isparent(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_risparent(ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_addltree(ltree,ltree)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_addtext(ltree,text)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltree_textadd(text,ltree)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR @> (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_isparent,
COMMUTATOR = '<@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^@> (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_isparent,
COMMUTATOR = '^<@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR <@ (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_risparent,
COMMUTATOR = '@>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^<@ (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_risparent,
COMMUTATOR = '^@>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR || (
LEFTARG = ltree, RIGHTARG = ltree, PROCEDURE = ltree_addltree
);
CREATE OPERATOR || (
LEFTARG = ltree, RIGHTARG = text, PROCEDURE = ltree_addtext
);
CREATE OPERATOR || (
LEFTARG = text, RIGHTARG = ltree, PROCEDURE = ltree_textadd
);
-- B-tree support
INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opcdefault, opckeytype)
VALUES (
(SELECT oid FROM pg_am WHERE amname = 'btree'),
'ltree_ops',
(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
1, -- UID of superuser is hardwired to 1 as of PG 7.3
(SELECT oid FROM pg_type WHERE typname = 'ltree'),
true,
0);
SELECT o.oid AS opoid, o.oprname
INTO TEMP TABLE ltree_ops_tmp
FROM pg_operator o, pg_type t
WHERE o.oprleft = t.oid and o.oprright = t.oid
and t.typname = 'ltree';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 1, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'ltree_ops' AND
c.oprname = '<';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 2, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'ltree_ops' AND
c.oprname = '<=';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 3, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'ltree_ops' AND
c.oprname = '=';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 4, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'ltree_ops' AND
c.oprname = '>=';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 5, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'ltree_ops' AND
c.oprname = '>';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 1, p.oid
FROM pg_opclass opcl, pg_proc p
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'ltree_ops' AND
p.proname = 'ltree_cmp';
drop table ltree_ops_tmp;
--lquery type
CREATE FUNCTION lquery_in(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE FUNCTION lquery_out(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE TYPE lquery (
internallength = -1,
input = lquery_in,
output = lquery_out,
storage = extended
);
CREATE FUNCTION ltq_regex(ltree,lquery)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION ltq_rregex(lquery,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR ~ (
LEFTARG = ltree, RIGHTARG = lquery, PROCEDURE = ltq_regex,
COMMUTATOR = '~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ~ (
LEFTARG = lquery, RIGHTARG = ltree, PROCEDURE = ltq_rregex,
COMMUTATOR = '~',
RESTRICT = contsel, JOIN = contjoinsel
);
--not-indexed
CREATE OPERATOR ^~ (
LEFTARG = ltree, RIGHTARG = lquery, PROCEDURE = ltq_regex,
COMMUTATOR = '^~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^~ (
LEFTARG = lquery, RIGHTARG = ltree, PROCEDURE = ltq_rregex,
COMMUTATOR = '^~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE FUNCTION ltxtq_in(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE FUNCTION ltxtq_out(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE TYPE ltxtquery (
internallength = -1,
input = ltxtq_in,
output = ltxtq_out,
storage = extended
);
-- operations with ltxtquery
CREATE FUNCTION ltxtq_exec(ltree, ltxtquery)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict, iscachable);
CREATE FUNCTION ltxtq_rexec(ltxtquery, ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict, iscachable);
CREATE OPERATOR @ (
LEFTARG = ltree, RIGHTARG = ltxtquery, PROCEDURE = ltxtq_exec,
COMMUTATOR = '@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR @ (
LEFTARG = ltxtquery, RIGHTARG = ltree, PROCEDURE = ltxtq_rexec,
COMMUTATOR = '@',
RESTRICT = contsel, JOIN = contjoinsel
);
--not-indexed
CREATE OPERATOR ^@ (
LEFTARG = ltree, RIGHTARG = ltxtquery, PROCEDURE = ltxtq_exec,
COMMUTATOR = '^@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^@ (
LEFTARG = ltxtquery, RIGHTARG = ltree, PROCEDURE = ltxtq_rexec,
COMMUTATOR = '^@',
RESTRICT = contsel, JOIN = contjoinsel
);
--GiST support for ltree
CREATE FUNCTION ltree_gist_in(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE FUNCTION ltree_gist_out(opaque)
RETURNS opaque
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict);
CREATE TYPE ltree_gist (
internallength = -1,
input = ltree_gist_in,
output = ltree_gist_out,
storage = plain
);
create function ltree_consistent(opaque,opaque,int2) returns bool as 'MODULE_PATHNAME' language 'C';
create function ltree_compress(opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
create function ltree_decompress(opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
create function ltree_penalty(opaque,opaque,opaque) returns opaque as 'MODULE_PATHNAME' language 'C' with(isstrict);
create function ltree_picksplit(opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
create function ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opckeytype, opcdefault)
SELECT pg_am.oid, 'gist_ltree_ops',
(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
1, -- UID of superuser is hardwired to 1 as of PG 7.3
pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = 'ltree' and
pg_am.amname='gist' and
pg_key.typname = 'ltree_gist';
SELECT o.oid AS opoid, o.oprname
INTO TABLE ltree_ops_tmp
FROM pg_operator o, pg_type t
WHERE o.oprleft = t.oid and o.oprright = t.oid
and t.typname = 'ltree';
INSERT INTO pg_amop (amopclaid, amopopr, amopstrategy, amopreqcheck)
SELECT opcl.oid, c.opoid, 1, 'f'
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE opcname = 'gist_ltree_ops'
and c.oprname = '<';
INSERT INTO pg_amop (amopclaid, amopopr, amopstrategy, amopreqcheck)
SELECT opcl.oid, c.opoid, 2, 'f'
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE opcname = 'gist_ltree_ops'
and c.oprname = '<=';
INSERT INTO pg_amop (amopclaid, amopopr, amopstrategy, amopreqcheck)
SELECT opcl.oid, c.opoid, 3, 'f'
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE opcname = 'gist_ltree_ops'
and c.oprname = '=';
INSERT INTO pg_amop (amopclaid, amopopr, amopstrategy, amopreqcheck)
SELECT opcl.oid, c.opoid, 4, 'f'
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE opcname = 'gist_ltree_ops'
and c.oprname = '>=';
INSERT INTO pg_amop (amopclaid, amopopr, amopstrategy, amopreqcheck)
SELECT opcl.oid, c.opoid, 5, 'f'
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE opcname = 'gist_ltree_ops'
and c.oprname = '>';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 10, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and c.oprname = '@>';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 11, false, c.opoid
FROM pg_opclass opcl, ltree_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and c.oprname = '<@';
DROP TABLE ltree_ops_tmp;
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 12, false, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and t.typname = 'ltree' and tq.typname = 'lquery'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '~';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 13, false, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and t.typname = 'lquery' and tq.typname = 'ltree'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '~';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 14, false, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and t.typname = 'ltree' and tq.typname = 'ltxtquery'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '@';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 15, false, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and t.typname = 'ltxtquery' and tq.typname = 'ltree'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '@';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 1, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_consistent';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 2, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_union';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 3, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_compress';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 4, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_decompress';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 5, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_penalty';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 6, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_picksplit';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 7, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist_ltree_ops'
and proname = 'ltree_same';
-- arrays of ltree
CREATE FUNCTION _ltree_isparent(_ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION _ltree_r_isparent(ltree,_ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION _ltree_risparent(_ltree,ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION _ltree_r_risparent(ltree,_ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION _ltq_regex(_ltree,lquery)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION _ltq_rregex(lquery,_ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE FUNCTION _ltxtq_exec(_ltree, ltxtquery)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict, iscachable);
CREATE FUNCTION _ltxtq_rexec(ltxtquery, _ltree)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict, iscachable);
CREATE OPERATOR @> (
LEFTARG = _ltree, RIGHTARG = ltree, PROCEDURE = _ltree_isparent,
COMMUTATOR = '<@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR <@ (
LEFTARG = ltree, RIGHTARG = _ltree, PROCEDURE = _ltree_r_isparent,
COMMUTATOR = '@>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR <@ (
LEFTARG = _ltree, RIGHTARG = ltree, PROCEDURE = _ltree_risparent,
COMMUTATOR = '@>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR @> (
LEFTARG = ltree, RIGHTARG = _ltree, PROCEDURE = _ltree_r_risparent,
COMMUTATOR = '<@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ~ (
LEFTARG = _ltree, RIGHTARG = lquery, PROCEDURE = _ltq_regex,
COMMUTATOR = '~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ~ (
LEFTARG = lquery, RIGHTARG = _ltree, PROCEDURE = _ltq_rregex,
COMMUTATOR = '~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR @ (
LEFTARG = _ltree, RIGHTARG = ltxtquery, PROCEDURE = _ltxtq_exec,
COMMUTATOR = '@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR @ (
LEFTARG = ltxtquery, RIGHTARG = _ltree, PROCEDURE = _ltxtq_rexec,
COMMUTATOR = '@',
RESTRICT = contsel, JOIN = contjoinsel
);
--not indexed
CREATE OPERATOR ^@> (
LEFTARG = _ltree, RIGHTARG = ltree, PROCEDURE = _ltree_isparent,
COMMUTATOR = '^<@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^<@ (
LEFTARG = ltree, RIGHTARG = _ltree, PROCEDURE = _ltree_r_isparent,
COMMUTATOR = '^@>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^<@ (
LEFTARG = _ltree, RIGHTARG = ltree, PROCEDURE = _ltree_risparent,
COMMUTATOR = '^@>',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^@> (
LEFTARG = ltree, RIGHTARG = _ltree, PROCEDURE = _ltree_r_risparent,
COMMUTATOR = '^<@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^~ (
LEFTARG = _ltree, RIGHTARG = lquery, PROCEDURE = _ltq_regex,
COMMUTATOR = '^~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^~ (
LEFTARG = lquery, RIGHTARG = _ltree, PROCEDURE = _ltq_rregex,
COMMUTATOR = '^~',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^@ (
LEFTARG = _ltree, RIGHTARG = ltxtquery, PROCEDURE = _ltxtq_exec,
COMMUTATOR = '^@',
RESTRICT = contsel, JOIN = contjoinsel
);
CREATE OPERATOR ^@ (
LEFTARG = ltxtquery, RIGHTARG = _ltree, PROCEDURE = _ltxtq_rexec,
COMMUTATOR = '^@',
RESTRICT = contsel, JOIN = contjoinsel
);
--extractors
CREATE FUNCTION _ltree_extract_isparent(_ltree,ltree)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR ?@> (
LEFTARG = _ltree, RIGHTARG = ltree, PROCEDURE = _ltree_extract_isparent
);
CREATE FUNCTION _ltree_extract_risparent(_ltree,ltree)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR ?<@ (
LEFTARG = _ltree, RIGHTARG = ltree, PROCEDURE = _ltree_extract_risparent
);
CREATE FUNCTION _ltq_extract_regex(_ltree,lquery)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR ?~ (
LEFTARG = _ltree, RIGHTARG = lquery, PROCEDURE = _ltq_extract_regex
);
CREATE FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery)
RETURNS ltree
AS 'MODULE_PATHNAME'
LANGUAGE 'c' with (isstrict,iscachable);
CREATE OPERATOR ?@ (
LEFTARG = _ltree, RIGHTARG = ltxtquery, PROCEDURE = _ltxtq_extract_exec
);
--GiST support for ltree[]
create function _ltree_consistent(opaque,opaque,int2) returns bool as 'MODULE_PATHNAME' language 'C';
create function _ltree_compress(opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
create function _ltree_penalty(opaque,opaque,opaque) returns opaque as 'MODULE_PATHNAME' language 'C' with(isstrict);
create function _ltree_picksplit(opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
create function _ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function _ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opckeytype, opcdefault)
SELECT pg_am.oid, 'gist__ltree_ops',
(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
1, -- UID of superuser is hardwired to 1 as of PG 7.3
pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = '_ltree' and
pg_am.amname='gist' and
pg_key.typname = 'ltree_gist';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 12, true, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and t.typname = '_ltree' and tq.typname = 'lquery'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '~';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 13, true, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and t.typname = 'lquery' and tq.typname = '_ltree'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '~';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 14, true, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and t.typname = '_ltree' and tq.typname = 'ltxtquery'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '@';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 15, true, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and t.typname = 'ltxtquery' and tq.typname = '_ltree'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '@';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 10, true, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and t.typname = '_ltree' and tq.typname = 'ltree'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '<@';
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 11, true, o.oid
FROM pg_opclass opcl, pg_operator o, pg_type t, pg_type tq
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and t.typname = 'ltree' and tq.typname = '_ltree'
and o.oprleft = t.oid and o.oprright = tq.oid
and o.oprname = '@>';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 1, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = '_ltree_consistent';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 2, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = '_ltree_union';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 3, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = '_ltree_compress';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 4, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = 'ltree_decompress';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 5, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = '_ltree_penalty';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 6, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = '_ltree_picksplit';
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 7, pro.oid
FROM pg_opclass opcl, pg_proc pro
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'gist')
and opcname = 'gist__ltree_ops'
and proname = '_ltree_same';
END;

600
contrib/ltree/ltree_gist.c Normal file
View File

@ -0,0 +1,600 @@
/*
* GiST support for ltree
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include "access/gist.h"
#include "access/rtree.h"
#include "access/nbtree.h"
#include "crc32.h"
PG_FUNCTION_INFO_V1( ltree_gist_in );
Datum ltree_gist_in(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_gist_out );
Datum ltree_gist_out(PG_FUNCTION_ARGS);
Datum
ltree_gist_in(PG_FUNCTION_ARGS) {
elog(ERROR,"Unimplemented");
PG_RETURN_DATUM(0);
}
Datum
ltree_gist_out(PG_FUNCTION_ARGS) {
elog(ERROR,"Unimplemented");
PG_RETURN_DATUM(0);
}
PG_FUNCTION_INFO_V1( ltree_compress );
Datum ltree_compress(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_decompress );
Datum ltree_decompress(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_same );
Datum ltree_same(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_union );
Datum ltree_union(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_penalty );
Datum ltree_penalty(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_picksplit );
Datum ltree_picksplit(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1( ltree_consistent );
Datum ltree_consistent(PG_FUNCTION_ARGS);
#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer(((GISTENTRY *) VARDATA(vec))[(pos)].key))
Datum
ltree_compress(PG_FUNCTION_ARGS) {
GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
GISTENTRY *retval = entry;
if ( entry->leafkey ) { /* ltree */
ltree_gist *key;
ltree *val = (ltree*)DatumGetPointer(PG_DETOAST_DATUM(entry->key));
int4 len = LTG_HDRSIZE + val->len;
key = (ltree_gist*)palloc( len );
key->len = len;
key->flag = LTG_ONENODE;
memcpy( (void*)LTG_NODE(key), (void*)val, val->len);
if ( PointerGetDatum(val) != entry->key )
pfree(val);
retval = (GISTENTRY*)palloc( sizeof(GISTENTRY) );
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
entry->offset, key->len, FALSE);
}
PG_RETURN_POINTER(retval);
}
Datum
ltree_decompress(PG_FUNCTION_ARGS) {
GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
ltree_gist *key = (ltree_gist*)DatumGetPointer( PG_DETOAST_DATUM(entry->key) );
if ( PointerGetDatum(key) != entry->key ) {
GISTENTRY *retval = (GISTENTRY*)palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
entry->offset, key->len, FALSE);
PG_RETURN_POINTER(retval);
}
PG_RETURN_POINTER(entry);
}
Datum
ltree_same(PG_FUNCTION_ARGS) {
ltree_gist* a=(ltree_gist*)PG_GETARG_POINTER(0);
ltree_gist* b=(ltree_gist*)PG_GETARG_POINTER(1);
bool *result = (bool *)PG_GETARG_POINTER(2);
*result = false;
if ( LTG_ISONENODE(a) != LTG_ISONENODE(b) )
PG_RETURN_POINTER(result);
if ( LTG_ISONENODE(a) ) {
*result = ( ISEQ(LTG_NODE(a), LTG_NODE(b)) ) ? true : false;
} else {
int4 i;
BITVECP sa=LTG_SIGN(a), sb=LTG_SIGN(b);
if ( LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b) )
PG_RETURN_POINTER(result);
if ( !ISEQ(LTG_LNODE(a), LTG_LNODE(b)) )
PG_RETURN_POINTER(result);
if ( !ISEQ(LTG_RNODE(a), LTG_RNODE(b)) )
PG_RETURN_POINTER(result);
*result = true;
if ( !LTG_ISALLTRUE(a) )
LOOPBYTE(
if ( sa[i] != sb[i] ) {
*result = false;
break;
}
);
}
PG_RETURN_POINTER(result);
}
static void
hashing(BITVECP sign, ltree *t) {
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
int hash;
while(tlen > 0) {
hash = crc32_sz( cur->name, cur->len );
HASH( sign, hash );
cur = LEVEL_NEXT(cur);
tlen--;
}
}
Datum
ltree_union(PG_FUNCTION_ARGS) {
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
BITVEC base;
int4 len = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
int4 i,j;
ltree_gist *result,*cur;
ltree *left=NULL, *right=NULL, *curtree;
bool isalltrue = false;
bool isleqr;
MemSet( (void*)base, 0, sizeof(BITVEC) );
for(j=0;j<len;j++) {
cur = GETENTRY(entryvec, j);
if ( LTG_ISONENODE(cur) ) {
curtree = LTG_NODE(cur);
hashing(base,curtree);
if ( !left || ltree_compare( left, curtree ) > 0 )
left = curtree;
if ( !right || ltree_compare( right, curtree ) < 0 )
right = curtree;
} else {
if ( isalltrue || LTG_ISALLTRUE(cur) )
isalltrue = true;
else {
BITVECP sc=LTG_SIGN(cur);
LOOPBYTE(
((unsigned char*)base)[i] |= sc[i];
);
}
curtree = LTG_LNODE(cur);
if ( !left || ltree_compare( left, curtree ) > 0 )
left = curtree;
curtree = LTG_RNODE(cur);
if ( !right || ltree_compare( right, curtree ) < 0 )
right = curtree;
}
}
if ( isalltrue == false ) {
isalltrue = true;
LOOPBYTE(
if ( ((unsigned char*)base)[i] != 0xff ) {
isalltrue = false;
break;
}
);
}
isleqr = ( left==right || ISEQ(left,right) ) ? true : false;
*size = LTG_HDRSIZE + ( (isalltrue) ? 0 : SIGLEN ) + left->len + ( (isleqr) ? 0 : right->len );
result = (ltree_gist*)palloc( *size );
result->len = *size;
result->flag = 0;
if ( isalltrue )
result->flag |= LTG_ALLTRUE;
else
memcpy( (void*)LTG_SIGN(result), base, SIGLEN );
memcpy( (void*)LTG_LNODE(result), (void*)left, left->len );
if ( isleqr )
result->flag |= LTG_NORIGHT;
else
memcpy( (void*)LTG_RNODE(result), (void*)right, right->len );
PG_RETURN_POINTER(result);
}
Datum
ltree_penalty(PG_FUNCTION_ARGS) {
ltree_gist *origval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(0) )->key );
ltree_gist *newval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(1) )->key );
float *penalty = (float *) PG_GETARG_POINTER(2);
int4 cmpr,cmpl;
cmpl = ltree_compare( LTG_GETLNODE(origval), LTG_GETLNODE(newval) );
cmpr = ltree_compare( LTG_GETRNODE(newval), LTG_GETRNODE(origval));
*penalty = max( cmpl, 0 ) + max( cmpr, 0 );
PG_RETURN_POINTER(penalty);
}
/* used for sorting */
typedef struct rix {
int index;
ltree *r;
} RIX;
static int
treekey_cmp(const void *a, const void *b) {
return ltree_compare(
((RIX *) a)->r,
((RIX *) b)->r
);
}
Datum
ltree_picksplit(PG_FUNCTION_ARGS) {
bytea *entryvec = (bytea*) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1);
OffsetNumber j;
int4 i;
RIX *array;
OffsetNumber maxoff;
int nbytes;
int size;
ltree *lu_l,*lu_r, *ru_l, *ru_r;
ltree_gist *lu, *ru;
BITVEC ls,rs;
bool lisat=false, risat=false, isleqr;
memset( (void*)ls,0,sizeof(BITVEC) );
memset( (void*)rs,0,sizeof(BITVEC) );
maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 1;
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
v->spl_left = (OffsetNumber *) palloc(nbytes);
v->spl_right = (OffsetNumber *) palloc(nbytes);
v->spl_nleft = 0;
v->spl_nright = 0;
array = (RIX *) palloc(sizeof(RIX) * (maxoff + 1));
/* copy the data into RIXes, and sort the RIXes */
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) {
array[j].index = j;
lu = GETENTRY( entryvec, j ); /* use as tmp val */
array[j].r = LTG_GETLNODE(lu);
}
qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
sizeof(RIX), treekey_cmp);
lu_l = lu_r = ru_l = ru_r = NULL;
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) {
lu = GETENTRY( entryvec, array[j].index ); /* use as tmp val */
if (j <= (maxoff - FirstOffsetNumber + 1) / 2) {
v->spl_left[v->spl_nleft] = array[j].index;
v->spl_nleft++;
if ( lu_r==NULL || ltree_compare( LTG_GETRNODE(lu), lu_r ) > 0 )
lu_r = LTG_GETRNODE(lu);
if ( LTG_ISONENODE(lu) )
hashing(ls,LTG_NODE(lu));
else {
if ( lisat || LTG_ISALLTRUE(lu) )
lisat = true;
else {
BITVECP sc=LTG_SIGN(lu);
LOOPBYTE(
((unsigned char*)ls)[i] |= sc[i];
);
}
}
} else {
v->spl_right[v->spl_nright] = array[j].index;
v->spl_nright++;
if ( ru_r==NULL || ltree_compare( LTG_GETRNODE(lu), ru_r ) > 0 )
ru_r = LTG_GETRNODE(lu);
if ( LTG_ISONENODE(lu) )
hashing(rs,LTG_NODE(lu));
else {
if ( risat || LTG_ISALLTRUE(lu) )
risat = true;
else {
BITVECP sc=LTG_SIGN(lu);
LOOPBYTE(
((unsigned char*)rs)[i] |= sc[i];
);
}
}
}
}
if ( lisat == false ) {
lisat = true;
LOOPBYTE(
if ( ((unsigned char*)ls)[i] != 0xff ) {
lisat = false;
break;
}
);
}
if ( risat == false ) {
risat = true;
LOOPBYTE(
if ( ((unsigned char*)rs)[i] != 0xff ) {
risat = false;
break;
}
);
}
lu_l = LTG_GETLNODE( GETENTRY( entryvec, array[FirstOffsetNumber].index ) );
isleqr = ( lu_l==lu_r || ISEQ(lu_l,lu_r) ) ? true : false;
size = LTG_HDRSIZE + ( (lisat) ? 0 : SIGLEN ) + lu_l->len + ( (isleqr) ? 0 : lu_r->len );
lu = (ltree_gist*)palloc( size );
lu->len = size;
lu->flag = 0;
if ( lisat )
lu->flag |= LTG_ALLTRUE;
else
memcpy( (void*)LTG_SIGN(lu), ls, SIGLEN );
memcpy( (void*)LTG_LNODE(lu), (void*)lu_l, lu_l->len );
if ( isleqr )
lu->flag |= LTG_NORIGHT;
else
memcpy( (void*)LTG_RNODE(lu), (void*)lu_r, lu_r->len );
ru_l = LTG_GETLNODE( GETENTRY( entryvec, array[ 1 + ((maxoff - FirstOffsetNumber + 1) / 2) ].index ) );
isleqr = ( ru_l==ru_r || ISEQ(ru_l,ru_r) ) ? true : false;
size = LTG_HDRSIZE + ( (risat) ? 0 : SIGLEN ) + ru_l->len + ( (isleqr) ? 0 : ru_r->len );
ru = (ltree_gist*)palloc( size );
ru->len = size;
ru->flag = 0;
if ( risat )
ru->flag |= LTG_ALLTRUE;
else
memcpy( (void*)LTG_SIGN(ru), rs, SIGLEN );
memcpy( (void*)LTG_LNODE(ru), (void*)ru_l, ru_l->len );
if ( isleqr )
ru->flag |= LTG_NORIGHT;
else
memcpy( (void*)LTG_RNODE(ru), (void*)ru_r, ru_r->len );
pfree(array);
v->spl_ldatum = PointerGetDatum(lu);
v->spl_rdatum = PointerGetDatum(ru);
PG_RETURN_POINTER(v);
}
static bool
gist_isparent(ltree_gist *key, ltree *query) {
int4 numlevel = query->numlevel;
int i;
for(i=query->numlevel;i>=0;i--) {
query->numlevel=i;
if ( ltree_compare(query,LTG_GETLNODE(key)) >=0 && ltree_compare(query,LTG_GETRNODE(key)) <= 0 ) {
query->numlevel = numlevel;
return true;
}
}
query->numlevel = numlevel;
return false;
}
static bool
gist_ischild(ltree_gist *key, ltree *query) {
ltree *left = LTG_GETLNODE(key);
ltree *right = LTG_GETRNODE(key);
int4 numlevelL = left->numlevel;
int4 numlevelR = right->numlevel;
bool res = true;
if ( numlevelL > query->numlevel )
left->numlevel = query->numlevel;
if ( ltree_compare(query,left) < 0 )
res = false;
if ( numlevelR > query->numlevel )
right->numlevel = query->numlevel;
if ( res && ltree_compare(query,right) > 0 )
res = false;
left->numlevel = numlevelL;
right->numlevel = numlevelR;
return res;
}
static bool
gist_qe(ltree_gist *key, lquery* query) {
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
int qlen = query->numlevel;
if ( LTG_ISALLTRUE(key) )
return true;
while( qlen>0 ) {
if ( curq->numvar && LQL_CANLOOKSIGN(curq) ) {
bool isexist=false;
int vlen = curq->numvar;
lquery_variant *curv = LQL_FIRST(curq);
while( vlen>0 ) {
if ( GETBIT( sign, HASHVAL( curv->val ) ) ) {
isexist=true;
break;
}
curv = LVAR_NEXT(curv);
vlen--;
}
if ( !isexist )
return false;
}
curq = LQL_NEXT(curq);
qlen--;
}
return true;
}
static int
gist_tqcmp(ltree* t, lquery* q) {
ltree_level *al = LTREE_FIRST(t);
lquery_level *ql = LQUERY_FIRST(q);
lquery_variant *bl;
int an = t->numlevel;
int bn = q->firstgood;
int res = 0;
while( an>0 && bn>0 ) {
bl = LQL_FIRST(ql);
if ( (res = strncmp( al->name, bl->name, min(al->len, bl->len))) == 0 ) {
if ( al->len != bl->len )
return al->len - bl->len;
} else
return res;
an--; bn--;
al = LEVEL_NEXT(al);
ql = LQL_NEXT(ql);
}
return t->numlevel - q->firstgood;
}
static bool
gist_between(ltree_gist *key, lquery* query) {
ltree *left = LTG_GETLNODE(key);
ltree *right = LTG_GETRNODE(key);
int4 numlevelL = left->numlevel;
int4 numlevelR = right->numlevel;
bool res = true;
if ( query->firstgood == 0 )
return true;
if ( numlevelL > query->firstgood )
left->numlevel = query->firstgood;
if ( gist_tqcmp(left,query) > 0 )
res = false;
if ( numlevelR > query->firstgood )
right->numlevel = query->firstgood;
if ( res && gist_tqcmp(right,query) < 0 )
res = false;
left->numlevel = numlevelL;
right->numlevel = numlevelR;
return res;
}
static bool
checkcondition_bit(void *checkval, ITEM* val ) {
return ( FLG_CANLOOKSIGN(val->flag) ) ? GETBIT( checkval, HASHVAL( val->val ) ) : true;
}
static bool
gist_qtxt(ltree_gist *key, ltxtquery* query) {
if ( LTG_ISALLTRUE(key) )
return true;
return execute(
GETQUERY(query),
(void*)LTG_SIGN(key), false,
checkcondition_bit
);
}
Datum
ltree_consistent(PG_FUNCTION_ARGS) {
GISTENTRY *entry = (GISTENTRY*)PG_GETARG_POINTER(0);
char *query = (char*)DatumGetPointer( PG_DETOAST_DATUM(PG_GETARG_DATUM(1)) );
ltree_gist *key = (ltree_gist*)DatumGetPointer( entry->key );
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
bool res = false;
#ifndef assert_enabled
#define assert_enabled 0
#endif
switch( strategy ) {
case BTLessStrategyNumber:
res = ( GIST_LEAF( entry ) ) ?
( ltree_compare((ltree*)query,LTG_NODE(key)) > 0 )
:
( ltree_compare((ltree*)query,LTG_GETLNODE(key)) >= 0 );
break;
case BTLessEqualStrategyNumber:
res = ( ltree_compare((ltree*)query,LTG_GETLNODE(key)) >= 0 );
break;
case BTEqualStrategyNumber:
if ( GIST_LEAF( entry ) )
res = ( ltree_compare((ltree*)query,LTG_NODE(key)) == 0 );
else
res = (
ltree_compare((ltree*)query,LTG_GETLNODE(key)) >= 0
&&
ltree_compare((ltree*)query,LTG_GETRNODE(key)) <= 0
);
break;
case BTGreaterEqualStrategyNumber:
res = ( ltree_compare((ltree*)query,LTG_GETRNODE(key)) <= 0 );
break;
case BTGreaterStrategyNumber:
res = ( GIST_LEAF( entry ) ) ?
( ltree_compare((ltree*)query,LTG_GETRNODE(key)) < 0 )
:
( ltree_compare((ltree*)query,LTG_GETRNODE(key)) <= 0 );
break;
case 10:
res = ( GIST_LEAF( entry ) ) ?
inner_isparent( (ltree*)query, LTG_NODE(key) )
:
gist_isparent( key, (ltree*)query);
break;
case 11:
res = ( GIST_LEAF( entry ) ) ?
inner_isparent( LTG_NODE(key), (ltree*)query)
:
gist_ischild( key, (ltree*)query);
break;
case 12:
case 13:
if ( GIST_LEAF( entry ) )
res = DatumGetBool( DirectFunctionCall2( ltq_regex,
PointerGetDatum( LTG_NODE(key) ),
PointerGetDatum( (lquery*)query )
) );
else
res = ( gist_qe(key, (lquery*)query) && gist_between(key, (lquery*)query) );
break;
case 14:
case 15:
if ( GIST_LEAF( entry ) )
res = DatumGetBool( DirectFunctionCall2( ltxtq_exec,
PointerGetDatum( LTG_NODE(key) ),
PointerGetDatum( (lquery*)query )
) );
else
res = gist_qtxt(key, (ltxtquery*)query);
break;
default:
elog(ERROR,"Unknown StrategyNumber: %d", strategy);
}
PG_RETURN_BOOL(res);
}

433
contrib/ltree/ltree_io.c Normal file
View File

@ -0,0 +1,433 @@
/*
* in/out function for ltree and lquery
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
#include "crc32.h"
PG_FUNCTION_INFO_V1(ltree_in);
Datum ltree_in(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ltree_out);
Datum ltree_out(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(lquery_in);
Datum lquery_in(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(lquery_out);
Datum lquery_out(PG_FUNCTION_ARGS);
#define UNCHAR elog(ERROR,"Syntax error in position %d near '%c'", ptr-buf, *ptr)
typedef struct {
char* start;
int len;
int flag;
} nodeitem;
#define LTPRS_WAITNAME 0
#define LTPRS_WAITDELIM 1
Datum
ltree_in(PG_FUNCTION_ARGS) {
char *buf = (char *) PG_GETARG_POINTER(0);
char *ptr;
nodeitem *list, *lptr;
int num=0, totallen = 0;
int state = LTPRS_WAITNAME;
ltree *result;
ltree_level *curlevel;
ptr=buf;
while( *ptr ) {
if ( *ptr == '.' )
num++;
ptr++;
}
list = lptr = (nodeitem*) palloc( sizeof(nodeitem)*(num+1) );
ptr=buf;
while( *ptr ) {
if ( state == LTPRS_WAITNAME ) {
if ( ISALNUM(*ptr) ) {
lptr->start = ptr;
state = LTPRS_WAITDELIM;
} else
UNCHAR;
} else if ( state == LTPRS_WAITDELIM ) {
if ( *ptr == '.' ) {
lptr->len = ptr - lptr->start;
if ( lptr->len > 255 )
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
lptr->len, lptr->start - buf);
totallen += lptr->len + LEVEL_HDRSIZE;
lptr++;
state = LTPRS_WAITNAME;
} else if ( !ISALNUM(*ptr) )
UNCHAR;
} else
elog(ERROR,"Inner error in parser");
ptr++;
}
if ( state == LTPRS_WAITDELIM ) {
lptr->len = ptr - lptr->start;
if ( lptr->len > 255 )
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
lptr->len, lptr->start - buf);
totallen += lptr->len + LEVEL_HDRSIZE;
lptr++;
} else if ( ! (state == LTPRS_WAITNAME && lptr == list) )
elog(ERROR,"Unexpected end of line");
result = (ltree*)palloc( LTREE_HDRSIZE + totallen );
result->len = LTREE_HDRSIZE + totallen;
result->numlevel = lptr-list;
curlevel = LTREE_FIRST(result);
lptr=list;
while( lptr-list < result->numlevel ) {
curlevel->len = (uint8) lptr->len;
memcpy( curlevel->name, lptr->start, lptr->len);
curlevel = LEVEL_NEXT(curlevel);
lptr++;
}
pfree(list);
PG_RETURN_POINTER(result);
}
Datum
ltree_out(PG_FUNCTION_ARGS) {
ltree *in = PG_GETARG_LTREE(0);
char *buf,*ptr;
int i;
ltree_level *curlevel;
ptr = buf = (char*)palloc( in->len );
curlevel = LTREE_FIRST(in);
for(i=0;i<in->numlevel;i++) {
if ( i!=0 ) {
*ptr = '.';
ptr++;
}
memcpy( ptr, curlevel->name, curlevel->len );
ptr+=curlevel->len;
curlevel = LEVEL_NEXT(curlevel);
}
*ptr='\0';
PG_FREE_IF_COPY(in,0);
PG_RETURN_POINTER(buf);
}
#define LQPRS_WAITLEVEL 0
#define LQPRS_WAITDELIM 1
#define LQPRS_WAITOPEN 2
#define LQPRS_WAITFNUM 3
#define LQPRS_WAITSNUM 4
#define LQPRS_WAITND 5
#define LQPRS_WAITCLOSE 6
#define LQPRS_WAITEND 7
#define LQPRS_WAITVAR 8
#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
Datum
lquery_in(PG_FUNCTION_ARGS) {
char *buf = (char *) PG_GETARG_POINTER(0);
char *ptr;
int num=0, totallen = 0, numOR=0;
int state = LQPRS_WAITLEVEL;
lquery *result;
nodeitem *lptr=NULL;
lquery_level *cur,*curqlevel, *tmpql;
lquery_variant *lrptr=NULL;
bool hasnot=false;
bool wasbad=false;
ptr=buf;
while( *ptr ) {
if ( *ptr == '.' )
num++;
else if ( *ptr == '|' )
numOR++;
ptr++;
}
num++;
curqlevel = tmpql = (lquery_level*) palloc( ( LQL_HDRSIZE+sizeof(nodeitem*) )*(num) );
memset((void*)tmpql,0, ( LQL_HDRSIZE+sizeof(nodeitem*) )*(num) );
ptr=buf;
while( *ptr ) {
if ( state==LQPRS_WAITLEVEL ) {
if ( ISALNUM(*ptr) ) {
GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) );
memset((void*)GETVAR(curqlevel), 0,sizeof(nodeitem)*(numOR+1) );
lptr->start = ptr;
state = LQPRS_WAITDELIM;
curqlevel->numvar = 1;
} else if ( *ptr == '!' ) {
GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) );
memset((void*)GETVAR(curqlevel), 0,sizeof(nodeitem)*(numOR+1) );
lptr->start = ptr+1;
state = LQPRS_WAITDELIM;
curqlevel->numvar = 1;
curqlevel->flag |= LQL_NOT;
hasnot=true;
} else if ( *ptr == '*' ) {
state = LQPRS_WAITOPEN;
} else
UNCHAR;
} else if ( state==LQPRS_WAITVAR ) {
if ( ISALNUM(*ptr) ) {
lptr++;
lptr->start = ptr;
state = LQPRS_WAITDELIM;
curqlevel->numvar++;
} else
UNCHAR;
} else if ( state==LQPRS_WAITDELIM ) {
if ( *ptr == '@' ) {
if ( lptr->start == ptr )
UNCHAR;
lptr->flag |= LVAR_INCASE;
curqlevel->flag |= LVAR_INCASE;
} else if ( *ptr == '*' ) {
if ( lptr->start == ptr )
UNCHAR;
lptr->flag |= LVAR_ANYEND;
curqlevel->flag |= LVAR_ANYEND;
} else if ( *ptr == '%' ) {
if ( lptr->start == ptr )
UNCHAR;
lptr->flag |= LVAR_SUBLEXEM;
curqlevel->flag |= LVAR_SUBLEXEM;
} else if ( *ptr == '|' ) {
lptr->len = ptr - lptr->start -
( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) -
( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) -
( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
if ( lptr->len > 255 )
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
lptr->len, lptr->start - buf);
state = LQPRS_WAITVAR;
} else if ( *ptr == '.' ) {
lptr->len = ptr - lptr->start -
( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) -
( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) -
( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
if ( lptr->len > 255 )
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
lptr->len, lptr->start - buf);
state = LQPRS_WAITLEVEL;
curqlevel++;
} else if ( ISALNUM(*ptr) ) {
if ( lptr->flag )
UNCHAR;
} else
UNCHAR;
} else if ( state == LQPRS_WAITOPEN ) {
if ( *ptr == '{' ) {
state = LQPRS_WAITFNUM;
} else if ( *ptr == '.' ) {
curqlevel->low=0;
curqlevel->high=0xffff;
curqlevel++;
state = LQPRS_WAITLEVEL;
} else
UNCHAR;
} else if ( state == LQPRS_WAITFNUM ) {
if ( *ptr == ',' ) {
state = LQPRS_WAITSNUM;
} else if ( isdigit(*ptr) ) {
curqlevel->low = atoi( ptr );
state = LQPRS_WAITND;
} else
UNCHAR;
} else if ( state == LQPRS_WAITSNUM ) {
if ( isdigit(*ptr) ) {
curqlevel->high = atoi( ptr );
state = LQPRS_WAITCLOSE;
} else if ( *ptr == '}' ) {
curqlevel->high = 0xffff;
state = LQPRS_WAITEND;
} else
UNCHAR;
} else if ( state == LQPRS_WAITCLOSE ) {
if ( *ptr == '}' )
state = LQPRS_WAITEND;
else if ( !isdigit(*ptr) )
UNCHAR;
} else if ( state == LQPRS_WAITND ) {
if ( *ptr == '}' ) {
curqlevel->high = curqlevel->low;
state = LQPRS_WAITEND;
} else if ( *ptr == ',' )
state = LQPRS_WAITSNUM;
else if ( !isdigit(*ptr) )
UNCHAR;
} else if ( state == LQPRS_WAITEND ) {
if ( *ptr == '.' ) {
state = LQPRS_WAITLEVEL;
curqlevel++;
} else
UNCHAR;
} else
elog(ERROR,"Inner error in parser");
ptr++;
}
if ( state==LQPRS_WAITDELIM ) {
if ( lptr->start == ptr )
elog(ERROR,"Unexpected end of line");
lptr->len = ptr - lptr->start -
( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) -
( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) -
( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
if ( lptr->len==0 )
elog(ERROR,"Unexpected end of line");
if ( lptr->len > 255 )
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
lptr->len, lptr->start - buf);
} else if ( state == LQPRS_WAITOPEN ) {
curqlevel->high = 0xffff;
} else if ( state != LQPRS_WAITEND )
elog(ERROR,"Unexpected end of line");
curqlevel = tmpql;
totallen = LQUERY_HDRSIZE;
while( curqlevel-tmpql < num ) {
totallen += LQL_HDRSIZE;
if ( curqlevel->numvar ) {
lptr = GETVAR(curqlevel);
while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
totallen += LVAR_HDRSIZE + lptr->len;
lptr++;
}
} else if ( curqlevel->low > curqlevel->high )
elog(ERROR,"Low limit(%d) is greater than upper(%d)",curqlevel->low,curqlevel->high );
curqlevel++;
}
result = (lquery*)palloc( totallen );
result->len = totallen;
result->numlevel = num;
result->firstgood = 0;
result->flag=0;
if ( hasnot )
result->flag |= LQUERY_HASNOT;
cur = LQUERY_FIRST(result);
curqlevel = tmpql;
while( curqlevel-tmpql < num ) {
memcpy(cur,curqlevel,LQL_HDRSIZE);
cur->totallen=LQL_HDRSIZE;
if ( curqlevel->numvar ) {
lrptr = LQL_FIRST(cur);
lptr = GETVAR(curqlevel);
while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
cur->totallen += LVAR_HDRSIZE + lptr->len;
lrptr->len = lptr->len;
lrptr->flag = lptr->flag;
lrptr->val = crc32_sz((uint8 *) lptr->start, lptr->len);
memcpy( lrptr->name, lptr->start, lptr->len);
lptr++;
lrptr = LVAR_NEXT( lrptr );
}
pfree( GETVAR(curqlevel) );
if ( cur->numvar > 1 || cur->flag != 0 )
wasbad=true;
else if ( wasbad==false )
(result->firstgood)++;
} else
wasbad=true;
curqlevel++;
cur = LQL_NEXT(cur);
}
pfree(tmpql);
PG_RETURN_POINTER(result);
}
Datum
lquery_out(PG_FUNCTION_ARGS) {
lquery *in = PG_GETARG_LQUERY(0);
char *buf,*ptr;
int i,j,totallen=0;
lquery_level *curqlevel;
lquery_variant *curtlevel;
curqlevel = LQUERY_FIRST(in);
for(i=0;i<in->numlevel;i++) {
if ( curqlevel->numvar )
totallen = (curqlevel->numvar*4) + 1 + curqlevel->totallen;
else
totallen = 2*11 + 4;
totallen++;
curqlevel = LQL_NEXT(curqlevel);
}
ptr = buf = (char*)palloc( totallen );
curqlevel = LQUERY_FIRST(in);
for(i=0;i<in->numlevel;i++) {
if ( i!=0 ) {
*ptr = '.';
ptr++;
}
if ( curqlevel->numvar ) {
if ( curqlevel->flag & LQL_NOT ) {
*ptr = '!';
ptr++;
}
curtlevel = LQL_FIRST(curqlevel);
for(j=0;j<curqlevel->numvar;j++) {
if ( j!=0 ) {
*ptr = '|';
ptr++;
}
memcpy( ptr, curtlevel->name, curtlevel->len );
ptr+=curtlevel->len;
if ( (curtlevel->flag & LVAR_SUBLEXEM) ) {
*ptr = '%';
ptr++;
}
if ( (curtlevel->flag & LVAR_INCASE) ) {
*ptr = '@';
ptr++;
}
if ( (curtlevel->flag & LVAR_ANYEND) ) {
*ptr = '*';
ptr++;
}
curtlevel = LVAR_NEXT(curtlevel);
}
} else {
if ( curqlevel->low == curqlevel->high ) {
sprintf(ptr,"*{%d}",curqlevel->low);
} else if ( curqlevel->low == 0 ) {
if ( curqlevel->high == 0xffff ) {
*ptr='*';
*(ptr+1)='\0';
} else
sprintf(ptr,"*{,%d}",curqlevel->high);
} else if ( curqlevel->high == 0xffff ) {
sprintf(ptr,"*{%d,}",curqlevel->low);
} else
sprintf(ptr,"*{%d,%d}", curqlevel->low, curqlevel->high);
ptr = strchr(ptr,'\0');
}
curqlevel = LQL_NEXT(curqlevel);
}
*ptr='\0';
PG_FREE_IF_COPY(in,0);
PG_RETURN_POINTER(buf);
}

310
contrib/ltree/ltree_op.c Normal file
View File

@ -0,0 +1,310 @@
/*
* op function for ltree
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
/* compare functions */
PG_FUNCTION_INFO_V1(ltree_cmp);
PG_FUNCTION_INFO_V1(ltree_lt);
PG_FUNCTION_INFO_V1(ltree_le);
PG_FUNCTION_INFO_V1(ltree_eq);
PG_FUNCTION_INFO_V1(ltree_ne);
PG_FUNCTION_INFO_V1(ltree_ge);
PG_FUNCTION_INFO_V1(ltree_gt);
PG_FUNCTION_INFO_V1(nlevel);
PG_FUNCTION_INFO_V1(ltree_isparent);
PG_FUNCTION_INFO_V1(ltree_risparent);
PG_FUNCTION_INFO_V1(subltree);
PG_FUNCTION_INFO_V1(subpath);
PG_FUNCTION_INFO_V1(ltree_addltree);
PG_FUNCTION_INFO_V1(ltree_addtext);
PG_FUNCTION_INFO_V1(ltree_textadd);
Datum ltree_cmp(PG_FUNCTION_ARGS);
Datum ltree_lt(PG_FUNCTION_ARGS);
Datum ltree_le(PG_FUNCTION_ARGS);
Datum ltree_eq(PG_FUNCTION_ARGS);
Datum ltree_ne(PG_FUNCTION_ARGS);
Datum ltree_ge(PG_FUNCTION_ARGS);
Datum ltree_gt(PG_FUNCTION_ARGS);
Datum nlevel(PG_FUNCTION_ARGS);
Datum subltree(PG_FUNCTION_ARGS);
Datum subpath(PG_FUNCTION_ARGS);
Datum ltree_addltree(PG_FUNCTION_ARGS);
Datum ltree_addtext(PG_FUNCTION_ARGS);
Datum ltree_textadd(PG_FUNCTION_ARGS);
int
ltree_compare(const ltree *a, const ltree *b) {
ltree_level *al = LTREE_FIRST(a);
ltree_level *bl = LTREE_FIRST(b);
int an = a->numlevel;
int bn = b->numlevel;
int res = 0;
while( an>0 && bn>0 ) {
if ( (res = strncmp( al->name, bl->name, min(al->len, bl->len))) == 0 ) {
if ( al->len != bl->len )
return (al->len - bl->len)*10*(an+1);
} else
return res*10*(an+1);
an--; bn--;
al = LEVEL_NEXT(al);
bl = LEVEL_NEXT(bl);
}
return (a->numlevel - b->numlevel)*10*(an+1);
}
#define RUNCMP \
ltree *a = PG_GETARG_LTREE(0); \
ltree *b = PG_GETARG_LTREE(1); \
int res = ltree_compare(a,b); \
PG_FREE_IF_COPY(a,0); \
PG_FREE_IF_COPY(b,1); \
Datum
ltree_cmp(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_INT32(res);
}
Datum
ltree_lt(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_BOOL( (res<0) ? true : false );
}
Datum
ltree_le(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_BOOL( (res<=0) ? true : false );
}
Datum
ltree_eq(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_BOOL( (res==0) ? true : false );
}
Datum
ltree_ge(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_BOOL( (res>=0) ? true : false );
}
Datum
ltree_gt(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_BOOL( (res>0) ? true : false );
}
Datum
ltree_ne(PG_FUNCTION_ARGS) {
RUNCMP
PG_RETURN_BOOL( (res!=0) ? true : false );
}
Datum
nlevel(PG_FUNCTION_ARGS) {
ltree *a = PG_GETARG_LTREE(0);
int res = a->numlevel;
PG_FREE_IF_COPY(a,0);
PG_RETURN_INT32(res);
}
bool
inner_isparent(const ltree *c, const ltree *p) {
ltree_level *cl = LTREE_FIRST(c);
ltree_level *pl = LTREE_FIRST(p);
int pn = p->numlevel;
if ( pn > c->numlevel )
return false;
while( pn>0 ) {
if ( cl->len != pl->len )
return false;
if ( strncmp( cl->name, pl->name, cl->len ) )
return false;
pn--;
cl = LEVEL_NEXT(cl);
pl = LEVEL_NEXT(pl);
}
return true;
}
Datum
ltree_isparent(PG_FUNCTION_ARGS) {
ltree *c = PG_GETARG_LTREE(1);
ltree *p = PG_GETARG_LTREE(0);
bool res = inner_isparent(c,p);
PG_FREE_IF_COPY(c,1);
PG_FREE_IF_COPY(p,0);
PG_RETURN_BOOL( res );
}
Datum
ltree_risparent(PG_FUNCTION_ARGS) {
ltree *c = PG_GETARG_LTREE(0);
ltree *p = PG_GETARG_LTREE(1);
bool res = inner_isparent(c,p);
PG_FREE_IF_COPY(c,0);
PG_FREE_IF_COPY(p,1);
PG_RETURN_BOOL( res );
}
static ltree*
inner_subltree(ltree *t, int4 startpos, int4 endpos) {
char *start=NULL,*end=NULL;
ltree_level *ptr = LTREE_FIRST(t);
ltree *res;
int i;
if ( startpos <0 || endpos <0 || startpos>=t->numlevel || startpos >= endpos )
elog(ERROR,"Wrong positions");
if ( endpos > t->numlevel )
endpos = t->numlevel;
for(i=0;i<endpos ;i++) {
if ( i==startpos )
start = (char*)ptr;
if ( i==endpos-1 ) {
end = (char*)LEVEL_NEXT(ptr);
break;
}
ptr = LEVEL_NEXT(ptr);
}
res=(ltree*)palloc( LTREE_HDRSIZE + (end-start) );
res->len = LTREE_HDRSIZE + (end-start);
res->numlevel = endpos-startpos;
memcpy( LTREE_FIRST(res), start, end-start);
return res;
}
Datum
subltree(PG_FUNCTION_ARGS) {
ltree *t = PG_GETARG_LTREE(0);
ltree *res = inner_subltree(t,PG_GETARG_INT32(1),PG_GETARG_INT32(2));
PG_FREE_IF_COPY(t,0);
PG_RETURN_POINTER(res);
}
Datum
subpath(PG_FUNCTION_ARGS) {
ltree *t = PG_GETARG_LTREE(0);
int4 start = PG_GETARG_INT32(1);
int4 len = ( fcinfo->nargs==3 ) ? PG_GETARG_INT32(2) : 0;
int4 end;
ltree *res;
end = start+len;
if ( start < 0 ) {
start = t->numlevel + start;
end = start+len;
}
if ( start < 0 ) { /* start > t->numlevel */
start = t->numlevel + start;
end = start+len;
}
if ( len < 0 )
end = t->numlevel + len;
else if ( len == 0 )
end = 0xffff;
res = inner_subltree(t,start,end);
PG_FREE_IF_COPY(t,0);
PG_RETURN_POINTER(res);
}
static ltree*
ltree_concat( ltree *a, ltree *b) {
ltree *r;
r=(ltree*)palloc( a->len + b->len - LTREE_HDRSIZE);
r->len = a->len + b->len - LTREE_HDRSIZE;
r->numlevel = a->numlevel + b->numlevel;
memcpy( LTREE_FIRST(r), LTREE_FIRST(a), a->len - LTREE_HDRSIZE);
memcpy( ((char*)LTREE_FIRST(r))+ a->len - LTREE_HDRSIZE, LTREE_FIRST(b), b->len -
LTREE_HDRSIZE);
return r;
}
Datum
ltree_addltree(PG_FUNCTION_ARGS) {
ltree *a = PG_GETARG_LTREE(0);
ltree *b = PG_GETARG_LTREE(1);
ltree *r;
r = ltree_concat(a, b);
PG_FREE_IF_COPY(a,0);
PG_FREE_IF_COPY(b,1);
PG_RETURN_POINTER(r);
}
Datum
ltree_addtext(PG_FUNCTION_ARGS) {
ltree *a = PG_GETARG_LTREE(0);
text *b = PG_GETARG_TEXT_P(1);
char *s;
ltree *r,*tmp;
s = (char*)palloc( VARSIZE(b) - VARHDRSZ+1 );
memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ );
s[VARSIZE(b) - VARHDRSZ] = '\0';
tmp = (ltree*)DatumGetPointer( DirectFunctionCall1(
ltree_in,
PointerGetDatum(s)
) );
pfree(s);
r = ltree_concat(a,tmp);
pfree( tmp );
PG_FREE_IF_COPY(a,0);
PG_FREE_IF_COPY(b,1);
PG_RETURN_POINTER(r);
}
Datum
ltree_textadd(PG_FUNCTION_ARGS) {
ltree *a = PG_GETARG_LTREE(1);
text *b = PG_GETARG_TEXT_P(0);
char *s;
ltree *r,*tmp;
s = (char*)palloc( VARSIZE(b) - VARHDRSZ + 1 );
memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ );
s[VARSIZE(b) - VARHDRSZ] = '\0';
tmp = (ltree*)DatumGetPointer( DirectFunctionCall1(
ltree_in,
PointerGetDatum(s)
) );
pfree(s);
r = ltree_concat(tmp,a);
pfree( tmp );
PG_FREE_IF_COPY(a,1);
PG_FREE_IF_COPY(b,0);
PG_RETURN_POINTER(r);
}

View File

@ -0,0 +1,16 @@
create table test ( path ltree);
insert into test values ('Top');
insert into test values ('Top.Science');
insert into test values ('Top.Science.Astronomy');
insert into test values ('Top.Science.Astronomy.Astrophysics');
insert into test values ('Top.Science.Astronomy.Cosmology');
insert into test values ('Top.Hobbies');
insert into test values ('Top.Hobbies.Amateurs_Astronomy');
insert into test values ('Top.Collections');
insert into test values ('Top.Collections.Pictures');
insert into test values ('Top.Collections.Pictures.Astronomy');
insert into test values ('Top.Collections.Pictures.Astronomy.Stars');
insert into test values ('Top.Collections.Pictures.Astronomy.Galaxies');
insert into test values ('Top.Collections.Pictures.Astronomy.Astronauts');
create index path_gist_idx on test using gist(path);
create index path_idx on test using btree(path);

View File

@ -0,0 +1,484 @@
/*
* txtquery io
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
#include "crc32.h"
PG_FUNCTION_INFO_V1(ltxtq_in);
Datum ltxtq_in(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ltxtq_out);
Datum ltxtq_out(PG_FUNCTION_ARGS);
/* parser's states */
#define WAITOPERAND 1
#define INOPERAND 2
#define WAITOPERATOR 3
/*
* node of query tree, also used
* for storing polish notation in parser
*/
typedef struct NODE {
int4 type;
int4 val;
int2 distance;
int2 length;
uint16 flag;
struct NODE *next;
} NODE;
typedef struct {
char *buf;
int4 state;
int4 count;
/* reverse polish notation in list (for temprorary usage) */
NODE *str;
/* number in str */
int4 num;
/* user-friendly operand */
int4 lenop;
int4 sumlen;
char *op;
char *curop;
} QPRS_STATE;
/*
* get token from query string
*/
static int4
gettoken_query(QPRS_STATE * state, int4 *val, int4 *lenval, char **strval, uint16 *flag)
{
while (1)
{
switch (state->state)
{
case WAITOPERAND:
if (*(state->buf) == '!')
{
(state->buf)++;
*val = (int4) '!';
return OPR;
}
else if (*(state->buf) == '(')
{
state->count++;
(state->buf)++;
return OPEN;
}
else if ( ISALNUM(*(state->buf)) )
{
state->state = INOPERAND;
*strval = state->buf;
*lenval = 1;
*flag = 0;
} else if ( !isspace(*(state->buf)) )
elog(ERROR,"Operand syntax error");
break;
case INOPERAND:
if ( ISALNUM(*(state->buf)) ) {
if ( *flag )
elog(ERROR,"Modificators syntax error");
(*lenval)++;
} else if ( *(state->buf) == '%' ) {
*flag |= LVAR_SUBLEXEM;
} else if ( *(state->buf) == '@' ) {
*flag |= LVAR_INCASE;
} else if ( *(state->buf) == '*' ) {
*flag |= LVAR_ANYEND;
} else {
state->state = WAITOPERATOR;
return VAL;
}
break;
case WAITOPERATOR:
if (*(state->buf) == '&' || *(state->buf) == '|')
{
state->state = WAITOPERAND;
*val = (int4) *(state->buf);
(state->buf)++;
return OPR;
}
else if (*(state->buf) == ')')
{
(state->buf)++;
state->count--;
return (state->count < 0) ? ERR : CLOSE;
}
else if (*(state->buf) == '\0')
return (state->count) ? ERR : END;
else if (*(state->buf) != ' ')
return ERR;
break;
default:
return ERR;
break;
}
(state->buf)++;
}
return END;
}
/*
* push new one in polish notation reverse view
*/
static void
pushquery(QPRS_STATE * state, int4 type, int4 val, int4 distance, int4 lenval, uint16 flag)
{
NODE *tmp = (NODE *) palloc(sizeof(NODE));
tmp->type = type;
tmp->val = val;
tmp->flag = flag;
if (distance > 0xffff)
elog(ERROR, "Value is too big");
if (lenval > 0xff)
elog(ERROR, "Operand is too long");
tmp->distance = distance;
tmp->length = lenval;
tmp->next = state->str;
state->str = tmp;
state->num++;
}
/*
* This function is used for query_txt parsing
*/
static void
pushval_asis(QPRS_STATE * state, int type, char *strval, int lenval, uint16 flag)
{
if (lenval > 0xffff)
elog(ERROR, "Word is too long");
pushquery(state, type, crc32_sz((uint8 *) strval, lenval),
state->curop - state->op, lenval, flag);
while (state->curop - state->op + lenval + 1 >= state->lenop)
{
int4 tmp = state->curop - state->op;
state->lenop *= 2;
state->op = (char *) repalloc((void *) state->op, state->lenop);
state->curop = state->op + tmp;
}
memcpy((void *) state->curop, (void *) strval, lenval);
state->curop += lenval;
*(state->curop) = '\0';
state->curop++;
state->sumlen += lenval + 1;
return;
}
#define STACKDEPTH 32
/*
* make polish notaion of query
*/
static int4
makepol(QPRS_STATE * state)
{
int4 val,
type;
int4 lenval;
char *strval;
int4 stack[STACKDEPTH];
int4 lenstack = 0;
uint16 flag;
while ((type = gettoken_query(state, &val, &lenval, &strval,&flag)) != END) {
switch (type)
{
case VAL:
pushval_asis(state, VAL, strval, lenval,flag);
while (lenstack && (stack[lenstack - 1] == (int4) '&' ||
stack[lenstack - 1] == (int4) '!'))
{
lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0);
}
break;
case OPR:
if (lenstack && val == (int4) '|')
pushquery(state, OPR, val, 0, 0, 0);
else
{
if (lenstack == STACKDEPTH)
elog(ERROR, "Stack too short");
stack[lenstack] = val;
lenstack++;
}
break;
case OPEN:
if (makepol(state) == ERR)
return ERR;
if (lenstack && (stack[lenstack - 1] == (int4) '&' ||
stack[lenstack - 1] == (int4) '!'))
{
lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0);
}
break;
case CLOSE:
while (lenstack)
{
lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0);
};
return END;
break;
case ERR:
default:
elog(ERROR, "Syntax error");
return ERR;
}
}
while (lenstack) {
lenstack--;
pushquery(state, OPR, stack[lenstack], 0, 0, 0);
};
return END;
}
static void
findoprnd(ITEM * ptr, int4 *pos)
{
if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
{
ptr[*pos].left = 0;
(*pos)++;
}
else if (ptr[*pos].val == (int4) '!')
{
ptr[*pos].left = 1;
(*pos)++;
findoprnd(ptr, pos);
}
else
{
ITEM *curitem = &ptr[*pos];
int4 tmp = *pos;
(*pos)++;
findoprnd(ptr, pos);
curitem->left = *pos - tmp;
findoprnd(ptr, pos);
}
}
/*
* input
*/
static ltxtquery *
queryin(char *buf)
{
QPRS_STATE state;
int4 i;
ltxtquery *query;
int4 commonlen;
ITEM *ptr;
NODE *tmp;
int4 pos = 0;
#ifdef BS_DEBUG
char pbuf[16384],
*cur;
#endif
/* init state */
state.buf = buf;
state.state = WAITOPERAND;
state.count = 0;
state.num = 0;
state.str = NULL;
/* init list of operand */
state.sumlen = 0;
state.lenop = 64;
state.curop = state.op = (char *) palloc(state.lenop);
*(state.curop) = '\0';
/* parse query & make polish notation (postfix, but in reverse order) */
makepol(&state);
if (!state.num)
elog(ERROR, "Empty query");
/* make finish struct */
commonlen = COMPUTESIZE(state.num, state.sumlen);
query = (ltxtquery *) palloc(commonlen);
query->len = commonlen;
query->size = state.num;
ptr = GETQUERY(query);
/* set item in polish notation */
for (i = 0; i < state.num; i++)
{
ptr[i].type = state.str->type;
ptr[i].val = state.str->val;
ptr[i].distance = state.str->distance;
ptr[i].length = state.str->length;
ptr[i].flag = state.str->flag;
tmp = state.str->next;
pfree(state.str);
state.str = tmp;
}
/* set user friendly-operand view */
memcpy((void *) GETOPERAND(query), (void *) state.op, state.sumlen);
pfree(state.op);
/* set left operand's position for every operator */
pos = 0;
findoprnd(ptr, &pos);
return query;
}
/*
* in without morphology
*/
Datum
ltxtq_in(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0)));
}
/*
* out function
*/
typedef struct
{
ITEM *curpol;
char *buf;
char *cur;
char *op;
int4 buflen;
} INFIX;
#define RESIZEBUF(inf,addsize) \
while( ( inf->cur - inf->buf ) + addsize + 1 >= inf->buflen ) \
{ \
int4 len = inf->cur - inf->buf; \
inf->buflen *= 2; \
inf->buf = (char*) repalloc( (void*)inf->buf, inf->buflen ); \
inf->cur = inf->buf + len; \
}
/*
* recursive walk on tree and print it in
* infix (human-readable) view
*/
static void
infix(INFIX * in, bool first)
{
if (in->curpol->type == VAL)
{
char *op = in->op + in->curpol->distance;
RESIZEBUF(in, in->curpol->length * 2 + 5);
while (*op) {
*(in->cur) = *op;
op++;
in->cur++;
}
if ( in->curpol->flag & LVAR_SUBLEXEM ) {
*(in->cur) = '%';
in->cur++;
}
if ( in->curpol->flag & LVAR_INCASE ) {
*(in->cur) = '@';
in->cur++;
}
if ( in->curpol->flag & LVAR_ANYEND ) {
*(in->cur) = '*';
in->cur++;
}
*(in->cur) = '\0';
in->curpol++;
}
else if (in->curpol->val == (int4) '!')
{
bool isopr = false;
RESIZEBUF(in, 1);
*(in->cur) = '!';
in->cur++;
*(in->cur) = '\0';
in->curpol++;
if (in->curpol->type == OPR)
{
isopr = true;
RESIZEBUF(in, 2);
sprintf(in->cur, "( ");
in->cur = strchr(in->cur, '\0');
}
infix(in, isopr);
if (isopr)
{
RESIZEBUF(in, 2);
sprintf(in->cur, " )");
in->cur = strchr(in->cur, '\0');
}
}
else
{
int4 op = in->curpol->val;
INFIX nrm;
in->curpol++;
if (op == (int4) '|' && !first)
{
RESIZEBUF(in, 2);
sprintf(in->cur, "( ");
in->cur = strchr(in->cur, '\0');
}
nrm.curpol = in->curpol;
nrm.op = in->op;
nrm.buflen = 16;
nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
/* get right operand */
infix(&nrm, false);
/* get & print left operand */
in->curpol = nrm.curpol;
infix(in, false);
/* print operator & right operand */
RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
sprintf(in->cur, " %c %s", op, nrm.buf);
in->cur = strchr(in->cur, '\0');
pfree(nrm.buf);
if (op == (int4) '|' && !first)
{
RESIZEBUF(in, 2);
sprintf(in->cur, " )");
in->cur = strchr(in->cur, '\0');
}
}
}
Datum
ltxtq_out(PG_FUNCTION_ARGS)
{
ltxtquery *query = PG_GETARG_LTXTQUERY(0);
INFIX nrm;
if (query->size == 0)
elog(ERROR, "Empty");
nrm.curpol = GETQUERY(query);
nrm.buflen = 32;
nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
*(nrm.cur) = '\0';
nrm.op = GETOPERAND(query);
infix(&nrm, true);
PG_FREE_IF_COPY(query, 0);
PG_RETURN_POINTER(nrm.buf);
}

View File

@ -0,0 +1,99 @@
/*
* txtquery operations with ltree
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
PG_FUNCTION_INFO_V1(ltxtq_exec);
PG_FUNCTION_INFO_V1(ltxtq_rexec);
/*
* check for boolean condition
*/
bool
execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM * val)) {
if (curitem->type == VAL)
return (*chkcond) (checkval, curitem);
else if (curitem->val == (int4) '!') {
return (calcnot) ?
((execute(curitem + 1, checkval, calcnot, chkcond)) ? false : true)
: true;
} else if (curitem->val == (int4) '&') {
if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
return execute(curitem + 1, checkval, calcnot, chkcond);
else
return false;
} else { /* |-operator */
if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
return true;
else
return execute(curitem + 1, checkval, calcnot, chkcond);
}
return false;
}
typedef struct {
ltree *node;
char *operand;
} CHKVAL;
static bool
checkcondition_str(void* checkval, ITEM * val) {
ltree_level *level = LTREE_FIRST( ((CHKVAL*)checkval)->node );
int tlen = ((CHKVAL*)checkval)->node->numlevel;
char *op = ((CHKVAL*)checkval)->operand + val->distance;
int (*cmpptr)(const char *,const char *,size_t);
cmpptr = ( val->flag & LVAR_INCASE ) ? strncasecmp : strncmp;
while( tlen > 0 ) {
if ( val->flag & LVAR_SUBLEXEM ) {
if ( compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND) ) )
return true;
} else if (
(
val->length == level->len ||
( level->len > val->length && (val->flag & LVAR_ANYEND) )
) &&
(*cmpptr)( op, level->name, val->length) == 0 )
return true;
tlen--;
level = LEVEL_NEXT(level);
}
return false;
}
Datum
ltxtq_exec(PG_FUNCTION_ARGS) {
ltree *val = PG_GETARG_LTREE(0);
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
CHKVAL chkval;
bool result;
chkval.node = val;
chkval.operand = GETOPERAND(query);
result = execute(
GETQUERY(query),
&chkval,
true,
checkcondition_str
);
PG_FREE_IF_COPY(val, 0);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(result);
}
Datum
ltxtq_rexec(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( ltxtq_exec,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}

71
contrib/ltree/patch.72 Normal file
View File

@ -0,0 +1,71 @@
*** ltree.sql.in Tue Jul 23 18:49:12 2002
--- ltree.sql.in.72 Wed Jul 17 18:59:08 2002
***************
*** 177,188 ****
-- B-tree support
! INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opcdefault, opckeytype)
VALUES (
(SELECT oid FROM pg_am WHERE amname = 'btree'),
'ltree_ops',
- (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
- 1, -- UID of superuser is hardwired to 1 as of PG 7.3
(SELECT oid FROM pg_type WHERE typname = 'ltree'),
true,
0);
--- 177,186 ----
-- B-tree support
! INSERT INTO pg_opclass (opcamid, opcname, opcintype, opcdefault, opckeytype)
VALUES (
(SELECT oid FROM pg_am WHERE amname = 'btree'),
'ltree_ops',
(SELECT oid FROM pg_type WHERE typname = 'ltree'),
true,
0);
***************
*** 376,386 ****
create function ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist_ltree_ops',
! (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
! 1, -- UID of superuser is hardwired to 1 as of PG 7.3
! pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = 'ltree' and
pg_am.amname='gist' and
--- 374,381 ----
create function ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist_ltree_ops', pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = 'ltree' and
pg_am.amname='gist' and
***************
*** 720,730 ****
create function _ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function _ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist__ltree_ops',
! (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
! 1, -- UID of superuser is hardwired to 1 as of PG 7.3
! pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = '_ltree' and
pg_am.amname='gist' and
--- 715,722 ----
create function _ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function _ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist__ltree_ops', pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = '_ltree' and
pg_am.amname='gist' and

238
contrib/ltree/sql/ltree.sql Normal file
View File

@ -0,0 +1,238 @@
\set ECHO none
\i ltree.sql
\set ECHO all
select ''::ltree;
select '1'::ltree;
select '1.2'::ltree;
select '1.2._3'::ltree;
select subltree('Top.Child1.Child2',1,2);
select subpath('Top.Child1.Child2',1,2);
select subpath('Top.Child1.Child2',-1,1);
select subpath('Top.Child1.Child2',0,-2);
select subpath('Top.Child1.Child2',0,-1);
select subpath('Top.Child1.Child2',0,0);
select subpath('Top.Child1.Child2',1,0);
select subpath('Top.Child1.Child2',0);
select subpath('Top.Child1.Child2',1);
select 'Top.Child1.Child2'::ltree || 'Child3'::text;
select 'Top.Child1.Child2'::ltree || 'Child3'::ltree;
select 'Top_0'::ltree || 'Top.Child1.Child2'::ltree;
select 'Top.Child1.Child2'::ltree || ''::ltree;
select ''::ltree || 'Top.Child1.Child2'::ltree;
select '1'::lquery;
select '4|3|2'::lquery;
select '1.2'::lquery;
select '1.4|3|2'::lquery;
select '1.0'::lquery;
select '4|3|2.0'::lquery;
select '1.2.0'::lquery;
select '1.4|3|2.0'::lquery;
select '1.*'::lquery;
select '4|3|2.*'::lquery;
select '1.2.*'::lquery;
select '1.4|3|2.*'::lquery;
select '*.1.*'::lquery;
select '*.4|3|2.*'::lquery;
select '*.1.2.*'::lquery;
select '*.1.4|3|2.*'::lquery;
select '1.*.4|3|2'::lquery;
select '1.*.4|3|2.0'::lquery;
select '1.*.4|3|2.*{1,4}'::lquery;
select '1.*.4|3|2.*{,4}'::lquery;
select '1.*.4|3|2.*{1,}'::lquery;
select '1.*.4|3|2.*{1}'::lquery;
select 'qwerty%@*.tu'::lquery;
select nlevel('1.2.3.4');
select '1.2'::ltree < '2.2'::ltree;
select '1.2'::ltree <= '2.2'::ltree;
select '2.2'::ltree = '2.2'::ltree;
select '3.2'::ltree >= '2.2'::ltree;
select '3.2'::ltree > '2.2'::ltree;
select '1.2.3'::ltree @> '1.2.3.4'::ltree;
select '1.2.3.4'::ltree @> '1.2.3.4'::ltree;
select '1.2.3.4.5'::ltree @> '1.2.3.4'::ltree;
select '1.3.3'::ltree @> '1.2.3.4'::ltree;
select 'a.b.c.d.e'::ltree ~ 'a.b.c.d.e';
select 'a.b.c.d.e'::ltree ~ 'A.b.c.d.e';
select 'a.b.c.d.e'::ltree ~ 'A@.b.c.d.e';
select 'aa.b.c.d.e'::ltree ~ 'A@.b.c.d.e';
select 'aa.b.c.d.e'::ltree ~ 'A*.b.c.d.e';
select 'aa.b.c.d.e'::ltree ~ 'A*@.b.c.d.e';
select 'aa.b.c.d.e'::ltree ~ 'A*@|g.b.c.d.e';
select 'g.b.c.d.e'::ltree ~ 'A*@|g.b.c.d.e';
select 'a.b.c.d.e'::ltree ~ 'a.b.c.d.e';
select 'a.b.c.d.e'::ltree ~ 'a.*.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{3}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{2}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{4}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{,4}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{2,}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{2,4}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{2,3}.e';
select 'a.b.c.d.e'::ltree ~ 'a.*{2,3}';
select 'a.b.c.d.e'::ltree ~ 'a.*{2,4}';
select 'a.b.c.d.e'::ltree ~ 'a.*{2,5}';
select 'a.b.c.d.e'::ltree ~ '*{2,3}.e';
select 'a.b.c.d.e'::ltree ~ '*{2,4}.e';
select 'a.b.c.d.e'::ltree ~ '*{2,5}.e';
select 'a.b.c.d.e'::ltree ~ '*.e';
select 'a.b.c.d.e'::ltree ~ '*.e.*';
select 'a.b.c.d.e'::ltree ~ '*.d.*';
select 'a.b.c.d.e'::ltree ~ '*.a.*.d.*';
select 'a.b.c.d.e'::ltree ~ '*.!d.*';
select 'a.b.c.d.e'::ltree ~ '*.!d';
select 'a.b.c.d.e'::ltree ~ '!d.*';
select 'a.b.c.d.e'::ltree ~ '!a.*';
select 'a.b.c.d.e'::ltree ~ '*.!e';
select 'a.b.c.d.e'::ltree ~ '*.!e.*';
select 'a.b.c.d.e'::ltree ~ 'a.*.!e';
select 'a.b.c.d.e'::ltree ~ 'a.*.!d';
select 'a.b.c.d.e'::ltree ~ 'a.*.!d.*';
select 'a.b.c.d.e'::ltree ~ 'a.*.!f.*';
select 'a.b.c.d.e'::ltree ~ '*.a.*.!f.*';
select 'a.b.c.d.e'::ltree ~ '*.a.*.!d.*';
select 'a.b.c.d.e'::ltree ~ '*.a.!d.*';
select 'a.b.c.d.e'::ltree ~ '*.a.!d';
select 'a.b.c.d.e'::ltree ~ 'a.!d.*';
select 'a.b.c.d.e'::ltree ~ '*.a.*.!d.*';
select 'a.b.c.d.e'::ltree ~ '*.!b.*';
select 'a.b.c.d.e'::ltree ~ '*.!b.c.*';
select 'a.b.c.d.e'::ltree ~ '*.!b.*.c.*';
select 'a.b.c.d.e'::ltree ~ '!b.*.c.*';
select 'a.b.c.d.e'::ltree ~ '!b.b.*';
select 'a.b.c.d.e'::ltree ~ '!b.*.e';
select 'a.b.c.d.e'::ltree ~ '!b.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '!b.*.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '*{2}.!b.*.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '*{1}.!b.*.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '*{1}.!b.*{1}.!c.*.e';
select 'a.b.c.d.e'::ltree ~ 'a.!b.*{1}.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*.e';
select 'a.b.c.d.e'::ltree ~ '!b.!c.*';
select 'a.b.c.d.e'::ltree ~ '!b.*.!c.*';
select 'a.b.c.d.e'::ltree ~ '*{2}.!b.*.!c.*';
select 'a.b.c.d.e'::ltree ~ '*{1}.!b.*.!c.*';
select 'a.b.c.d.e'::ltree ~ '*{1}.!b.*{1}.!c.*';
select 'a.b.c.d.e'::ltree ~ 'a.!b.*{1}.!c.*';
select 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*';
select 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*';
select 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*';
select 'QWER_TY'::ltree ~ 'q%@*';
select 'QWER_TY'::ltree ~ 'Q_t%@*';
select 'QWER_GY'::ltree ~ 'q_t%@*';
--ltxtquery
select '!tree & aWdf@*'::ltxtquery;
select 'tree & aw_qw%*'::ltxtquery;
select 'ltree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
select 'tree.awdfg'::ltree @ '!tree & aWdf@*'::ltxtquery;
select 'tree.awdfg'::ltree @ '!tree | aWdf@*'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree | aWdf@*'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree & aWdf@*'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree & aWdf@'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree & aWdf*'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree & aWdf'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree & awdf*'::ltxtquery;
select 'tree.awdfg'::ltree @ 'tree & aWdfg@'::ltxtquery;
select 'tree.awdfg_qwerty'::ltree @ 'tree & aw_qw%*'::ltxtquery;
select 'tree.awdfg_qwerty'::ltree @ 'tree & aw_rw%*'::ltxtquery;
--arrays
select '{1.2.3}'::ltree[] @> '1.2.3.4';
select '{1.2.3.4}'::ltree[] @> '1.2.3.4';
select '{1.2.3.4.5}'::ltree[] @> '1.2.3.4';
select '{1.3.3}'::ltree[] @> '1.2.3.4';
select '{5.67.8, 1.2.3}'::ltree[] @> '1.2.3.4';
select '{5.67.8, 1.2.3.4}'::ltree[] @> '1.2.3.4';
select '{5.67.8, 1.2.3.4.5}'::ltree[] @> '1.2.3.4';
select '{5.67.8, 1.3.3}'::ltree[] @> '1.2.3.4';
select '{1.2.3, 7.12.asd}'::ltree[] @> '1.2.3.4';
select '{1.2.3.4, 7.12.asd}'::ltree[] @> '1.2.3.4';
select '{1.2.3.4.5, 7.12.asd}'::ltree[] @> '1.2.3.4';
select '{1.3.3, 7.12.asd}'::ltree[] @> '1.2.3.4';
select '{ltree.asd, tree.awdfg}'::ltree[] @ 'tree & aWdfg@'::ltxtquery;
select '{j.k.l.m, g.b.c.d.e}'::ltree[] ~ 'A*@|g.b.c.d.e';
--exractors
select ('{3456,1.2.3.34}'::ltree[] ?@> '1.2.3.4') is null;
select '{3456,1.2.3}'::ltree[] ?@> '1.2.3.4';
select '{3456,1.2.3.4}'::ltree[] ?<@ '1.2.3';
select ('{3456,1.2.3.4}'::ltree[] ?<@ '1.2.5') is null;
select '{ltree.asd, tree.awdfg}'::ltree[] ?@ 'tree & aWdfg@'::ltxtquery;
select '{j.k.l.m, g.b.c.d.e}'::ltree[] ?~ 'A*@|g.b.c.d.e';
create table ltreetest (t ltree);
\copy ltreetest from 'data/ltree.data'
select * from ltreetest where t < '12.3' order by t asc;
select * from ltreetest where t <= '12.3' order by t asc;
select * from ltreetest where t = '12.3' order by t asc;
select * from ltreetest where t >= '12.3' order by t asc;
select * from ltreetest where t > '12.3' order by t asc;
select * from ltreetest where t @> '1.1.1' order by t asc;
select * from ltreetest where t <@ '1.1.1' order by t asc;
select * from ltreetest where t ~ '1.1.1.*' order by t asc;
select * from ltreetest where t ~ '*.1' order by t asc;
select * from ltreetest where t ~ '23.*.1' order by t asc;
select * from ltreetest where t ~ '23.*{1}.1' order by t asc;
select * from ltreetest where t @ '23 & 1' order by t asc;
create unique index tstidx on ltreetest (t);
set enable_seqscan=off;
select * from ltreetest where t < '12.3' order by t asc;
select * from ltreetest where t <= '12.3' order by t asc;
select * from ltreetest where t = '12.3' order by t asc;
select * from ltreetest where t >= '12.3' order by t asc;
select * from ltreetest where t > '12.3' order by t asc;
drop index tstidx;
create index tstidx on ltreetest using gist (t);
set enable_seqscan=off;
select * from ltreetest where t < '12.3' order by t asc;
select * from ltreetest where t <= '12.3' order by t asc;
select * from ltreetest where t = '12.3' order by t asc;
select * from ltreetest where t >= '12.3' order by t asc;
select * from ltreetest where t > '12.3' order by t asc;
select * from ltreetest where t @> '1.1.1' order by t asc;
select * from ltreetest where t <@ '1.1.1' order by t asc;
select * from ltreetest where t ~ '1.1.1.*' order by t asc;
select * from ltreetest where t ~ '*.1' order by t asc;
select * from ltreetest where t ~ '23.*.1' order by t asc;
select * from ltreetest where t ~ '23.*{1}.1' order by t asc;
select * from ltreetest where t @ '23 & 1' order by t asc;
create table _ltreetest (t ltree[]);
\copy _ltreetest from 'data/_ltree.data'
select count(*) from _ltreetest where t @> '1.1.1' ;
select count(*) from _ltreetest where t <@ '1.1.1' ;
select count(*) from _ltreetest where t ~ '1.1.1.*' ;
select count(*) from _ltreetest where t ~ '*.1' ;
select count(*) from _ltreetest where t ~ '23.*.1' ;
select count(*) from _ltreetest where t ~ '23.*{1}.1' ;
select count(*) from _ltreetest where t @ '23 & 1' ;
create index _tstidx on _ltreetest using gist (t);
set enable_seqscan=off;
select count(*) from _ltreetest where t @> '1.1.1' ;
select count(*) from _ltreetest where t <@ '1.1.1' ;
select count(*) from _ltreetest where t ~ '1.1.1.*' ;
select count(*) from _ltreetest where t ~ '*.1' ;
select count(*) from _ltreetest where t ~ '23.*.1' ;
select count(*) from _ltreetest where t ~ '23.*{1}.1' ;
select count(*) from _ltreetest where t @ '23 & 1' ;