postgresql/doc/src/sgml/ltree.sgml

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

868 lines
30 KiB
Plaintext
Raw Normal View History

2010-09-20 22:08:53 +02:00
<!-- doc/src/sgml/ltree.sgml -->
<sect1 id="ltree" xreflabel="ltree">
<title>ltree &mdash; 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
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
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>
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
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>
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
*{,<replaceable>m</replaceable>} <lineannotation>Match at most <replaceable>m</replaceable> labels &mdash; 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>
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
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
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
<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>
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
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>
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
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>
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
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>&amp;</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 &amp; Russia*@ &amp; !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>&lt;&gt;</literal>,
<literal>&lt;</literal>, <literal>&gt;</literal>, <literal>&lt;=</literal>, <literal>&gt;=</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>@&gt;</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>&lt;@</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>@&gt;</literal> <type>ltree</type>
<returnvalue>boolean</returnvalue>
</para>
<para role="func_signature">
<type>ltree</type> <literal>&lt;@</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>&lt;@</literal> <type>ltree</type>
<returnvalue>boolean</returnvalue>
</para>
<para role="func_signature">
<type>ltree</type> <literal>@&gt;</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>?@&gt;</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>?&lt;@</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>&lt;@</literal>, <literal>@&gt;</literal>,
<literal>@</literal> and <literal>~</literal> have analogues
<literal>^&lt;@</literal>, <literal>^@&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</literal>
</para>
</listitem>
<listitem>
<para>
Hash index over <type>ltree</type>:
<literal>=</literal>
</para>
</listitem>
<listitem>
<para>
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal>
opclass):
<literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</literal>,
<literal>@&gt;</literal>, <literal>&lt;@</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.
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
</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);
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
</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>
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
GiST index over <type>ltree[]</type> (<literal>gist__ltree_ops</literal>
opclass):
<literal>ltree[] &lt;@ ltree</literal>, <literal>ltree @&gt; ltree[]</literal>,
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
<literal>gist__ltree_ops</literal> GiST opclass works similarly to
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
<literal>gist_ltree_ops</literal> and also takes signature length as
a parameter. The default value of <literal>siglen</literal> in
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
<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);
Implement operator class parameters PostgreSQL provides set of template index access methods, where opclasses have much freedom in the semantics of indexing. These index AMs are GiST, GIN, SP-GiST and BRIN. There opclasses define representation of keys, operations on them and supported search strategies. So, it's natural that opclasses may be faced some tradeoffs, which require user-side decision. This commit implements opclass parameters allowing users to set some values, which tell opclass how to index the particular dataset. This commit doesn't introduce new storage in system catalog. Instead it uses pg_attribute.attoptions, which is used for table column storage options but unused for index attributes. In order to evade changing signature of each opclass support function, we implement unified way to pass options to opclass support functions. Options are set to fn_expr as the constant bytea expression. It's possible due to the fact that opclass support functions are executed outside of expressions, so fn_expr is unused for them. This commit comes with some examples of opclass options usage. We parametrize signature length in GiST. That applies to multiple opclasses: tsvector_ops, gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops and gist_hstore_ops. Also we parametrize maximum number of integer ranges for gist__int_ops. However, the main future usage of this feature is expected to be json, where users would be able to specify which way to index particular json parts. Catversion is bumped. Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru Author: Nikita Glukhov, revised by me Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
2020-03-30 18:17:11 +02:00
</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);
CREATE INDEX path_hash_idx ON test USING HASH (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=&gt; SELECT path FROM test WHERE path &lt;@ '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=&gt; 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)
Fix lquery's NOT handling, and add ability to quantify non-'*' items. The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
2020-03-31 17:14:30 +02:00
ltreetest=&gt; 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=&gt; SELECT path FROM test WHERE path @ 'Astro*% &amp; !pictures@';
path
------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
Top.Hobbies.Amateurs_Astronomy
(4 rows)
ltreetest=&gt; SELECT path FROM test WHERE path @ 'Astro* &amp; !pictures@';
path
------------------------------------
Top.Science.Astronomy
Top.Science.Astronomy.Astrophysics
Top.Science.Astronomy.Cosmology
(3 rows)
</screen>
</para>
<para>
Path construction using functions:
<screen>
ltreetest=&gt; SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE path &lt;@ '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=&gt; SELECT ins_label(path,2,'Space') FROM test WHERE path &lt;@ '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>
Make contrib modules' installation scripts more secure. Hostile objects located within the installation-time search_path could capture references in an extension's installation or upgrade script. If the extension is being installed with superuser privileges, this opens the door to privilege escalation. While such hazards have existed all along, their urgency increases with the v13 "trusted extensions" feature, because that lets a non-superuser control the installation path for a superuser-privileged script. Therefore, make a number of changes to make such situations more secure: * Tweak the construction of the installation-time search_path to ensure that references to objects in pg_catalog can't be subverted; and explicitly add pg_temp to the end of the path to prevent attacks using temporary objects. * Disable check_function_bodies within installation/upgrade scripts, so that any security gaps in SQL-language or PL-language function bodies cannot create a risk of unwanted installation-time code execution. * Adjust lookup of type input/receive functions and join estimator functions to complain if there are multiple candidate functions. This prevents capture of references to functions whose signature is not the first one checked; and it's arguably more user-friendly anyway. * Modify various contrib upgrade scripts to ensure that catalog modification queries are executed with secure search paths. (These are in-place modifications with no extension version changes, since it is the update process itself that is at issue, not the end result.) Extensions that depend on other extensions cannot be made fully secure by these methods alone; therefore, revert the "trusted" marking that commit eb67623c9 applied to earthdistance and hstore_plperl, pending some better solution to that set of issues. Also add documentation around these issues, to help extension authors write secure installation scripts. Patch by me, following an observation by Andres Freund; thanks to Noah Misch for review. Security: CVE-2020-14350
2020-08-10 16:44:42 +02:00
<caution>
<para>
It is strongly recommended that the transform extension be installed in
Make contrib modules' installation scripts more secure. Hostile objects located within the installation-time search_path could capture references in an extension's installation or upgrade script. If the extension is being installed with superuser privileges, this opens the door to privilege escalation. While such hazards have existed all along, their urgency increases with the v13 "trusted extensions" feature, because that lets a non-superuser control the installation path for a superuser-privileged script. Therefore, make a number of changes to make such situations more secure: * Tweak the construction of the installation-time search_path to ensure that references to objects in pg_catalog can't be subverted; and explicitly add pg_temp to the end of the path to prevent attacks using temporary objects. * Disable check_function_bodies within installation/upgrade scripts, so that any security gaps in SQL-language or PL-language function bodies cannot create a risk of unwanted installation-time code execution. * Adjust lookup of type input/receive functions and join estimator functions to complain if there are multiple candidate functions. This prevents capture of references to functions whose signature is not the first one checked; and it's arguably more user-friendly anyway. * Modify various contrib upgrade scripts to ensure that catalog modification queries are executed with secure search paths. (These are in-place modifications with no extension version changes, since it is the update process itself that is at issue, not the end result.) Extensions that depend on other extensions cannot be made fully secure by these methods alone; therefore, revert the "trusted" marking that commit eb67623c9 applied to earthdistance and hstore_plperl, pending some better solution to that set of issues. Also add documentation around these issues, to help extension authors write secure installation scripts. Patch by me, following an observation by Andres Freund; thanks to Noah Misch for review. Security: CVE-2020-14350
2020-08-10 16:44:42 +02:00
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>