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