mirror of https://github.com/postgres/postgres
parent
210e64fe08
commit
1dedbf2da5
@ -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 |
@ -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. |
@ -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); |
||||
} |
||||
|
@ -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); |
||||
} |
||||
|
@ -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; |
||||
} |
@ -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
@ -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) |
||||
) ); |
||||
} |
@ -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 |
||||
|
@ -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; |
@ -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); |
||||
} |
||||
|
@ -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); |
||||
} |
||||
|
||||
|
@ -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); |
||||
} |
@ -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); |
@ -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); |
||||
} |
||||
|
@ -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) |
||||
) ); |
||||
} |
||||
|
||||
|
@ -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 |
@ -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' ; |
||||
|
Loading…
Reference in new issue