mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Significant update from Vince Vielhaber.
This commit is contained in:
parent
874957a32a
commit
d6efbf1956
@ -1,87 +1,88 @@
|
|||||||
<Chapter Id="xindex">
|
<chapter id="xindex">
|
||||||
<Title>Interfacing Extensions To Indices</Title>
|
<title>Interfacing Extensions To Indices</title>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The procedures described thus far let you define a new
|
The procedures described thus far let you define a new type, new
|
||||||
type, new functions and new operators. However, we
|
functions and new operators. However, we cannot yet define a secondary
|
||||||
cannot yet define a secondary index (such as a <Acronym>B-tree</Acronym>,
|
index (such as a <acronym>B-tree</acronym>, <acronym>R-tree</acronym> or
|
||||||
<Acronym>R-tree</Acronym> or hash access method) over a new type or its
|
hash access method) over a new type or its operators.
|
||||||
operators.
|
</para>
|
||||||
</Para>
|
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Look back at
|
Look back at
|
||||||
<XRef LinkEnd="EXTEND-CATALOGS" EndTerm="EXTEND-CATALOGS">.
|
<xref endterm="EXTEND-CATALOGS" linkend="EXTEND-CATALOGS">.
|
||||||
The right half shows the catalogs
|
The right half shows the catalogs that we must modify in order to tell
|
||||||
that we must modify in order to tell <ProductName>Postgres</ProductName> how
|
<productname>Postgres</productname> how to use a user-defined type and/or
|
||||||
to use a user-defined type and/or user-defined operators
|
user-defined operators with an index (i.e., <filename>pg_am, pg_amop,
|
||||||
with an index (i.e., <FileName>pg_am, pg_amop, pg_amproc</FileName> and
|
pg_amproc, pg_operator</filename> and <filename>pg_opclass</filename>).
|
||||||
<FileName>pg_opclass</FileName>). Unfortunately, there is no simple command
|
Unfortunately, there is no simple command to do this. We will demonstrate
|
||||||
to do this. We will demonstrate how to modify these
|
how to modify these catalogs through a running example: a new operator
|
||||||
catalogs through a running example: a new operator
|
class for the <acronym>B-tree</acronym> access method that stores and
|
||||||
class for the <Acronym>B-tree</Acronym> access method that sorts integers
|
sorts complex numbers in ascending absolute value order.
|
||||||
in ascending absolute value order.
|
</para>
|
||||||
</Para>
|
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The <FileName>pg_am</FileName> class contains one instance for every user
|
The <filename>pg_am</filename> class contains one instance for every user
|
||||||
defined access method. Support for the heap access
|
defined access method. Support for the heap access method is built into
|
||||||
method is built into <ProductName>Postgres</ProductName>, but every other access
|
<productname>Postgres</productname>, but every other access method is
|
||||||
method is described here. The schema is
|
described here. The schema is
|
||||||
|
|
||||||
<TABLE TOCENTRY="1">
|
<table tocentry="1">
|
||||||
<Title>Index Schema</Title>
|
<title>Index Schema</title>
|
||||||
<TitleAbbrev>Indices</TitleAbbrev>
|
<titleabbrev>Indices</titleabbrev>
|
||||||
<TGroup Cols="2">
|
<tgroup cols="2">
|
||||||
<THead>
|
<thead>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>Attribute</Entry>
|
<entry>Attribute</entry>
|
||||||
<Entry>Description</Entry>
|
<entry>Description</entry>
|
||||||
</Row>
|
</row>
|
||||||
</THead>
|
</thead>
|
||||||
<TBody>
|
<tbody>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amname</Entry>
|
<entry>amname</entry>
|
||||||
<Entry>name of the access method</Entry>
|
<entry>name of the access method</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amowner</Entry>
|
<entry>amowner</entry>
|
||||||
<Entry>object id of the owner's instance in pg_user</Entry>
|
<entry>object id of the owner's instance in pg_user</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amkind</Entry>
|
<entry>amkind</entry>
|
||||||
<Entry>not used at present, but set to 'o' as a place holder</Entry>
|
<entry>not used at present, but set to 'o' as a place holder</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amstrategies</Entry>
|
<entry>amstrategies</entry>
|
||||||
<Entry>number of strategies for this access method (see below)</Entry>
|
<entry>number of strategies for this access method (see below)</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amsupport</Entry>
|
<entry>amsupport</entry>
|
||||||
<Entry>number of support routines for this access method (see below)</Entry>
|
<entry>number of support routines for this access method (see below)</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amgettuple
|
<entry>amgettuple</entry>
|
||||||
aminsert
|
</row>
|
||||||
...</Entry>
|
<row>
|
||||||
|
<entry>aminsert</entry>
|
||||||
<Entry>procedure identifiers for interface routines to the access
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>...</entry>
|
||||||
|
<entry>procedure identifiers for interface routines to the access
|
||||||
method. For example, regproc ids for opening, closing, and
|
method. For example, regproc ids for opening, closing, and
|
||||||
getting instances from the access method appear here. </Entry>
|
getting instances from the access method appear here.</entry>
|
||||||
</Row>
|
</row>
|
||||||
</TBody>
|
</tbody>
|
||||||
</TGroup>
|
</tgroup>
|
||||||
</TABLE>
|
</table>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The <Acronym>object ID</Acronym> of the instance in <FileName>pg_am</FileName> is used as a
|
The <acronym>object ID</acronym> of the instance in
|
||||||
foreign key in lots of other classes. You don't need
|
<filename>pg_am</filename> is used as a foreign key in lots of other
|
||||||
to add a new instance to this class; all you're interested in
|
classes. You don't need to add a new instance to this class; all
|
||||||
is the <Acronym>object ID</Acronym> of the access method instance
|
you're interested in is the <acronym>object ID</acronym> of the access
|
||||||
you want to extend:
|
method instance you want to extend:
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
SELECT oid FROM pg_am WHERE amname = 'btree';
|
SELECT oid FROM pg_am WHERE amname = 'btree';
|
||||||
|
|
||||||
+----+
|
+----+
|
||||||
@ -89,181 +90,187 @@ SELECT oid FROM pg_am WHERE amname = 'btree';
|
|||||||
+----+
|
+----+
|
||||||
|403 |
|
|403 |
|
||||||
+----+
|
+----+
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
</Para>
|
|
||||||
|
|
||||||
<Para>
|
We will use that <command>SELECT</command> in a <command>WHERE</command>
|
||||||
The <FileName>amstrategies</FileName> attribute exists to standardize
|
clause later.
|
||||||
comparisons across data types. For example, <Acronym>B-tree</Acronym>s
|
</para>
|
||||||
impose a strict ordering on keys, lesser to greater.
|
|
||||||
Since <ProductName>Postgres</ProductName> allows the user to define operators,
|
<para>
|
||||||
<ProductName>Postgres</ProductName> cannot look at the name of an operator (eg, ">"
|
The <filename>amstrategies</filename> attribute exists to standardize
|
||||||
or "<") and tell what kind of comparison it is. In fact,
|
comparisons across data types. For example, <acronym>B-tree</acronym>s
|
||||||
some access methods don't impose any ordering at all.
|
impose a strict ordering on keys, lesser to greater. Since
|
||||||
For example, <Acronym>R-tree</Acronym>s express a rectangle-containment
|
<productname>Postgres</productname> allows the user to define operators,
|
||||||
relationship, whereas a hashed data structure expresses
|
<productname>Postgres</productname> cannot look at the name of an operator
|
||||||
only bitwise similarity based on the value of a hash
|
(eg, ">" or "<") and tell what kind of comparison it is. In fact,
|
||||||
function. <ProductName>Postgres</ProductName> needs some consistent way of taking
|
some access methods don't impose any ordering at all. For example,
|
||||||
a qualification in your query, looking at the operator
|
<acronym>R-tree</acronym>s express a rectangle-containment relationship,
|
||||||
and then deciding if a usable index exists. This
|
whereas a hashed data structure expresses only bitwise similarity based
|
||||||
implies that <ProductName>Postgres</ProductName> needs to know, for example, that
|
on the value of a hash function. <productname>Postgres</productname>
|
||||||
the "<=" and ">" operators partition a <Acronym>B-tree</Acronym>. <ProductName>Postgres</ProductName>
|
needs some consistent way of taking a qualification in your query,
|
||||||
|
looking at the operator and then deciding if a usable index exists. This
|
||||||
|
implies that <productname>Postgres</productname> needs to know, for
|
||||||
|
example, that the "<=" and ">" operators partition a
|
||||||
|
<acronym>B-tree</acronym>. <productname>Postgres</productname>
|
||||||
uses strategies to express these relationships between
|
uses strategies to express these relationships between
|
||||||
operators and the way they can be used to scan indices.
|
operators and the way they can be used to scan indices.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Defining a new set of strategies is beyond the scope of
|
Defining a new set of strategies is beyond the scope of this discussion,
|
||||||
this discussion, but we'll explain how <Acronym>B-tree</Acronym> strategies
|
but we'll explain how <acronym>B-tree</acronym> strategies work because
|
||||||
work because you'll need to know that to add a new
|
you'll need to know that to add a new operator class. In the
|
||||||
operator class. In the <FileName>pg_am</FileName> class, the amstrategies
|
<filename>pg_am</filename> class, the amstrategies attribute is the
|
||||||
attribute is the number of strategies defined for this
|
number of strategies defined for this access method. For
|
||||||
access method. For <Acronym>B-tree</Acronym>s, this number is 5. These
|
<acronym>B-tree</acronym>s, this number is 5. These strategies
|
||||||
strategies correspond to
|
correspond to
|
||||||
|
|
||||||
<TABLE TOCENTRY="1">
|
<table tocentry="1">
|
||||||
<Title>B-tree Strategies</Title>
|
<title>B-tree Strategies</title>
|
||||||
<TitleAbbrev>B-tree</TitleAbbrev>
|
<titleabbrev>B-tree</titleabbrev>
|
||||||
<TGroup Cols="2">
|
<tgroup cols="2">
|
||||||
<THead>
|
<thead>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>Operation</Entry>
|
<entry>Operation</entry>
|
||||||
<Entry>Index</Entry>
|
<entry>Index</entry>
|
||||||
</Row>
|
</row>
|
||||||
</THead>
|
</thead>
|
||||||
<TBody>
|
<tbody>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>less than</Entry>
|
<entry>less than</entry>
|
||||||
<Entry>1</Entry>
|
<entry>1</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>less than or equal</Entry>
|
<entry>less than or equal</entry>
|
||||||
<Entry>2</Entry>
|
<entry>2</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>equal</Entry>
|
<entry>equal</entry>
|
||||||
<Entry>3</Entry>
|
<entry>3</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>greater than or equal</Entry>
|
<entry>greater than or equal</entry>
|
||||||
<Entry>4</Entry>
|
<entry>4</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>greater than</Entry>
|
<entry>greater than</entry>
|
||||||
<Entry>5</Entry>
|
<entry>5</entry>
|
||||||
</Row>
|
</row>
|
||||||
</TBody>
|
</tbody>
|
||||||
</TGroup>
|
</tgroup>
|
||||||
</TABLE>
|
</table>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The idea is that you'll need to add procedures corresponding
|
The idea is that you'll need to add procedures corresponding to the
|
||||||
to the comparisons above to the <FileName>pg_amop</FileName> relation
|
comparisons above to the <filename>pg_amop</filename> relation (see below).
|
||||||
(see below). The access method code can use these
|
The access method code can use these strategy numbers, regardless of data
|
||||||
strategy numbers, regardless of data type, to figure
|
type, to figure out how to partition the <acronym>B-tree</acronym>,
|
||||||
out how to partition the <Acronym>B-tree</Acronym>, compute selectivity,
|
compute selectivity, and so on. Don't worry about the details of adding
|
||||||
and so on. Don't worry about the details of adding
|
procedures yet; just understand that there must be a set of these
|
||||||
procedures yet; just understand that there must be a
|
procedures for <filename>int2, int4, oid,</filename> and every other
|
||||||
set of these procedures for <FileName>int2, int4, oid,</FileName> and every
|
data type on which a <acronym>B-tree</acronym> can operate.
|
||||||
other data type on which a <Acronym>B-tree</Acronym> can operate.
|
</para>
|
||||||
|
|
||||||
Sometimes, strategies aren't enough information for the
|
<para>
|
||||||
system to figure out how to use an index. Some access
|
Sometimes, strategies aren't enough information for the system to figure
|
||||||
methods require other support routines in order to
|
out how to use an index. Some access methods require other support
|
||||||
work. For example, the <Acronym>B-tree</Acronym> access method must be
|
routines in order to work. For example, the <acronym>B-tree</acronym>
|
||||||
able to compare two keys and determine whether one is
|
access method must be able to compare two keys and determine whether one
|
||||||
greater than, equal to, or less than the other.
|
is greater than, equal to, or less than the other. Similarly, the
|
||||||
Similarly, the <Acronym>R-tree</Acronym> access method must be able to compute
|
<acronym>R-tree</acronym> access method must be able to compute
|
||||||
intersections, unions, and sizes of rectangles. These
|
intersections, unions, and sizes of rectangles. These
|
||||||
operations do not correspond to user qualifications in
|
operations do not correspond to user qualifications in
|
||||||
SQL queries; they are administrative routines used by
|
SQL queries; they are administrative routines used by
|
||||||
the access methods, internally.
|
the access methods, internally.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
In order to manage diverse support routines
|
In order to manage diverse support routines consistently across all
|
||||||
consistently across all <ProductName>Postgres</ProductName> access methods, <FileName>pg_am</FileName>
|
<productname>Postgres</productname> access methods,
|
||||||
includes an attribute called <FileName>amsupport</FileName>. This attribute
|
<filename>pg_am</filename> includes an attribute called
|
||||||
records the number of support routines used by an
|
<filename>amsupport</filename>. This attribute records the number of
|
||||||
access method. For <Acronym>B-tree</Acronym>s, this number is one -- the
|
support routines used by an access method. For <acronym>B-tree</acronym>s,
|
||||||
routine to take two keys and return -1, 0, or +1,
|
this number is one -- the routine to take two keys and return -1, 0, or
|
||||||
depending on whether the first key is less than, equal
|
+1, depending on whether the first key is less than, equal
|
||||||
to, or greater than the second.
|
to, or greater than the second.
|
||||||
<Note>
|
|
||||||
<Para>
|
<note>
|
||||||
|
<para>
|
||||||
Strictly speaking, this routine can return a negative
|
Strictly speaking, this routine can return a negative
|
||||||
number (< 0), 0, or a non-zero positive number (> 0).
|
number (< 0), 0, or a non-zero positive number (> 0).
|
||||||
</Para>
|
|
||||||
</Note>
|
|
||||||
</para>
|
</para>
|
||||||
<Para>
|
</note>
|
||||||
The <FileName>amstrategies</FileName> entry in pg_am is just the number of
|
</para>
|
||||||
strategies defined for the access method in question.
|
|
||||||
The procedures for less than, less equal, and so on
|
|
||||||
don't appear in <FileName>pg_am</FileName>. Similarly, <FileName>amsupport</FileName> is just
|
|
||||||
the number of support routines required by the access
|
|
||||||
method. The actual routines are listed elsewhere.
|
|
||||||
</Para>
|
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The next class of interest is pg_opclass. This class
|
The <filename>amstrategies</filename> entry in pg_am is just the number
|
||||||
exists only to associate a name with an oid. In
|
of strategies defined for the access method in question. The procedures
|
||||||
pg_amop, every <Acronym>B-tree</Acronym> operator class has a set of
|
for less than, less equal, and so on don't appear in
|
||||||
procedures, one through five, above. Some existing
|
<filename>pg_am</filename>. Similarly, <filename>amsupport</filename>
|
||||||
opclasses are <FileName>int2_ops, int4_ops, and oid_ops</FileName>. You
|
is just the number of support routines required by the access
|
||||||
need to add an instance with your opclass name (for
|
method. The actual routines are listed elsewhere.
|
||||||
example, <FileName>complex_abs_ops</FileName>) to <FileName>pg_opclass</FileName>. The <FileName>oid</FileName> of
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The next class of interest is pg_opclass. This class exists only to
|
||||||
|
associate a name and default type with an oid. In pg_amop, every
|
||||||
|
<acronym>B-tree</acronym> operator class has a set of procedures, one
|
||||||
|
through five, above. Some existing opclasses are <filename>int2_ops,
|
||||||
|
int4_ops, and oid_ops</filename>. You need to add an instance with your
|
||||||
|
opclass name (for example, <filename>complex_abs_ops</filename>) to
|
||||||
|
<filename>pg_opclass</filename>. The <filename>oid</filename> of
|
||||||
this instance is a foreign key in other classes.
|
this instance is a foreign key in other classes.
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
INSERT INTO pg_opclass (opcname) VALUES ('complex_abs_ops');
|
INSERT INTO pg_opclass (opcname, opcdeftype)
|
||||||
|
SELECT 'complex_abs_ops', oid FROM pg_type WHERE typname = 'complex_abs';
|
||||||
|
|
||||||
SELECT oid, opcname
|
SELECT oid, opcname, opcdeftype
|
||||||
FROM pg_opclass
|
FROM pg_opclass
|
||||||
WHERE opcname = 'complex_abs_ops';
|
WHERE opcname = 'complex_abs_ops';
|
||||||
|
|
||||||
+------+--------------+
|
+------+-----------------+------------+
|
||||||
|oid | opcname |
|
|oid | opcname | opcdeftype |
|
||||||
+------+--------------+
|
+------+-----------------+------------+
|
||||||
|17314 | int4_abs_ops |
|
|17314 | complex_abs_ops | 29058 |
|
||||||
+------+--------------+
|
+------+-----------------+------------+
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
|
|
||||||
Note that the oid for your <FileName>pg_opclass</FileName> instance will be
|
Note that the oid for your <filename>pg_opclass</filename> instance will
|
||||||
different! You should substitute your value for 17314
|
be different! Don't worry about this though. We'll get this number
|
||||||
wherever it appears in this discussion.
|
from the system later just like we got the oid of the type here.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
So now we have an access method and an operator class.
|
So now we have an access method and an operator class.
|
||||||
We still need a set of operators; the procedure for
|
We still need a set of operators; the procedure for
|
||||||
defining operators was discussed earlier in this manual.
|
defining operators was discussed earlier in this manual.
|
||||||
For the complex_abs_ops operator class on Btrees,
|
For the complex_abs_ops operator class on Btrees,
|
||||||
the operators we require are:
|
the operators we require are:
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
absolute value less-than
|
absolute value less-than
|
||||||
absolute value less-than-or-equal
|
absolute value less-than-or-equal
|
||||||
absolute value equal
|
absolute value equal
|
||||||
absolute value greater-than-or-equal
|
absolute value greater-than-or-equal
|
||||||
absolute value greater-than
|
absolute value greater-than
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Suppose the code that implements the functions defined
|
Suppose the code that implements the functions defined
|
||||||
is stored in the file
|
is stored in the file
|
||||||
<FileName>PGROOT/src/tutorial/complex.c</FileName>
|
<filename>PGROOT/src/tutorial/complex.c</filename>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Part of the code look like this: (note that we will
|
Part of the code look like this: (note that we will only show the
|
||||||
only show the equality operator for the rest of the
|
equality operator for the rest of the examples. The other four
|
||||||
examples. The other four operators are very similar.
|
operators are very similar. Refer to <filename>complex.c</filename>
|
||||||
Refer to <FileName>complex.c</FileName> or <FileName>complex.sql</FileName> for the details.)
|
or <filename>complex.source</filename> for the details.)
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
|
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -272,61 +279,57 @@ SELECT oid, opcname
|
|||||||
double amag = Mag(a), bmag = Mag(b);
|
double amag = Mag(a), bmag = Mag(b);
|
||||||
return (amag==bmag);
|
return (amag==bmag);
|
||||||
}
|
}
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
There are a couple of important things that are happening below.
|
There are a couple of important things that are happening below.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
First, note that operators for less-than, less-than-or
|
First, note that operators for less-than, less-than-or equal, equal,
|
||||||
equal, equal, greater-than-or-equal, and greater-than
|
greater-than-or-equal, and greater-than for <filename>int4</filename>
|
||||||
for <FileName>int4</FileName> are being defined. All of these operators are
|
are being defined. All of these operators are already defined for
|
||||||
already defined for <FileName>int4</FileName> under the names <, <=, =, >=,
|
<filename>int4</filename> under the names <, <=, =, >=,
|
||||||
and >. The new operators behave differently, of
|
and >. The new operators behave differently, of course. In order
|
||||||
course. In order to guarantee that <ProductName>Postgres</ProductName> uses these
|
to guarantee that <productname>Postgres</productname> uses these
|
||||||
new operators rather than the old ones, they need to be
|
new operators rather than the old ones, they need to be named differently
|
||||||
named differently from the old ones. This is a key
|
from the old ones. This is a key point: you can overload operators in
|
||||||
point: you can overload operators in <ProductName>Postgres</ProductName>, but only
|
<productname>Postgres</productname>, but only if the operator isn't
|
||||||
if the operator isn't already defined for the argument
|
already defined for the argument types. That is, if you have <
|
||||||
types. That is, if you have < defined for (int4,
|
defined for (int4, int4), you can't define it again.
|
||||||
int4), you can't define it again. <ProductName>Postgres</ProductName> does not
|
<productname>Postgres</productname> does not check this when you define
|
||||||
check this when you define your operator, so be careful.
|
your operator, so be careful. To avoid this problem, odd names will be
|
||||||
To avoid this problem, odd names will be used for
|
used for the operators. If you get this wrong, the access methods
|
||||||
the operators. If you get this wrong, the access methods
|
|
||||||
are likely to crash when you try to do scans.
|
are likely to crash when you try to do scans.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The other important point is that all the operator
|
The other important point is that all the operator functions return
|
||||||
functions return Boolean values. The access methods
|
Boolean values. The access methods rely on this fact. (On the other
|
||||||
rely on this fact. (On the other hand, the support
|
hand, the support function returns whatever the particular access method
|
||||||
function returns whatever the particular access method
|
expects -- in this case, a signed integer.) The final routine in the
|
||||||
expects -- in this case, a signed integer.)
|
file is the "support routine" mentioned when we discussed the amsupport
|
||||||
The final routine in the file is the "support routine"
|
attribute of the <filename>pg_am</filename> class. We will use this
|
||||||
mentioned when we discussed the amsupport attribute of
|
later on. For now, ignore it.
|
||||||
the <FileName>pg_am</FileName> class. We will use this later on. For now,
|
</para>
|
||||||
ignore it.
|
|
||||||
</Para>
|
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
CREATE FUNCTION complex_abs_eq(complex, complex)
|
CREATE FUNCTION complex_abs_eq(complex_abs, complex_abs)
|
||||||
RETURNS bool
|
RETURNS bool
|
||||||
AS 'PGROOT/tutorial/obj/complex.so'
|
AS 'PGROOT/tutorial/obj/complex.so'
|
||||||
LANGUAGE 'c';
|
LANGUAGE 'c';
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Now define the operators that use them. As noted, the
|
Now define the operators that use them. As noted, the operator names
|
||||||
operator names must be unique among all operators that
|
must be unique among all operators that take two <filename>int4</filename>
|
||||||
take two <FileName>int4</FileName> operands. In order to see if the
|
operands. In order to see if the operator names listed below are taken,
|
||||||
operator names listed below are taken, we can do a query on
|
we can do a query on <filename>pg_operator</filename>:
|
||||||
<FileName>pg_operator</FileName>:
|
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
/*
|
/*
|
||||||
* this query uses the regular expression operator (~)
|
* this query uses the regular expression operator (~)
|
||||||
* to find three-character operator names that end in
|
* to find three-character operator names that end in
|
||||||
@ -335,95 +338,93 @@ CREATE FUNCTION complex_abs_eq(complex, complex)
|
|||||||
SELECT *
|
SELECT *
|
||||||
FROM pg_operator
|
FROM pg_operator
|
||||||
WHERE oprname ~ '^..&$'::text;
|
WHERE oprname ~ '^..&$'::text;
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
|
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
to see if your name is taken for the types you want.
|
to see if your name is taken for the types you want. The important
|
||||||
The important things here are the procedure (which are
|
things here are the procedure (which are the <acronym>C</acronym>
|
||||||
the <Acronym>C</Acronym> functions defined above) and the restriction and
|
functions defined above) and the restriction and join selectivity
|
||||||
join selectivity functions. You should just use the
|
functions. You should just use the ones used below--note that there
|
||||||
ones used below--note that there are different such
|
are different such functions for the less-than, equal, and greater-than
|
||||||
functions for the less-than, equal, and greater-than
|
cases. These must be supplied, or the access method will crash when it
|
||||||
cases. These must be supplied, or the access method
|
tries to use the operator. You should copy the names for restrict and
|
||||||
will crash when it tries to use the operator. You
|
join, but use the procedure names you defined in the last step.
|
||||||
should copy the names for restrict and join, but use
|
|
||||||
the procedure names you defined in the last step.
|
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
CREATE OPERATOR = (
|
CREATE OPERATOR = (
|
||||||
leftarg = complex, rightarg = complex,
|
leftarg = complex_abs, rightarg = complex_abs,
|
||||||
procedure = complex_abs_eq,
|
procedure = complex_abs_eq,
|
||||||
restrict = eqsel, join = eqjoinsel
|
restrict = eqsel, join = eqjoinsel
|
||||||
)
|
)
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Notice that five operators corresponding to less, less
|
Notice that five operators corresponding to less, less equal, equal,
|
||||||
equal, equal, greater, and greater equal are defined.
|
greater, and greater equal are defined.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
We're just about finished. the last thing we need to do
|
We're just about finished. the last thing we need to do is to update
|
||||||
is to update the <FileName>pg_amop</FileName> relation. To do this, we need
|
the <filename>pg_amop</filename> relation. To do this, we need the
|
||||||
the following attributes:
|
following attributes:
|
||||||
|
|
||||||
<TABLE TOCENTRY="1">
|
<table tocentry="1">
|
||||||
<Title><FileName>pg_amproc</FileName> Schema</Title>
|
<title><filename>pg_amproc</filename> Schema</title>
|
||||||
<TitleAbbrev><FileName>pg_amproc</FileName></TitleAbbrev>
|
<titleabbrev><filename>pg_amproc</filename></titleabbrev>
|
||||||
<TGroup Cols="2">
|
<tgroup cols="2">
|
||||||
<THead>
|
<thead>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>Attribute</Entry>
|
<entry>Attribute</entry>
|
||||||
<Entry>Description</Entry>
|
<entry>Description</entry>
|
||||||
</Row>
|
</row>
|
||||||
</THead>
|
</thead>
|
||||||
<TBody>
|
<tbody>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amopid</Entry>
|
<entry>amopid</entry>
|
||||||
<Entry>the <FileName>oid</FileName> of the <FileName>pg_am</FileName> instance
|
<entry>the <filename>oid</filename> of the <filename>pg_am</filename> instance
|
||||||
for B-tree (== 403, see above)</Entry>
|
for B-tree (== 403, see above)</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amopclaid</Entry>
|
<entry>amopclaid</entry>
|
||||||
<Entry>the <FileName>oid</FileName> of the
|
<entry>the <filename>oid</filename> of the
|
||||||
<FileName>pg_opclass</FileName> instance for <FileName>int4_abs_ops</FileName>
|
<filename>pg_opclass</filename> instance for <filename>complex_abs_ops</filename>
|
||||||
(== whatever you got instead of <FileName>17314</FileName>, see above)</Entry>
|
(== whatever you got instead of <filename>17314</filename>, see above)</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amopopr</Entry>
|
<entry>amopopr</entry>
|
||||||
<Entry>the <FileName>oid</FileName>s of the operators for the opclass
|
<entry>the <filename>oid</filename>s of the operators for the opclass
|
||||||
(which we'll get in just a minute)</Entry>
|
(which we'll get in just a minute)</entry>
|
||||||
</Row>
|
</row>
|
||||||
<Row>
|
<row>
|
||||||
<Entry>amopselect, amopnpages</Entry>
|
<entry>amopselect, amopnpages</entry>
|
||||||
<Entry>cost functions</Entry>
|
<entry>cost functions</entry>
|
||||||
</Row>
|
</row>
|
||||||
</TBody>
|
</tbody>
|
||||||
</TGroup>
|
</tgroup>
|
||||||
</TABLE>
|
</table>
|
||||||
|
|
||||||
The cost functions are used by the query optimizer to
|
The cost functions are used by the query optimizer to decide whether or
|
||||||
decide whether or not to use a given index in a scan.
|
not to use a given index in a scan. Fortunately, these already exist.
|
||||||
Fortunately, these already exist. The two functions
|
The two functions we'll use are <filename>btreesel</filename>, which
|
||||||
we'll use are <FileName>btreesel</FileName>, which estimates the selectivity
|
estimates the selectivity of the <acronym>B-tree</acronym>, and
|
||||||
of the <Acronym>B-tree</Acronym>, and <FileName>btreenpage</FileName>, which estimates the
|
<filename>btreenpage</filename>, which estimates the number of pages a
|
||||||
number of pages a search will touch in the tree.
|
search will touch in the tree.
|
||||||
</Para>
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
So we need the <FileName>oid</FileName>s of the operators we just defined.
|
So we need the <filename>oid</filename>s of the operators we just
|
||||||
We'll look up the names of all the operators that take
|
defined. We'll look up the names of all the operators that take
|
||||||
two <FileName>int4</FileName>s, and pick ours out:
|
two <filename>complex</filename>es, and pick ours out:
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
SELECT o.oid AS opoid, o.oprname
|
SELECT o.oid AS opoid, o.oprname
|
||||||
INTO TABLE complex_ops_tmp
|
INTO TABLE complex_ops_tmp
|
||||||
FROM pg_operator o, pg_type t
|
FROM pg_operator o, pg_type t
|
||||||
WHERE o.oprleft = t.oid and o.oprright = t.oid
|
WHERE o.oprleft = t.oid and o.oprright = t.oid
|
||||||
and t.typname = 'complex';
|
and t.typname = 'complex_abs';
|
||||||
|
|
||||||
+------+---------+
|
+------+---------+
|
||||||
|oid | oprname |
|
|oid | oprname |
|
||||||
@ -438,78 +439,169 @@ CREATE OPERATOR = (
|
|||||||
+------+---------+
|
+------+---------+
|
||||||
|17325 | > |
|
|17325 | > |
|
||||||
+------+---------+
|
+------+---------+
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
|
|
||||||
(Again, some of your <FileName>oid</FileName> numbers will almost certainly
|
(Again, some of your <filename>oid</filename> numbers will almost
|
||||||
be different.) The operators we are interested in are
|
certainly be different.) The operators we are interested in are those
|
||||||
those with <FileName>oid</FileName>s 17321 through 17325. The values you
|
with <filename>oid</filename>s 17321 through 17325. The values you
|
||||||
get will probably be different, and you should
|
get will probably be different, and you should substitute them for the
|
||||||
substitute them for the values below. We can look at the
|
values below. We will do this with a select statement.
|
||||||
operator names and pick out the ones we just added.
|
</para>
|
||||||
</Para>
|
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
Now we're ready to update <FileName>pg_amop</FileName> with our new operator
|
Now we're ready to update <filename>pg_amop</filename> with our new
|
||||||
class. The most important thing in this entire
|
operator class. The most important thing in this entire discussion
|
||||||
discussion is that the operators are ordered, from less equal
|
is that the operators are ordered, from less equal through greater
|
||||||
through greater equal, in <FileName>pg_amop</FileName>. We add the
|
equal, in <filename>pg_amop</filename>. We add the instances we need:
|
||||||
instances we need:
|
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
INSERT INTO pg_amop (amopid, amopclaid,
|
INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy,
|
||||||
amopopr, amopstrategy,
|
|
||||||
amopselect, amopnpages)
|
amopselect, amopnpages)
|
||||||
SELECT am.oid, opcl.oid, c.opoid, 3,
|
SELECT am.oid, opcl.oid, c.opoid, 1,
|
||||||
'btreesel'::regproc, 'btreenpage'::regproc
|
'btreesel'::regproc, 'btreenpage'::regproc
|
||||||
FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
|
FROM pg_am am, pg_opclass opcl, complex_abs_ops_tmp c
|
||||||
WHERE amname = 'btree'
|
WHERE amname = 'btree' AND
|
||||||
and opcname = 'complex_abs_ops'
|
opcname = 'complex_abs_ops' AND
|
||||||
and c.oprname = '=';
|
c.oprname = '<';
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
|
|
||||||
Note the order: "less than" is 1, "less than or equal"
|
Now do this for the other operators substituting for the "1" in the
|
||||||
is 2, "equal" is 3, "greater than or equal" is 4, and
|
third line above and the "<" in the last line. Note the order:
|
||||||
"greater than" is 5.
|
"less than" is 1, "less than or equal" is 2, "equal" is 3, "greater
|
||||||
</Para>
|
than or equal" is 4, and "greater than" is 5.
|
||||||
|
</para>
|
||||||
|
|
||||||
<Para>
|
<para>
|
||||||
The last step (finally!) is registration of the
|
The next step is registration of the "support routine" previously
|
||||||
"support routine" previously described in our discussion of
|
described in our discussion of <filename>pg_am</filename>. The
|
||||||
<FileName>pg_am</FileName>. The <FileName>oid</FileName> of this support routine is stored in
|
<filename>oid</filename> of this support routine is stored in the
|
||||||
the <FileName>pg_amproc</FileName> class, keyed by the access method <FileName>oid</FileName> and
|
<filename>pg_amproc</filename> class, keyed by the access method
|
||||||
the operator class <FileName>oid</FileName>. First, we need to register the
|
<filename>oid</filename> and the operator class <filename>oid</filename>.
|
||||||
function in <ProductName>Postgres</ProductName> (recall that we put the <Acronym>C</Acronym> code
|
First, we need to register the function in
|
||||||
that implements this routine in the bottom of the file
|
<productname>Postgres</productname> (recall that we put the
|
||||||
in which we implemented the operator routines):
|
<acronym>C</acronym> code that implements this routine in the bottom of
|
||||||
|
the file in which we implemented the operator routines):
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
CREATE FUNCTION int4_abs_cmp(int4, int4)
|
CREATE FUNCTION complex_abs_cmp(complex, complex)
|
||||||
RETURNS int4
|
RETURNS int4
|
||||||
AS 'PGROOT/tutorial/obj/complex.so'
|
AS 'PGROOT/tutorial/obj/complex.so'
|
||||||
LANGUAGE 'c';
|
LANGUAGE 'c';
|
||||||
|
|
||||||
SELECT oid, proname FROM pg_proc
|
SELECT oid, proname FROM pg_proc
|
||||||
WHERE prname = 'int4_abs_cmp';
|
WHERE proname = 'complex_abs_cmp';
|
||||||
|
|
||||||
+------+--------------+
|
+------+-----------------+
|
||||||
|oid | proname |
|
|oid | proname |
|
||||||
+------+--------------+
|
+------+-----------------+
|
||||||
|17328 | int4_abs_cmp |
|
|17328 | complex_abs_cmp |
|
||||||
+------+--------------+
|
+------+-----------------+
|
||||||
</ProgramListing>
|
</programlisting>
|
||||||
|
|
||||||
(Again, your <FileName>oid</FileName> number will probably be different and
|
(Again, your <filename>oid</filename> number will probably be different
|
||||||
you should substitute the value you see for the value
|
and you should substitute the value you see for the value below.)
|
||||||
below.) Recalling that the <Acronym>B-tree</Acronym> instance's oid is
|
We can add the new instance as follows:
|
||||||
403 and that of <FileName>int4_abs_ops</FileName> is 17314, we can add the
|
|
||||||
new instance as follows:
|
|
||||||
|
|
||||||
<ProgramListing>
|
<programlisting>
|
||||||
INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
|
INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
|
||||||
VALUES ('403'::oid, -- btree oid
|
SELECT a.oid, b.oid, c.oid, 1
|
||||||
'17314'::oid, -- pg_opclass tuple
|
FROM pg_am a, pg_opclass b, pg_proc c
|
||||||
'17328'::oid, -- new pg_proc oid
|
WHERE a.amname = 'btree' AND
|
||||||
'1'::int2);
|
b.opcname = 'complex_abs_ops' AND
|
||||||
</ProgramListing>
|
c.proname = 'complex_abs_cmp';
|
||||||
</Para>
|
</programlisting>
|
||||||
</Chapter>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Now we need to add a hashing strategy to allow the type to be indexed.
|
||||||
|
We do this by using another type in pg_am but we reuse the sames ops.
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy,
|
||||||
|
amopselect, amopnpages)
|
||||||
|
SELECT am.oid, opcl.oid, c.opoid, 1,
|
||||||
|
'hashsel'::regproc, 'hashnpage'::regproc
|
||||||
|
FROM pg_am am, pg_opclass opcl, complex_abs_ops_tmp c
|
||||||
|
WHERE amname = 'hash' AND
|
||||||
|
opcname = 'complex_abs_ops' AND
|
||||||
|
c.oprname = '=';
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In order to use this index in a where clause, we need to modify the
|
||||||
|
<filename>pg_operator</filename> class as follows.
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'eqsel'::regproc, oprjoin = 'eqjoinsel'
|
||||||
|
WHERE oprname = '=' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
|
||||||
|
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'neqsel'::regproc, oprjoin = 'neqjoinsel'
|
||||||
|
WHERE oprname = '<filename>' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
|
||||||
|
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'neqsel'::regproc, oprjoin = 'neqjoinsel'
|
||||||
|
WHERE oprname = '<filename>' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
|
||||||
|
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'intltsel'::regproc, oprjoin = 'intltjoinsel'
|
||||||
|
WHERE oprname = '<' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
|
||||||
|
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'intltsel'::regproc, oprjoin = 'intltjoinsel'
|
||||||
|
WHERE oprname = '<=' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
|
||||||
|
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'intgtsel'::regproc, oprjoin = 'intgtjoinsel'
|
||||||
|
WHERE oprname = '>' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
|
||||||
|
|
||||||
|
UPDATE pg_operator
|
||||||
|
SET oprrest = 'intgtsel'::regproc, oprjoin = 'intgtjoinsel'
|
||||||
|
WHERE oprname = '>=' AND
|
||||||
|
oprleft = oprright AND
|
||||||
|
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');</filename></filename>
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
And last (Finally!) we register a description of this type.
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
INSERT INTO pg_description (objoid, description)
|
||||||
|
SELECT oid, 'Two part G/L account'
|
||||||
|
FROM pg_type WHERE typname = 'complex_abs';
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</chapter>
|
||||||
|
|
||||||
|
<!-- Keep this comment at the end of the file
|
||||||
|
Local variables:
|
||||||
|
mode: sgml
|
||||||
|
sgml-omittag:nil
|
||||||
|
sgml-shorttag:t
|
||||||
|
sgml-minimize-attributes:nil
|
||||||
|
sgml-always-quote-attributes:t
|
||||||
|
sgml-indent-step:1
|
||||||
|
sgml-indent-data:t
|
||||||
|
sgml-parent-document:nil
|
||||||
|
sgml-default-dtd-file:"./reference.ced"
|
||||||
|
sgml-exposed-tags:nil
|
||||||
|
sgml-local-catalogs:"/usr/lib/sgml/catalog"
|
||||||
|
sgml-local-ecat-files:nil
|
||||||
|
End:
|
||||||
|
-->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user