70
Appendix F. Additional Supplied Modules
F.20. lo
The
lo
module provides support for managing Large Objects (also called LOs or BLOBs). This
includes a data type
lo
and a trigger
lo_manage
.
F.20.1. Rationale
One of the problems with the JDBC driver (and this affects the ODBC driver also), is that the specifi-
cation assumes thatreferences to BLOBs (Binary Large OBjects) are stored withina table, and if that
entry is changed, the associated BLOB is deleted from the database.
As PostgreSQL stands, this doesn’t occur. Large objects are treated as objects in their own right; a
table entry can reference a large object by OID, but there can be multiple table entries referencing
the same large object OID, so the system doesn’t delete the large object just because you change or
remove one such entry.
Now this is fine for PostgreSQL-specific applications, but standard code using JDBCor ODBCwon’t
delete the objects, resulting in orphan objects — objects that are not referenced by anything, and
simply occupy disk space.
The
lo
module allows fixing this by attaching a trigger to tables that contain LO reference columns.
The trigger essentially just does a
lo_unlink
whenever you delete or modify a value referencing a
large object. When you use this trigger, you are assuming that there is only one database reference to
any large object that is referenced in a trigger-controlled column!
The module also provides a data type
lo
,whichis really just a domain of the
oid
type. This is useful
for differentiating database columns that hold large object references from those that are OIDs of
other things. You don’t have to use the
lo
type to use the trigger, but it may be convenient to use it to
keep track of which columns in your database represent large objects that you are managing with the
trigger. Itis also rumored that the ODBC driver gets confusedif you don’t use
lo
for BLOB columns.
F.20.2. How to Use It
Here’s a simple example of usage:
CREATE TABLE image (title TEXT, raster lo);
CREATE TRIGGER t_raster BEFORE UPDATE OR DELETE ON image
FOR EACH ROW EXECUTE PROCEDURE lo_manage(raster);
For each column that will contain unique references to large objects, create a
BEFORE UPDATE OR
DELETE
trigger, and give the column name as the sole trigger argument. You can also restrict the
trigger to only execute on updates to the column by using
BEFORE UPDATE OF column_name
.If
you need multiple
lo
columns in the same table, create a separate trigger for each one, remembering
to give a different name to each trigger on the same table.
F.20.3. Limitations
•
Dropping a table will still orphan any objects it contains, as the trigger is not executed. You can
avoid this by preceding the
DROP TABLE
with
DELETE FROM
table
.
TRUNCATE
has the same hazard.
2810
115
Appendix F. Additional Supplied Modules
If you already have, or suspect you have, orphaned large objects, see the vacuumlo module to help
you clean them up. It’s a good idea to run vacuumlo occasionally as a back-stop to the
lo_manage
trigger.
•
Some frontends may create their own tables, and will not create the associated trigger(s). Also,
users maynot remember (or know) to create the triggers.
F.20.4. Author
Peter Mount <
peter@retep.org.uk
>
F.21. ltree
This module implements a data type
ltree
for representing labels of data stored in a hierarchical
tree-like structure. Extensive facilities for searching through label trees are provided.
F.21.1. Definitions
Alabel is a sequence of alphanumeric characters and underscores (for example, in C locale the char-
acters
A-Za-z0-9_
are allowed). Labels must be less than 256 bytes long.
Examples:
42
,
Personal_Services
Alabel path is a sequence of zero or more labels separated by dots, for example
L1.L2.L3
,repre-
senting a path from the root of a hierarchicaltree to a particular node. The length of a label path must
be less than 65Kb, but keeping it under 2Kb is preferable. In practice this is not a major limitation;
for example, the longest label path in the DMOZ catalog (http://www.dmoz.org) is about 240 bytes.
Example:
Top.Countries.Europe.Russia
The
ltree
module provides several data types:
•
ltree
stores a label path.
•
lquery
represents a regular-expression-like pattern for matching
ltree
values. A simple word
matches that label within a path. A star symbol (
*
)matches zero or more labels. For example:
foo
Match the exact label path
foo
*
.foo.
*
Match any label path containing the label
foo
*
.foo
Match any label path whose last label is
foo
Star symbols can alsobe quantified torestrict how many labels theycan match:
*
{
n
}
Match exactly
n
labels
*
{
n
,}
Match at least
n
labels
*
{
n
,
m
}
Match at least
n
but not more than
m
labels
*
{,
m
}
Match at most
m
labels -- same as
*
{0,
m
}
Thereare severalmodifiers thatcanbe put atthe endof a non-star label in
lquery
to makeit match
more thanjust the exact match:
@
Match case-insensitively, for example
a@
matches
A
*
Match any label with this prefix, for example
foo
*
matches
foobar
%
Match initial underscore-separated words
2811
158
Appendix F. Additional Supplied Modules
The behavior of
%
is a bit complicated. It tries to match words rather than the entire label. For ex-
ample
foo_bar%
matches
foo_bar_baz
but not
foo_barbaz
.If combined with
*
,prefix match-
ing applies to each word separately, for example
foo_bar%
*
matches
foo1_bar2_baz
but not
foo1_br2_baz
.
Also, you can write several possibly-modified labels separated with
|
(OR) to match any of those
labels, and you can put
!
(NOT) at the start to match any label that doesn’t match any of the
alternatives.
Here’s an annotated example of
lquery
:
Top.
*
{0,2}.sport
*
@.!football|tennis.Russ
*
|Spain
a.
b.
c.
d.
e.
This query will match any label path that:
a. begins with the label
Top
b. and next has zero to two labels before
c. a label beginning with the case-insensitive prefix
sport
d. then a label not matching
football
nor
tennis
e. and then ends with a label beginning with
Russ
or exactly matching
Spain
.
•
ltxtquery
represents a full-text-search-like pattern for matching
ltree
values. An
ltxtquery
value contains words, possibly with the modifiers
@
,
*
,
%
at the end; the modifiers have the same
meanings as in
lquery
.Words canbe combinedwith
&
(AND),
|
(OR),
!
(NOT), and parentheses.
The key difference from
lquery
is that
ltxtquery
matches words withoutregard totheir position
in the label path.
Here’s an example
ltxtquery
:
Europe & Russia
*
@ & !Transportation
This will match paths that contain the label
Europe
and any label beginning with
Russia
(case-
insensitive), but not paths containing the label
Transportation
. The location of these words
within the path is not important. Also, when
%
is used, the wordcan be matched toanyunderscore-
separated word within a label, regardless of position.
Note:
ltxtquery
allows whitespace between symbols, but
ltree
and
lquery
do not.
F.21.2. Operators and Functions
Type
ltree
has the usual comparison operators
=
,
<>
,
<
,
>
,
<=
,
>=
.Comparison sorts inthe order
of a treetraversal, withthechildrenof a nodesortedbylabeltext. In addition, thespecialized operators
shown in Table F-12 are available.
Table F-12.
ltree
Operators
Operator
Returns
Description
ltree @> ltree
boolean
is left argument an ancestor of
right (or equal)?
ltree <@ ltree
boolean
is left argument a descendant of
right (or equal)?
ltree ~ lquery
boolean
does
ltree
match
lquery
?
lquery ~ ltree
boolean
does
ltree
match
lquery
?
2812
156
Appendix F. Additional Supplied Modules
Operator
Returns
Description
ltree ? lquery[]
boolean
does
ltree
match any
lquery
in array?
lquery[] ? ltree
boolean
does
ltree
match any
lquery
in array?
ltree @ ltxtquery
boolean
does
ltree
match
ltxtquery
?
ltxtquery @ ltree
boolean
does
ltree
match
ltxtquery
?
ltree || ltree
ltree
concatenate
ltree
paths
ltree || text
ltree
convert text to
ltree
and
concatenate
text || ltree
ltree
convert text to
ltree
and
concatenate
ltree[] @> ltree
boolean
does array contain an ancestor
of
ltree
?
ltree <@ ltree[]
boolean
does array contain an ancestor
of
ltree
?
ltree[] <@ ltree
boolean
does array contain a descendant
of
ltree
?
ltree @> ltree[]
boolean
does array contain a descendant
of
ltree
?
ltree[] ~ lquery
boolean
does array contain any path
matching
lquery
?
lquery ~ ltree[]
boolean
does array contain any path
matching
lquery
?
ltree[] ? lquery[]
boolean
does
ltree
array contain any
path matching any
lquery
?
lquery[] ? ltree[]
boolean
does
ltree
array contain any
path matching any
lquery
?
ltree[] @ ltxtquery
boolean
does array contain any path
matching
ltxtquery
?
ltxtquery @ ltree[]
boolean
does array contain any path
matching
ltxtquery
?
ltree[] ?@> ltree
ltree
first array entry that is an
ancestor of
ltree
;NULL if
none
ltree[] ?<@ ltree
ltree
first array entry that is a
descendant of
ltree
;NULL if
none
ltree[] ?~ lquery
ltree
first array entry that matches
lquery
;NULL if none
ltree[] ?@ ltxtquery
ltree
first array entry that matches
ltxtquery
;NULL if none
The operators
<@
,
@>
,
@
and
~
have analogues
^<@
,
^@>
,
^@
,
^~
,which are the same except they do
not use indexes. These are useful only for testing purposes.
2813
117
Appendix F. Additional Supplied Modules
The available functions are shown in Table F-13.
Table F-13.
ltree
Functions
Function
Return Type
Description
Example
Result
subltree(ltree,
int start,
int end)
ltree
subpath of
ltree
from position
start
to position
end
-1 (counting
from 0)
subltree(’Top.Child1.Child2’,1,2)
Child1
subpath(ltree,
int offset,
int len)
ltree
subpath of
ltree
starting at position
offset
,length
len
.If
offset
is
negative, subpath
starts that far from
the end of the
path. If
len
is
negative, leaves
that many labels
off the end of the
path.
subpath(’Top.Child1.Child2’,0,2)
Top.Child1
subpath(ltree,
int offset)
ltree
subpath of
ltree
starting at position
offset
,
extending to end
of path. If
offset
is negative,
subpath starts that
far from the end
of the path.
subpath(’Top.Child1.Child2’,1)
Child1.Child2
nlevel(ltree)
integer
number of labels
in path
nlevel(’Top.Child1.Child2’)
3
index(ltree
a, ltree b)
integer
position of first
occurrence of
b
in
a
;-1 if not found
index(’0.1.2.3.5.4.5.6.8.5.6.8’,’5.6’)
6
index(ltree
a, ltree b,
int offset)
integer
position of first
occurrence of
b
in
a
,searching
starting at
offset
;negative
offset
means
start
-offset
labels from the
end of the path
index(’0.1.2.3.5.4.5.6.8.5.6.8’,’5.6’,-4)
9
text2ltree(text)
ltree
cast
text
to
ltree
ltree2text(ltree)
text
cast
ltree
to
text
2814
107
Appendix F. Additional Supplied Modules
Function
Return Type
Description
Example
Result
lca(ltree,
ltree, ...)
ltree
lowest common
ancestor, i.e.,
longest common
prefix of paths (up
to 8 arguments
supported)
lca(’1.2.2.3’,’1.2.3.4.5.6’)
1.2
lca(ltree[])
ltree
lowest common
ancestor, i.e.,
longest common
prefix of paths
lca(array[’1.2.2.3’::ltree,’1.2.3’])
1.2
F.21.3. Indexes
ltree
supports several types of indexes that can speed up the indicated operators:
•
B-tree index over
ltree
:
<
,
<=
,
=
,
>=
,
>
•
GiST index over
ltree
:
<
,
<=
,
=
,
>=
,
>
,
@>
,
<@
,
@
,
~
,
?
Example of creating such an index:
CREATE INDEX path_gist_idx ON test USING GIST (path);
•
GiST index over
ltree[]
:
ltree[] <@ ltree
,
ltree @> ltree[]
,
@
,
~
,
?
Example of creating such an index:
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
Note: This index type is lossy.
F.21.4. Example
This exampleuses thefollowingdata(alsoavailable infile
contrib/ltree/ltreetest.sql
in the
source distribution):
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);
Now, we have a table
test
populated with data describing the hierarchy shown below:
2815
74
Appendix F. Additional Supplied Modules
Top
/
|
\
Science Hobbies Collections
/
|
\
Astronomy
Amateurs_Astronomy Pictures
/
\
|
Astrophysics
Cosmology
Astronomy
/
|
\
Galaxies Stars Astronauts
We can do 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)
Here are some examples of path 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)
Here are some examples of 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@’;
2816
43
Appendix F. Additional Supplied Modules
path
------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
(3 rows)
Path construction 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 simplify this by creating a SQL function that inserts a label at a specified position in a path:
CREATE FUNCTION ins_label(ltree, int, text) RETURNS ltree
AS ’select subpath($1,0,$2) || $3 || subpath($1,$2);’
LANGUAGE SQL IMMUTABLE;
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)
F.21.5. Authors
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.
F.22. pageinspect
The
pageinspect
module provides functions thatallowyoutoinspectthe contents of database pages
at a low level, which is useful for debugging purposes. All of these functions may be used only by
superusers.
2817
Documents you may be interested
Documents you may be interested