<!--
-$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
-->
<programlisting>
SELECT oid FROM pg_am WHERE amname = 'btree';
- +----+
- |oid |
- +----+
- |403 |
- +----+
+ oid
+-----
+ 403
+(1 row)
</programlisting>
We will use that <command>SELECT</command> in a <command>WHERE</command>
</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
for less than, less equal, and so on don't appear in
<filename>pg_am</filename>. Similarly, <filename>amsupport</filename>
</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
+ The next class of interest is <filename>pg_opclass</filename>. This class
+ exists only to associate an operator class name and perhaps a default type
+ with an operator class oid. Some existing opclasses are <filename>int2_ops,
+ int4_ops,</filename> and <filename>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 will be a foreign key in other classes, notably
+ <filename>pg_amop</filename>.
<programlisting>
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
FROM pg_opclass
WHERE opcname = 'complex_abs_ops';
- +------+-----------------+------------+
- |oid | opcname | opcdeftype |
- +------+-----------------+------------+
- |17314 | complex_abs_ops | 29058 |
- +------+-----------------+------------+
+ oid | opcname | opcdeftype
+--------+-----------------+------------
+ 277975 | complex_abs_ops | 277946
+(1 row)
</programlisting>
Note that the oid for your <filename>pg_opclass</filename> instance will
from the system later just like we got the oid of the type here.
</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>
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.
- 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:
<programlisting>
</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
operators are very similar. Refer to <filename>complex.c</filename>
or <filename>complex.source</filename> for the details.)
</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>
- First, note that operators for less-than, less-than-or equal, equal,
- 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 <, <=, =, >=,
- and >. 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 <
- 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.
+ There are some important things that are happening here.
</para>
<para>
- The other important point is that all the 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.
+ First, note that operators for less-than, less-than-or-equal, equal,
+ greater-than-or-equal, and greater-than for <filename>complex</filename>
+ are being defined. We can only have one operator named, say, = and
+ taking type <filename>complex</filename> for both operands. In this case
+ we don't have any other operator = for <filename>complex</filename>,
+ but if we were building a practical datatype we'd probably want = to
+ 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>
- <programlisting>
-CREATE FUNCTION complex_abs_eq(complex_abs, complex_abs)
- RETURNS bool
- AS 'PGROOT/tutorial/obj/complex.so'
- LANGUAGE 'c';
- </programlisting>
+ Second, although Postgres can cope with operators having
+ the same name as long as they have different input datatypes, C can only
+ cope with one global routine having a given name, period. So we shouldn't
+ name the C function something simple like <filename>abs_eq</filename>.
+ Usually it's a good practice to include the datatype name in the C
+ function name, so as not to conflict with functions for other datatypes.
</para>
<para>
- Now define the operators that use them. As noted, the operator names
- must be unique among all operators that take two <filename>int4</filename>
- operands. In order to see if the operator names listed below are taken,
- we can do a query on <filename>pg_operator</filename>:
-
- <programlisting>
- /*
- * this query uses the regular expression operator (~)
- * to find three-character operator names that end in
- * the character &
- */
- SELECT *
- FROM pg_operator
- WHERE oprname ~ '^..&$'::text;
- </programlisting>
+ Third, we could have made the Postgres name of the function
+ <filename>abs_eq</filename>, relying on Postgres to distinguish it
+ by input datatypes from any other Postgres function of the same name.
+ To keep the example simple, we make the function have the same names
+ at the C level and Postgres level.
+ </para>
+ <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>
- to see if your name is taken for the types you want. The important
- 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.
+ Now we are ready to define the operators:
<programlisting>
CREATE OPERATOR = (
- leftarg = complex_abs, rightarg = complex_abs,
+ leftarg = complex, rightarg = complex,
procedure = complex_abs_eq,
restrict = eqsel, join = eqjoinsel
)
</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">
- <title><filename>pg_amproc</filename> Schema</title>
- <titleabbrev><filename>pg_amproc</filename></titleabbrev>
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Attribute</entry>
- <entry>Description</entry>
- </row>
- </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>
+ The important
+ things here are the procedure names (which are the <acronym>C</acronym>
+ functions defined above) and the restriction and join selectivity
+ functions. You should just use the selectivity functions used in
+ the example (see <filename>complex.source</filename>).
+ Note that there
+ are different such functions for the less-than, equal, and greater-than
+ cases. These must be supplied, or the optimizer will be unable to
+ make effective use of the index.
</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
two <filename>complex</filename>es, and pick ours out:
INTO TABLE complex_ops_tmp
FROM pg_operator o, pg_type t
WHERE o.oprleft = t.oid and o.oprright = t.oid
- and t.typname = 'complex_abs';
-
- +------+---------+
- |oid | oprname |
- +------+---------+
- |17321 | < |
- +------+---------+
- |17322 | <= |
- +------+---------+
- |17323 | = |
- +------+---------+
- |17324 | >= |
- +------+---------+
- |17325 | > |
- +------+---------+
+ and t.typname = 'complex';
+
+ opoid | oprname
+--------+---------
+ 277963 | +
+ 277970 | <
+ 277971 | <=
+ 277972 | =
+ 277973 | >=
+ 277974 | >
+(6 rows)
</programlisting>
(Again, some of your <filename>oid</filename> numbers will almost
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
values below. We will do this with a select statement.
</para>
<para>
Now we're ready to update <filename>pg_amop</filename> with our new
operator class. The most important thing in this entire discussion
- is that the operators are ordered, from less equal through greater
- equal, in <filename>pg_amop</filename>. We add the instances we need:
+ is that the operators are ordered, from less than through greater
+ than, in <filename>pg_amop</filename>. We add the instances we need:
<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
+ FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
WHERE amname = 'btree' AND
opcname = 'complex_abs_ops' AND
c.oprname = '<';
SELECT oid, proname FROM pg_proc
WHERE proname = 'complex_abs_cmp';
- +------+-----------------+
- |oid | proname |
- +------+-----------------+
- |17328 | complex_abs_cmp |
- +------+-----------------+
+ oid | proname
+--------+-----------------
+ 277997 | complex_abs_cmp
+(1 row)
</programlisting>
- (Again, your <filename>oid</filename> number will probably be different
- and you should substitute the value you see for the value below.)
+ (Again, your <filename>oid</filename> number will probably be different.)
We can add the new instance as follows:
<programlisting>
</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 same ops.
-
- <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>
+ And we're done! (Whew.) It should now be possible to create
+ and use btree indexes on <filename>complex</filename> columns.
</para>
</chapter>