mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-03 01:56:54 +02:00
8bbd0cce92
Unaligned siglen could lead to an unaligned access to subsequent key fields. Backpatch to 13, where opclass options were introduced. Reported-by: Alexander Lakhin Bug: 17847 Discussion: https://postgr.es/m/17847-171232970bea406b%40postgresql.org Reviewed-by: Tom Lane, Pavel Borisov, Alexander Lakhin Backpatch-through: 13
860 lines
30 KiB
Plaintext
860 lines
30 KiB
Plaintext
<!-- doc/src/sgml/ltree.sgml -->
|
|
|
|
<sect1 id="ltree" xreflabel="ltree">
|
|
<title>ltree — hierarchical tree-like data type</title>
|
|
|
|
<indexterm zone="ltree">
|
|
<primary>ltree</primary>
|
|
</indexterm>
|
|
|
|
<para>
|
|
This module implements a data type <type>ltree</type> for representing
|
|
labels of data stored in a hierarchical tree-like structure.
|
|
Extensive facilities for searching through label trees are provided.
|
|
</para>
|
|
|
|
<para>
|
|
This module is considered <quote>trusted</quote>, that is, it can be
|
|
installed by non-superusers who have <literal>CREATE</literal> privilege
|
|
on the current database.
|
|
</para>
|
|
|
|
<sect2 id="ltree-definitions">
|
|
<title>Definitions</title>
|
|
|
|
<para>
|
|
A <firstterm>label</firstterm> is a sequence of alphanumeric characters,
|
|
underscores, and hyphens. Valid alphanumeric character ranges are
|
|
dependent on the database locale. For example, in C locale, the characters
|
|
<literal>A-Za-z0-9_-</literal> are allowed.
|
|
Labels must be no more than 1000 characters long.
|
|
</para>
|
|
|
|
<para>
|
|
Examples: <literal>42</literal>, <literal>Personal_Services</literal>
|
|
</para>
|
|
|
|
<para>
|
|
A <firstterm>label path</firstterm> is a sequence of zero or more
|
|
labels separated by dots, for example <literal>L1.L2.L3</literal>, representing
|
|
a path from the root of a hierarchical tree to a particular node. The
|
|
length of a label path cannot exceed 65535 labels.
|
|
</para>
|
|
|
|
<para>
|
|
Example: <literal>Top.Countries.Europe.Russia</literal>
|
|
</para>
|
|
|
|
<para>
|
|
The <filename>ltree</filename> module provides several data types:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
<type>ltree</type> stores a label path.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
<type>lquery</type> represents a regular-expression-like pattern
|
|
for matching <type>ltree</type> values. A simple word matches that
|
|
label within a path. A star symbol (<literal>*</literal>) matches zero
|
|
or more labels. These can be joined with dots to form a pattern that
|
|
must match the whole label path. For example:
|
|
<synopsis>
|
|
foo <lineannotation>Match the exact label path <literal>foo</literal></lineannotation>
|
|
*.foo.* <lineannotation>Match any label path containing the label <literal>foo</literal></lineannotation>
|
|
*.foo <lineannotation>Match any label path whose last label is <literal>foo</literal></lineannotation>
|
|
</synopsis>
|
|
</para>
|
|
|
|
<para>
|
|
Both star symbols and simple words can be quantified to restrict how many
|
|
labels they can match:
|
|
<synopsis>
|
|
*{<replaceable>n</replaceable>} <lineannotation>Match exactly <replaceable>n</replaceable> labels</lineannotation>
|
|
*{<replaceable>n</replaceable>,} <lineannotation>Match at least <replaceable>n</replaceable> labels</lineannotation>
|
|
*{<replaceable>n</replaceable>,<replaceable>m</replaceable>} <lineannotation>Match at least <replaceable>n</replaceable> but not more than <replaceable>m</replaceable> labels</lineannotation>
|
|
*{,<replaceable>m</replaceable>} <lineannotation>Match at most <replaceable>m</replaceable> labels — same as </lineannotation>*{0,<replaceable>m</replaceable>}
|
|
foo{<replaceable>n</replaceable>,<replaceable>m</replaceable>} <lineannotation>Match at least <replaceable>n</replaceable> but not more than <replaceable>m</replaceable> occurrences of <literal>foo</literal></lineannotation>
|
|
foo{,} <lineannotation>Match any number of occurrences of <literal>foo</literal>, including zero</lineannotation>
|
|
</synopsis>
|
|
In the absence of any explicit quantifier, the default for a star symbol
|
|
is to match any number of labels (that is, <literal>{,}</literal>) while
|
|
the default for a non-star item is to match exactly once (that
|
|
is, <literal>{1}</literal>).
|
|
</para>
|
|
|
|
<para>
|
|
There are several modifiers that can be put at the end of a non-star
|
|
<type>lquery</type> item to make it match more than just the exact match:
|
|
<synopsis>
|
|
@ <lineannotation>Match case-insensitively, for example <literal>a@</literal> matches <literal>A</literal></lineannotation>
|
|
* <lineannotation>Match any label with this prefix, for example <literal>foo*</literal> matches <literal>foobar</literal></lineannotation>
|
|
% <lineannotation>Match initial underscore-separated words</lineannotation>
|
|
</synopsis>
|
|
The behavior of <literal>%</literal> is a bit complicated. It tries to match
|
|
words rather than the entire label. For example
|
|
<literal>foo_bar%</literal> matches <literal>foo_bar_baz</literal> but not
|
|
<literal>foo_barbaz</literal>. If combined with <literal>*</literal>, prefix
|
|
matching applies to each word separately, for example
|
|
<literal>foo_bar%*</literal> matches <literal>foo1_bar2_baz</literal> but
|
|
not <literal>foo1_br2_baz</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Also, you can write several possibly-modified non-star items separated with
|
|
<literal>|</literal> (OR) to match any of those items, and you can put
|
|
<literal>!</literal> (NOT) at the start of a non-star group to match any
|
|
label that doesn't match any of the alternatives. A quantifier, if any,
|
|
goes at the end of the group; it means some number of matches for the
|
|
group as a whole (that is, some number of labels matching or not matching
|
|
any of the alternatives).
|
|
</para>
|
|
|
|
<para>
|
|
Here's an annotated example of <type>lquery</type>:
|
|
<programlisting>
|
|
Top.*{0,2}.sport*@.!football|tennis{1,}.Russ*|Spain
|
|
a. b. c. d. e.
|
|
</programlisting>
|
|
This query will match any label path that:
|
|
</para>
|
|
<orderedlist numeration="loweralpha">
|
|
<listitem>
|
|
<para>
|
|
begins with the label <literal>Top</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
and next has zero to two labels before
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
a label beginning with the case-insensitive prefix <literal>sport</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
then has one or more labels, none of which
|
|
match <literal>football</literal> nor <literal>tennis</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
and then ends with a label beginning with <literal>Russ</literal> or
|
|
exactly matching <literal>Spain</literal>.
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><type>ltxtquery</type> represents a full-text-search-like
|
|
pattern for matching <type>ltree</type> values. An
|
|
<type>ltxtquery</type> value contains words, possibly with the
|
|
modifiers <literal>@</literal>, <literal>*</literal>, <literal>%</literal> at the end;
|
|
the modifiers have the same meanings as in <type>lquery</type>.
|
|
Words can be combined with <literal>&</literal> (AND),
|
|
<literal>|</literal> (OR), <literal>!</literal> (NOT), and parentheses.
|
|
The key difference from
|
|
<type>lquery</type> is that <type>ltxtquery</type> matches words without
|
|
regard to their position in the label path.
|
|
</para>
|
|
|
|
<para>
|
|
Here's an example <type>ltxtquery</type>:
|
|
<programlisting>
|
|
Europe & Russia*@ & !Transportation
|
|
</programlisting>
|
|
This will match paths that contain the label <literal>Europe</literal> and
|
|
any label beginning with <literal>Russia</literal> (case-insensitive),
|
|
but not paths containing the label <literal>Transportation</literal>.
|
|
The location of these words within the path is not important.
|
|
Also, when <literal>%</literal> is used, the word can be matched to any
|
|
underscore-separated word within a label, regardless of position.
|
|
</para>
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
Note: <type>ltxtquery</type> allows whitespace between symbols, but
|
|
<type>ltree</type> and <type>lquery</type> do not.
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 id="ltree-ops-funcs">
|
|
<title>Operators and Functions</title>
|
|
|
|
<para>
|
|
Type <type>ltree</type> has the usual comparison operators
|
|
<literal>=</literal>, <literal><></literal>,
|
|
<literal><</literal>, <literal>></literal>, <literal><=</literal>, <literal>>=</literal>.
|
|
Comparison sorts in the order of a tree traversal, with the children
|
|
of a node sorted by label text. In addition, the specialized
|
|
operators shown in <xref linkend="ltree-op-table"/> are available.
|
|
</para>
|
|
|
|
<table id="ltree-op-table">
|
|
<title><type>ltree</type> Operators</title>
|
|
<tgroup cols="1">
|
|
<thead>
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
Operator
|
|
</para>
|
|
<para>
|
|
Description
|
|
</para></entry>
|
|
</row>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal>@></literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Is left argument an ancestor of right (or equal)?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal><@</literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Is left argument a descendant of right (or equal)?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal>~</literal> <type>lquery</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>lquery</type> <literal>~</literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does <type>ltree</type> match <type>lquery</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal>?</literal> <type>lquery[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>lquery[]</type> <literal>?</literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does <type>ltree</type> match any <type>lquery</type> in array?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal>@</literal> <type>ltxtquery</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>ltxtquery</type> <literal>@</literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does <type>ltree</type> match <type>ltxtquery</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal>||</literal> <type>ltree</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Concatenates <type>ltree</type> paths.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree</type> <literal>||</literal> <type>text</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>text</type> <literal>||</literal> <type>ltree</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Converts text to <type>ltree</type> and concatenates.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>@></literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>ltree</type> <literal><@</literal> <type>ltree[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does array contain an ancestor of <type>ltree</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal><@</literal> <type>ltree</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>ltree</type> <literal>@></literal> <type>ltree[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does array contain a descendant of <type>ltree</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>~</literal> <type>lquery</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>lquery</type> <literal>~</literal> <type>ltree[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does array contain any path matching <type>lquery</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>?</literal> <type>lquery[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>lquery[]</type> <literal>?</literal> <type>ltree[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does <type>ltree</type> array contain any path matching
|
|
any <type>lquery</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>@</literal> <type>ltxtquery</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para role="func_signature">
|
|
<type>ltxtquery</type> <literal>@</literal> <type>ltree[]</type>
|
|
<returnvalue>boolean</returnvalue>
|
|
</para>
|
|
<para>
|
|
Does array contain any path matching <type>ltxtquery</type>?
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>?@></literal> <type>ltree</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns first array entry that is an ancestor of <type>ltree</type>,
|
|
or <literal>NULL</literal> if none.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>?<@</literal> <type>ltree</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns first array entry that is a descendant of <type>ltree</type>,
|
|
or <literal>NULL</literal> if none.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>?~</literal> <type>lquery</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns first array entry that matches <type>lquery</type>,
|
|
or <literal>NULL</literal> if none.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<type>ltree[]</type> <literal>?@</literal> <type>ltxtquery</type>
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns first array entry that matches <type>ltxtquery</type>,
|
|
or <literal>NULL</literal> if none.
|
|
</para></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
|
|
<para>
|
|
The operators <literal><@</literal>, <literal>@></literal>,
|
|
<literal>@</literal> and <literal>~</literal> have analogues
|
|
<literal>^<@</literal>, <literal>^@></literal>, <literal>^@</literal>,
|
|
<literal>^~</literal>, which are the same except they do not use
|
|
indexes. These are useful only for testing purposes.
|
|
</para>
|
|
|
|
<para>
|
|
The available functions are shown in <xref linkend="ltree-func-table"/>.
|
|
</para>
|
|
|
|
<table id="ltree-func-table">
|
|
<title><type>ltree</type> Functions</title>
|
|
<tgroup cols="1">
|
|
<thead>
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
Function
|
|
</para>
|
|
<para>
|
|
Description
|
|
</para>
|
|
<para>
|
|
Example(s)
|
|
</para></entry>
|
|
</row>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>subltree</primary></indexterm>
|
|
<function>subltree</function> ( <type>ltree</type>, <parameter>start</parameter> <type>integer</type>, <parameter>end</parameter> <type>integer</type> )
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns subpath of <type>ltree</type> from
|
|
position <parameter>start</parameter> to
|
|
position <parameter>end</parameter>-1 (counting from 0).
|
|
</para>
|
|
<para>
|
|
<literal>subltree('Top.Child1.Child2', 1, 2)</literal>
|
|
<returnvalue>Child1</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>subpath</primary></indexterm>
|
|
<function>subpath</function> ( <type>ltree</type>, <parameter>offset</parameter> <type>integer</type>, <parameter>len</parameter> <type>integer</type> )
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns subpath of <type>ltree</type> starting at
|
|
position <parameter>offset</parameter>, with
|
|
length <parameter>len</parameter>. If <parameter>offset</parameter>
|
|
is negative, subpath starts that far from the end of the path.
|
|
If <parameter>len</parameter> is negative, leaves that many labels off
|
|
the end of the path.
|
|
</para>
|
|
<para>
|
|
<literal>subpath('Top.Child1.Child2', 0, 2)</literal>
|
|
<returnvalue>Top.Child1</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<function>subpath</function> ( <type>ltree</type>, <parameter>offset</parameter> <type>integer</type> )
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns subpath of <type>ltree</type> starting at
|
|
position <parameter>offset</parameter>, extending to end of path.
|
|
If <parameter>offset</parameter> is negative, subpath starts that far
|
|
from the end of the path.
|
|
</para>
|
|
<para>
|
|
<literal>subpath('Top.Child1.Child2', 1)</literal>
|
|
<returnvalue>Child1.Child2</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>nlevel</primary></indexterm>
|
|
<function>nlevel</function> ( <type>ltree</type> )
|
|
<returnvalue>integer</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns number of labels in path.
|
|
</para>
|
|
<para>
|
|
<literal>nlevel('Top.Child1.Child2')</literal>
|
|
<returnvalue>3</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>index</primary></indexterm>
|
|
<function>index</function> ( <parameter>a</parameter> <type>ltree</type>, <parameter>b</parameter> <type>ltree</type> )
|
|
<returnvalue>integer</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns position of first occurrence of <parameter>b</parameter> in
|
|
<parameter>a</parameter>, or -1 if not found.
|
|
</para>
|
|
<para>
|
|
<literal>index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6')</literal>
|
|
<returnvalue>6</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<function>index</function> ( <parameter>a</parameter> <type>ltree</type>, <parameter>b</parameter> <type>ltree</type>, <parameter>offset</parameter> <type>integer</type> )
|
|
<returnvalue>integer</returnvalue>
|
|
</para>
|
|
<para>
|
|
Returns position of first occurrence of <parameter>b</parameter>
|
|
in <parameter>a</parameter>, or -1 if not found. The search starts at
|
|
position <parameter>offset</parameter>;
|
|
negative <parameter>offset</parameter> means
|
|
start <parameter>-offset</parameter> labels from the end of the path.
|
|
</para>
|
|
<para>
|
|
<literal>index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6', -4)</literal>
|
|
<returnvalue>9</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>text2ltree</primary></indexterm>
|
|
<function>text2ltree</function> ( <type>text</type> )
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Casts <type>text</type> to <type>ltree</type>.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>ltree2text</primary></indexterm>
|
|
<function>ltree2text</function> ( <type>ltree</type> )
|
|
<returnvalue>text</returnvalue>
|
|
</para>
|
|
<para>
|
|
Casts <type>ltree</type> to <type>text</type>.
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<indexterm><primary>lca</primary></indexterm>
|
|
<function>lca</function> ( <type>ltree</type> <optional>, <type>ltree</type> <optional>, ... </optional></optional> )
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Computes longest common ancestor of paths
|
|
(up to 8 arguments are supported).
|
|
</para>
|
|
<para>
|
|
<literal>lca('1.2.3', '1.2.3.4.5.6')</literal>
|
|
<returnvalue>1.2</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry role="func_table_entry"><para role="func_signature">
|
|
<function>lca</function> ( <type>ltree[]</type> )
|
|
<returnvalue>ltree</returnvalue>
|
|
</para>
|
|
<para>
|
|
Computes longest common ancestor of paths in array.
|
|
</para>
|
|
<para>
|
|
<literal>lca(array['1.2.3'::ltree,'1.2.3.4'])</literal>
|
|
<returnvalue>1.2</returnvalue>
|
|
</para></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</sect2>
|
|
|
|
<sect2 id="ltree-indexes">
|
|
<title>Indexes</title>
|
|
<para>
|
|
<filename>ltree</filename> supports several types of indexes that can speed
|
|
up the indicated operators:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
B-tree index over <type>ltree</type>:
|
|
<literal><</literal>, <literal><=</literal>, <literal>=</literal>,
|
|
<literal>>=</literal>, <literal>></literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal>
|
|
opclass):
|
|
<literal><</literal>, <literal><=</literal>, <literal>=</literal>,
|
|
<literal>>=</literal>, <literal>></literal>,
|
|
<literal>@></literal>, <literal><@</literal>,
|
|
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
|
|
</para>
|
|
<para>
|
|
<literal>gist_ltree_ops</literal> GiST opclass approximates a set of
|
|
path labels as a bitmap signature. Its optional integer parameter
|
|
<literal>siglen</literal> determines the
|
|
signature length in bytes. The default signature length is 8 bytes.
|
|
The length must be a positive multiple of <type>int</type> alignment
|
|
(4 bytes on most machines)) up to 2024. Longer
|
|
signatures lead to a more precise search (scanning a smaller fraction of the index and
|
|
fewer heap pages), at the cost of a larger index.
|
|
</para>
|
|
<para>
|
|
Example of creating such an index with the default signature length of 8 bytes:
|
|
</para>
|
|
<programlisting>
|
|
CREATE INDEX path_gist_idx ON test USING GIST (path);
|
|
</programlisting>
|
|
<para>
|
|
Example of creating such an index with a signature length of 100 bytes:
|
|
</para>
|
|
<programlisting>
|
|
CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
|
|
</programlisting>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
GiST index over <type>ltree[]</type> (<literal>gist__ltree_ops</literal>
|
|
opclass):
|
|
<literal>ltree[] <@ ltree</literal>, <literal>ltree @> ltree[]</literal>,
|
|
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
|
|
</para>
|
|
<para>
|
|
<literal>gist__ltree_ops</literal> GiST opclass works similarly to
|
|
<literal>gist_ltree_ops</literal> and also takes signature length as
|
|
a parameter. The default value of <literal>siglen</literal> in
|
|
<literal>gist__ltree_ops</literal> is 28 bytes.
|
|
</para>
|
|
<para>
|
|
Example of creating such an index with the default signature length of 28 bytes:
|
|
</para>
|
|
<programlisting>
|
|
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
|
|
</programlisting>
|
|
<para>
|
|
Example of creating such an index with a signature length of 100 bytes:
|
|
</para>
|
|
<programlisting>
|
|
CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
|
|
</programlisting>
|
|
<para>
|
|
Note: This index type is lossy.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</sect2>
|
|
|
|
<sect2 id="ltree-example">
|
|
<title>Example</title>
|
|
|
|
<para>
|
|
This example uses the following data (also available in file
|
|
<filename>contrib/ltree/ltreetest.sql</filename> in the source distribution):
|
|
</para>
|
|
|
|
<programlisting>
|
|
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);
|
|
</programlisting>
|
|
|
|
<para>
|
|
Now, we have a table <structname>test</structname> populated with data describing
|
|
the hierarchy shown below:
|
|
</para>
|
|
|
|
<literallayout class="monospaced">
|
|
Top
|
|
/ | \
|
|
Science Hobbies Collections
|
|
/ | \
|
|
Astronomy Amateurs_Astronomy Pictures
|
|
/ \ |
|
|
Astrophysics Cosmology Astronomy
|
|
/ | \
|
|
Galaxies Stars Astronauts
|
|
</literallayout>
|
|
|
|
<para>
|
|
We can do inheritance:
|
|
<screen>
|
|
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)
|
|
</screen>
|
|
</para>
|
|
|
|
<para>
|
|
Here are some examples of path matching:
|
|
<screen>
|
|
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)
|
|
</screen>
|
|
</para>
|
|
|
|
<para>
|
|
Here are some examples of full text search:
|
|
<screen>
|
|
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)
|
|
</screen>
|
|
</para>
|
|
|
|
<para>
|
|
Path construction using functions:
|
|
<screen>
|
|
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)
|
|
</screen>
|
|
</para>
|
|
|
|
<para>
|
|
We could simplify this by creating an SQL function that inserts a label
|
|
at a specified position in a path:
|
|
<screen>
|
|
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)
|
|
</screen>
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 id="ltree-transforms">
|
|
<title>Transforms</title>
|
|
|
|
<para>
|
|
The <literal>ltree_plpython3u</literal> extension implements transforms for
|
|
the <type>ltree</type> type for PL/Python. If installed and specified when
|
|
creating a function, <type>ltree</type> values are mapped to Python lists.
|
|
(The reverse is currently not supported, however.)
|
|
</para>
|
|
|
|
<caution>
|
|
<para>
|
|
It is strongly recommended that the transform extension be installed in
|
|
the same schema as <filename>ltree</filename>. Otherwise there are
|
|
installation-time security hazards if a transform extension's schema
|
|
contains objects defined by a hostile user.
|
|
</para>
|
|
</caution>
|
|
</sect2>
|
|
|
|
<sect2 id="ltree-authors">
|
|
<title>Authors</title>
|
|
|
|
<para>
|
|
All work was done by Teodor Sigaev (<email>teodor@stack.net</email>) and
|
|
Oleg Bartunov (<email>oleg@sai.msu.su</email>). See
|
|
<ulink url="http://www.sai.msu.su/~megera/postgres/gist/"></ulink> for
|
|
additional information. Authors would like to thank Eugeny Rodichev for
|
|
helpful discussions. Comments and bug reports are welcome.
|
|
</para>
|
|
</sect2>
|
|
|
|
</sect1>
|