1
0
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:
Thomas G. Lockhart 1999-05-27 15:44:54 +00:00
parent 874957a32a
commit d6efbf1956

View File

@ -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)-&gt;x*(c)-&gt;x + (c)-&gt;y*(c)-&gt;y) #define Mag(c) ((c)-&gt;x*(c)-&gt;x + (c)-&gt;y*(c)-&gt;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 &lt;, &lt;=, =, &gt;=, <filename>int4</filename> under the names &lt;, &lt;=, =, &gt;=,
and &gt;. The new operators behave differently, of and &gt;. 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 &lt;
types. That is, if you have &lt; 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 ~ '^..&amp;$'::text; WHERE oprname ~ '^..&amp;$'::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 | &gt; | |17325 | &gt; |
+------+---------+ +------+---------+
</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:
-->