Commit 0fb864f1 authored by Tom Lane's avatar Tom Lane

Clean up major bogosity in description of creating a new index operator

class.  This chapter was apparently sewed together from several
inconsistent examples... and perhaps from old docs that no longer
apply at all.
parent f71fb9e0
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/xindex.sgml,v 1.8 2000/02/17 03:39:39 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/xindex.sgml,v 1.9 2000/03/28 02:53:02 tgl Exp $
Postgres documentation Postgres documentation
--> -->
...@@ -91,11 +91,10 @@ Postgres documentation ...@@ -91,11 +91,10 @@ Postgres documentation
<programlisting> <programlisting>
SELECT oid FROM pg_am WHERE amname = 'btree'; SELECT oid FROM pg_am WHERE amname = 'btree';
+----+ oid
|oid | -----
+----+ 403
|403 | (1 row)
+----+
</programlisting> </programlisting>
We will use that <command>SELECT</command> in a <command>WHERE</command> We will use that <command>SELECT</command> in a <command>WHERE</command>
...@@ -210,7 +209,8 @@ SELECT oid FROM pg_am WHERE amname = 'btree'; ...@@ -210,7 +209,8 @@ SELECT oid FROM pg_am WHERE amname = 'btree';
</para> </para>
<para> <para>
The <filename>amstrategies</filename> entry in pg_am is just the number The <filename>amstrategies</filename> entry in <filename>pg_am</filename>
is just the number
of strategies defined for the access method in question. The procedures of strategies defined for the access method in question. The procedures
for less than, less equal, and so on don't appear in for less than, less equal, and so on don't appear in
<filename>pg_am</filename>. Similarly, <filename>amsupport</filename> <filename>pg_am</filename>. Similarly, <filename>amsupport</filename>
...@@ -228,28 +228,28 @@ SELECT oid FROM pg_am WHERE amname = 'btree'; ...@@ -228,28 +228,28 @@ SELECT oid FROM pg_am WHERE amname = 'btree';
</para> </para>
<para> <para>
The next class of interest is pg_opclass. This class exists only to The next class of interest is <filename>pg_opclass</filename>. This class
associate a name and default type with an oid. In pg_amop, every exists only to associate an operator class name and perhaps a default type
<acronym>B-tree</acronym> operator class has a set of procedures, one with an operator class oid. Some existing opclasses are <filename>int2_ops,
through five, above. Some existing opclasses are <filename>int2_ops, int4_ops,</filename> and <filename>oid_ops</filename>. You need to add an
int4_ops, and oid_ops</filename>. You need to add an instance with your instance with your opclass name (for example,
opclass name (for example, <filename>complex_abs_ops</filename>) to <filename>complex_abs_ops</filename>) to
<filename>pg_opclass</filename>. The <filename>oid</filename> of <filename>pg_opclass</filename>. The <filename>oid</filename> of
this instance is a foreign key in other classes. this instance will be a foreign key in other classes, notably
<filename>pg_amop</filename>.
<programlisting> <programlisting>
INSERT INTO pg_opclass (opcname, opcdeftype) INSERT INTO pg_opclass (opcname, opcdeftype)
SELECT 'complex_abs_ops', oid FROM pg_type WHERE typname = 'complex_abs'; SELECT 'complex_abs_ops', oid FROM pg_type WHERE typname = 'complex';
SELECT oid, opcname, opcdeftype SELECT oid, opcname, opcdeftype
FROM pg_opclass FROM pg_opclass
WHERE opcname = 'complex_abs_ops'; WHERE opcname = 'complex_abs_ops';
+------+-----------------+------------+ oid | opcname | opcdeftype
|oid | opcname | opcdeftype | --------+-----------------+------------
+------+-----------------+------------+ 277975 | complex_abs_ops | 277946
|17314 | complex_abs_ops | 29058 | (1 row)
+------+-----------------+------------+
</programlisting> </programlisting>
Note that the oid for your <filename>pg_opclass</filename> instance will Note that the oid for your <filename>pg_opclass</filename> instance will
...@@ -257,11 +257,23 @@ SELECT oid, opcname, opcdeftype ...@@ -257,11 +257,23 @@ SELECT oid, opcname, opcdeftype
from the system later just like we got the oid of the type here. from the system later just like we got the oid of the type here.
</para> </para>
<para>
The above example assumes that you want to make this new opclass the
default index opclass for the <filename>complex</filename> datatype.
If you don't, just insert zero into <filename>opcdeftype</filename>,
rather than inserting the datatype's oid:
<programlisting>
INSERT INTO pg_opclass (opcname, opcdeftype) VALUES ('complex_abs_ops', 0);
</programlisting>
</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 <filename>complex_abs_ops</filename> operator class on Btrees,
the operators we require are: the operators we require are:
<programlisting> <programlisting>
...@@ -280,7 +292,7 @@ SELECT oid, opcname, opcdeftype ...@@ -280,7 +292,7 @@ SELECT oid, opcname, opcdeftype
</para> </para>
<para> <para>
Part of the code look like this: (note that we will only show the Part of the C code looks like this: (note that we will only show the
equality operator for the rest of the examples. The other four equality operator for the rest of the examples. The other four
operators are very similar. Refer to <filename>complex.c</filename> operators are very similar. Refer to <filename>complex.c</filename>
or <filename>complex.source</filename> for the details.) or <filename>complex.source</filename> for the details.)
...@@ -298,128 +310,83 @@ SELECT oid, opcname, opcdeftype ...@@ -298,128 +310,83 @@ SELECT oid, opcname, opcdeftype
</para> </para>
<para> <para>
There are a couple of important things that are happening below. We make the function known to Postgres like this:
<programlisting>
CREATE FUNCTION complex_abs_eq(complex, complex)
RETURNS bool
AS 'PGROOT/tutorial/obj/complex.so'
LANGUAGE 'c';
</programlisting>
</para> </para>
<para> <para>
First, note that operators for less-than, less-than-or equal, equal, There are some important things that are happening here.
greater-than-or-equal, and greater-than for <filename>int4</filename>
are being defined. All of these operators are already defined for
<filename>int4</filename> under the names &lt;, &lt;=, =, &gt;=,
and &gt;. The new operators behave differently, of course. In order
to guarantee that <productname>Postgres</productname> uses these
new operators rather than the old ones, they need to be named differently
from the old ones. This is a key point: you can overload operators in
<productname>Postgres</productname>, but only if the operator isn't
already defined for the argument types. That is, if you have &lt;
defined for (int4, int4), you can't define it again.
<productname>Postgres</productname> does not check this when you define
your operator, so be careful. To avoid this problem, odd names will be
used for the operators. If you get this wrong, the access methods
are likely to crash when you try to do scans.
</para> </para>
<para> <para>
The other important point is that all the operator functions return First, note that operators for less-than, less-than-or-equal, equal,
Boolean values. The access methods rely on this fact. (On the other greater-than-or-equal, and greater-than for <filename>complex</filename>
hand, the support function returns whatever the particular access method are being defined. We can only have one operator named, say, = and
expects -- in this case, a signed integer.) The final routine in the taking type <filename>complex</filename> for both operands. In this case
file is the "support routine" mentioned when we discussed the amsupport we don't have any other operator = for <filename>complex</filename>,
attribute of the <filename>pg_am</filename> class. We will use this but if we were building a practical datatype we'd probably want = to
later on. For now, ignore it. be the ordinary equality operation for complex numbers. In that case,
we'd need to use some other operator name for complex_abs_eq.
</para> </para>
<para> <para>
<programlisting> Second, although Postgres can cope with operators having
CREATE FUNCTION complex_abs_eq(complex_abs, complex_abs) the same name as long as they have different input datatypes, C can only
RETURNS bool cope with one global routine having a given name, period. So we shouldn't
AS 'PGROOT/tutorial/obj/complex.so' name the C function something simple like <filename>abs_eq</filename>.
LANGUAGE 'c'; Usually it's a good practice to include the datatype name in the C
</programlisting> function name, so as not to conflict with functions for other datatypes.
</para> </para>
<para> <para>
Now define the operators that use them. As noted, the operator names Third, we could have made the Postgres name of the function
must be unique among all operators that take two <filename>int4</filename> <filename>abs_eq</filename>, relying on Postgres to distinguish it
operands. In order to see if the operator names listed below are taken, by input datatypes from any other Postgres function of the same name.
we can do a query on <filename>pg_operator</filename>: To keep the example simple, we make the function have the same names
at the C level and Postgres level.
<programlisting> </para>
/*
* this query uses the regular expression operator (~)
* to find three-character operator names that end in
* the character &amp;
*/
SELECT *
FROM pg_operator
WHERE oprname ~ '^..&amp;$'::text;
</programlisting>
<para>
Finally, note that these operator functions return Boolean values.
The access methods rely on this fact. (On the other
hand, the support function returns whatever the particular access method
expects -- in this case, a signed integer.) The final routine in the
file is the "support routine" mentioned when we discussed the amsupport
attribute of the <filename>pg_am</filename> class. We will use this
later on. For now, ignore it.
</para> </para>
<para> <para>
to see if your name is taken for the types you want. The important Now we are ready to define the operators:
things here are the procedure (which are the <acronym>C</acronym>
functions defined above) and the restriction and join selectivity
functions. You should just use the ones used below--note that there
are different such functions for the less-than, equal, and greater-than
cases. These must be supplied, or the access method will crash when it
tries to use the operator. You 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_abs, rightarg = complex_abs, leftarg = complex, rightarg = complex,
procedure = complex_abs_eq, procedure = complex_abs_eq,
restrict = eqsel, join = eqjoinsel restrict = eqsel, join = eqjoinsel
) )
</programlisting> </programlisting>
</para>
<para>
Notice that five operators corresponding to less, less equal, equal,
greater, and greater equal are defined.
</para>
<para>
We're just about finished. the last thing we need to do is to update
the <filename>pg_amop</filename> relation. To do this, we need the
following attributes:
<table tocentry="1"> The important
<title><filename>pg_amproc</filename> Schema</title> things here are the procedure names (which are the <acronym>C</acronym>
<titleabbrev><filename>pg_amproc</filename></titleabbrev> functions defined above) and the restriction and join selectivity
<tgroup cols="2"> functions. You should just use the selectivity functions used in
<thead> the example (see <filename>complex.source</filename>).
<row> Note that there
<entry>Attribute</entry> are different such functions for the less-than, equal, and greater-than
<entry>Description</entry> cases. These must be supplied, or the optimizer will be unable to
</row> make effective use of the index.
</thead>
<tbody>
<row>
<entry>amopid</entry>
<entry>the <filename>oid</filename> of the <filename>pg_am</filename> instance
for B-tree (== 403, see above)</entry>
</row>
<row>
<entry>amopclaid</entry>
<entry>the <filename>oid</filename> of the
<filename>pg_opclass</filename> instance for <filename>complex_abs_ops</filename>
(== whatever you got instead of <filename>17314</filename>, see above)</entry>
</row>
<row>
<entry>amopopr</entry>
<entry>the <filename>oid</filename>s of the operators for the opclass
(which we'll get in just a minute)</entry>
</row>
</tbody>
</tgroup>
</table>
</para> </para>
<para> <para>
So we need the <filename>oid</filename>s of the operators we just The next step is to add entries for these operators to
the <filename>pg_amop</filename> relation. To do this,
we'll need the <filename>oid</filename>s of the operators we just
defined. 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>complex</filename>es, and pick ours out: two <filename>complex</filename>es, and pick ours out:
...@@ -428,26 +395,22 @@ CREATE OPERATOR = ( ...@@ -428,26 +395,22 @@ CREATE OPERATOR = (
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_abs'; and t.typname = 'complex';
+------+---------+ opoid | oprname
|oid | oprname | --------+---------
+------+---------+ 277963 | +
|17321 | &lt; | 277970 | &lt;
+------+---------+ 277971 | &lt;=
|17322 | &lt;= | 277972 | =
+------+---------+ 277973 | &gt;=
|17323 | = | 277974 | &gt;
+------+---------+ (6 rows)
|17324 | &gt;= |
+------+---------+
|17325 | &gt; |
+------+---------+
</programlisting> </programlisting>
(Again, some of your <filename>oid</filename> numbers will almost (Again, some of your <filename>oid</filename> numbers will almost
certainly be different.) The operators we are interested in are those certainly be different.) The operators we are interested in are those
with <filename>oid</filename>s 17321 through 17325. The values you with <filename>oid</filename>s 277970 through 277974. The values you
get will probably be different, and you should substitute them for the get will probably be different, and you should substitute them for the
values below. We will do this with a select statement. values below. We will do this with a select statement.
</para> </para>
...@@ -455,13 +418,13 @@ CREATE OPERATOR = ( ...@@ -455,13 +418,13 @@ CREATE OPERATOR = (
<para> <para>
Now we're ready to update <filename>pg_amop</filename> with our new Now we're ready to update <filename>pg_amop</filename> with our new
operator class. The most important thing in this entire discussion operator class. The most important thing in this entire discussion
is that the operators are ordered, from less equal through greater is that the operators are ordered, from less than through greater
equal, in <filename>pg_amop</filename>. We add the instances we need: than, in <filename>pg_amop</filename>. We add the instances we need:
<programlisting> <programlisting>
INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy)
SELECT am.oid, opcl.oid, c.opoid, 1 SELECT am.oid, opcl.oid, c.opoid, 1
FROM pg_am am, pg_opclass opcl, complex_abs_ops_tmp c FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
WHERE amname = 'btree' AND WHERE amname = 'btree' AND
opcname = 'complex_abs_ops' AND opcname = 'complex_abs_ops' AND
c.oprname = '<'; c.oprname = '<';
...@@ -493,15 +456,13 @@ CREATE OPERATOR = ( ...@@ -493,15 +456,13 @@ CREATE OPERATOR = (
SELECT oid, proname FROM pg_proc SELECT oid, proname FROM pg_proc
WHERE proname = 'complex_abs_cmp'; WHERE proname = 'complex_abs_cmp';
+------+-----------------+ oid | proname
|oid | proname | --------+-----------------
+------+-----------------+ 277997 | complex_abs_cmp
|17328 | complex_abs_cmp | (1 row)
+------+-----------------+
</programlisting> </programlisting>
(Again, your <filename>oid</filename> number will probably be different (Again, your <filename>oid</filename> number will probably be different.)
and you should substitute the value you see for the value below.)
We can add the new instance as follows: We can add the new instance as follows:
<programlisting> <programlisting>
...@@ -515,76 +476,8 @@ CREATE OPERATOR = ( ...@@ -515,76 +476,8 @@ CREATE OPERATOR = (
</para> </para>
<para> <para>
Now we need to add a hashing strategy to allow the type to be indexed. And we're done! (Whew.) It should now be possible to create
We do this by using another type in pg_am but we reuse the same ops. and use btree indexes on <filename>complex</filename> columns.
<programlisting>
INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy)
SELECT am.oid, opcl.oid, c.opoid, 1
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 = 'scalarltsel'::regproc, oprjoin = 'scalarltjoinsel'
WHERE oprname = '<' AND
oprleft = oprright AND
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
UPDATE pg_operator
SET oprrest = 'scalarltsel'::regproc, oprjoin = 'scalarltjoinsel'
WHERE oprname = '<=' AND
oprleft = oprright AND
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
UPDATE pg_operator
SET oprrest = 'scalargtsel'::regproc, oprjoin = 'scalargtjoinsel'
WHERE oprname = '>' AND
oprleft = oprright AND
oprleft = (SELECT oid FROM pg_type WHERE typname = 'complex_abs');
UPDATE pg_operator
SET oprrest = 'scalargtsel'::regproc, oprjoin = 'scalargtjoinsel'
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> </para>
</chapter> </chapter>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment