mirror of https://github.com/postgres/postgres
documentation (xindex.sgml should be rewritten), need to teach pg_dump about it, need to update contrib modules that currently build pg_opclass entries by hand. Original patch by Bill Studenmund, grammar adjustments and general update for 7.3 by Tom Lane.REL7_3_STABLE
parent
b9459c6adb
commit
ea4686e3e1
@ -0,0 +1,316 @@ |
||||
<!-- |
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_opclass.sgml,v 1.1 2002/07/29 22:14:10 tgl Exp $ |
||||
PostgreSQL documentation |
||||
--> |
||||
|
||||
<refentry id="SQL-CREATEOPCLASS"> |
||||
<refmeta> |
||||
<refentrytitle id="sql-createopclass-title">CREATE OPERATOR CLASS</refentrytitle> |
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||
</refmeta> |
||||
<refnamediv> |
||||
<refname> |
||||
CREATE OPERATOR CLASS |
||||
</refname> |
||||
<refpurpose> |
||||
define a new operator class for indexes |
||||
</refpurpose> |
||||
</refnamediv> |
||||
<refsynopsisdiv> |
||||
<refsynopsisdivinfo> |
||||
<date>2002-07-28</date> |
||||
</refsynopsisdivinfo> |
||||
<synopsis> |
||||
CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAULT ] FOR TYPE <replaceable class="parameter">data_type</replaceable> USING <replaceable class="parameter">access_method</replaceable> AS |
||||
{ OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_id</replaceable> [ ( <replaceable class="parameter">type</replaceable>, <replaceable class="parameter">type</replaceable> ) ] [ RECHECK ] |
||||
| FUNCTION <replaceable class="parameter">support_number</replaceable> <replaceable class="parameter">func_name</replaceable> ( <replaceable class="parameter">parameter_types</replaceable> ) |
||||
| STORAGE <replaceable class="parameter">storage_type</replaceable> |
||||
} [, ... ] |
||||
</synopsis> |
||||
|
||||
<refsect2 id="R2-SQL-CREATEOPCLASS-1"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
Inputs |
||||
</title> |
||||
<para> |
||||
|
||||
<variablelist> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">name</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name of the operator class to be created. |
||||
The name may be schema-qualified. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><literal>DEFAULT</></term> |
||||
<listitem> |
||||
<para> |
||||
If present, the operator class will become the default index |
||||
operator class for its datatype. At most one operator class |
||||
can be the default for a specific datatype and access method. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">data_type</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The column datatype that this operator class is for. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">access_method</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name of the index access method this operator class is for. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">strategy_number</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The index access method's strategy number for an operator associated |
||||
with the operator class. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">operator_id</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The identifier (optionally schema-qualified) of an operator associated |
||||
with the operator class. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">type</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The input datatype(s) of an operator, or <literal>NONE</> to |
||||
signify a left-unary or right-unary operator. The input datatypes |
||||
may be omitted in the normal case where they are the same as the |
||||
operator class's datatype. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><literal>RECHECK</></term> |
||||
<listitem> |
||||
<para> |
||||
If present, the index is <quote>lossy</> for this operator, |
||||
and so the tuples retrieved using the index must be rechecked |
||||
to verify that they actually satisfy the qualification clause |
||||
involving this operator. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">support_number</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The index access method's support procedure number for a function |
||||
associated with the operator class. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">func_name</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name (optionally schema-qualified) of a function that is |
||||
an index access method support procedure for the operator class. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">parameter_types</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The parameter datatype(s) of the function. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">storage_type</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The datatype actually stored in the index. Normally this is the |
||||
same as the column datatype, but some index access methods (only |
||||
GIST at this writing) allow it to be different. The |
||||
<literal>STORAGE</> clause must be omitted unless the index access |
||||
method allows a different type to be used. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</para> |
||||
</refsect2> |
||||
|
||||
<refsect2 id="R2-SQL-CREATEOPCLASS-2"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
Outputs |
||||
</title> |
||||
<para> |
||||
<variablelist> |
||||
<varlistentry> |
||||
<term><computeroutput> |
||||
CREATE OPERATOR CLASS |
||||
</computeroutput></term> |
||||
<listitem> |
||||
<para> |
||||
Message returned if the operator class is successfully created. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</para> |
||||
</refsect2> |
||||
</refsynopsisdiv> |
||||
|
||||
<refsect1 id="R1-SQL-CREATEOPCLASS-1"> |
||||
<refsect1info> |
||||
<date>2002-07-28</date> |
||||
</refsect1info> |
||||
<title> |
||||
Description |
||||
</title> |
||||
<para> |
||||
<command>CREATE OPERATOR CLASS</command> defines a new operator class, |
||||
<replaceable class="parameter">name</replaceable>. |
||||
</para> |
||||
<para> |
||||
An operator class defines how a particular datatype can be used with |
||||
an index. The operator class specifies that certain operators will fill |
||||
particular roles or <quote>strategies</> for this datatype and this |
||||
access method. The operator class also specifies the support procedures to |
||||
be used by |
||||
the index access method when the operator class is selected for an |
||||
index column. All the operators and functions used by an operator |
||||
class must be defined before the operator class is created. |
||||
</para> |
||||
|
||||
<para> |
||||
If a schema name is given then the operator class is created in the |
||||
specified schema. Otherwise it is created in the current schema (the one |
||||
at the front of the search path; see <literal>CURRENT_SCHEMA()</>). |
||||
Two operator classes in the same schema can have the same name only if they |
||||
are for different index access methods. |
||||
</para> |
||||
<para> |
||||
The user who defines an operator class becomes its owner. The user |
||||
must own the datatype for which the operator class is being defined, |
||||
and must have execute permission for all referenced operators and functions. |
||||
</para> |
||||
|
||||
<para> |
||||
<command>CREATE OPERATOR CLASS</command> does not presently check |
||||
whether the class definition includes all the operators and functions |
||||
required by the index access method. It is the user's |
||||
responsibility to define a valid operator class. |
||||
</para> |
||||
|
||||
<para> |
||||
Refer to the chapter on interfacing extensions to indexes in the |
||||
<citetitle>PostgreSQL Programmer's Guide</citetitle> |
||||
for further information. |
||||
</para> |
||||
|
||||
<refsect2 id="R2-SQL-CREATEOPCLASS-3"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
Notes |
||||
</title> |
||||
<para> |
||||
Refer to |
||||
<xref linkend="sql-dropopclass" endterm="sql-dropopclass-title"> |
||||
to delete user-defined operator classes from a database. |
||||
</para> |
||||
</refsect2> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="R1-SQL-CREATEOPCLASS-2"> |
||||
<title> |
||||
Usage |
||||
</title> |
||||
<para> |
||||
The following example command defines a GiST index operator class |
||||
for datatype <literal>_int4</> (array of int4). See |
||||
<filename>contrib/intarray/</> for the complete example. |
||||
</para> |
||||
|
||||
<programlisting> |
||||
CREATE OPERATOR CLASS gist__int_ops |
||||
DEFAULT FOR TYPE _int4 USING gist AS |
||||
OPERATOR 3 &&, |
||||
OPERATOR 6 = RECHECK, |
||||
OPERATOR 7 @, |
||||
OPERATOR 8 ~, |
||||
OPERATOR 20 @@ (_int4, query_int), |
||||
FUNCTION 1 g_int_consistent (opaque, _int4, int4), |
||||
FUNCTION 2 g_int_union (bytea, opaque), |
||||
FUNCTION 3 g_int_compress (opaque), |
||||
FUNCTION 4 g_int_decompress (opaque), |
||||
FUNCTION 5 g_int_penalty (opaque, opaque, opaque), |
||||
FUNCTION 6 g_int_picksplit (opaque, opaque), |
||||
FUNCTION 7 g_int_same (_int4, _int4, opaque); |
||||
</programlisting> |
||||
|
||||
<para> |
||||
The <literal>OPERATOR</>, <literal>FUNCTION</>, and <literal>STORAGE</> |
||||
clauses may appear in any order. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="R1-SQL-CREATEOPCLASS-3"> |
||||
<title> |
||||
Compatibility |
||||
</title> |
||||
|
||||
<refsect2 id="R2-SQL-CREATEOPCLASS-4"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
SQL92 |
||||
</title> |
||||
|
||||
<para> |
||||
<command>CREATE OPERATOR CLASS</command> |
||||
is a <productname>PostgreSQL</productname> extension. |
||||
There is no <command>CREATE OPERATOR CLASS</command> |
||||
statement in <acronym>SQL92</acronym>. |
||||
</para> |
||||
</refsect2> |
||||
</refsect1> |
||||
</refentry> |
||||
|
||||
<!-- 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: |
||||
--> |
@ -0,0 +1,184 @@ |
||||
<!-- |
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_opclass.sgml,v 1.1 2002/07/29 22:14:10 tgl Exp $ |
||||
PostgreSQL documentation |
||||
--> |
||||
|
||||
<refentry id="SQL-DROPOPCLASS"> |
||||
<refmeta> |
||||
<refentrytitle id="SQL-DROPOPCLASS-TITLE">DROP OPERATOR CLASS</refentrytitle> |
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||
</refmeta> |
||||
<refnamediv> |
||||
<refname> |
||||
DROP OPERATOR CLASS |
||||
</refname> |
||||
<refpurpose> |
||||
remove a user-defined operator class |
||||
</refpurpose> |
||||
</refnamediv> |
||||
|
||||
<refsynopsisdiv> |
||||
<refsynopsisdivinfo> |
||||
<date>2002-07-28</date> |
||||
</refsynopsisdivinfo> |
||||
<synopsis> |
||||
DROP OPERATOR CLASS <replaceable class="PARAMETER">name</replaceable> USING <replaceable class="PARAMETER">access_method</replaceable> [ CASCADE | RESTRICT ] |
||||
</synopsis> |
||||
|
||||
<refsect2 id="R2-SQL-DROPOPCLASS-1"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
Inputs |
||||
</title> |
||||
<para> |
||||
<variablelist> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">name</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name (optionally schema-qualified) of an existing operator class. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">access_method</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name of the index access method the operator class is for. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term>CASCADE</term> |
||||
<listitem> |
||||
<para> |
||||
Automatically drop objects that depend on the operator class. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
<varlistentry> |
||||
<term>RESTRICT</term> |
||||
<listitem> |
||||
<para> |
||||
Refuse to drop the operator class if there are any dependent objects. |
||||
This is the default. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</para> |
||||
</refsect2> |
||||
|
||||
<refsect2 id="R2-SQL-DROPOPCLASS-2"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
Outputs |
||||
</title> |
||||
<para> |
||||
|
||||
<variablelist> |
||||
<varlistentry> |
||||
<term><computeroutput> |
||||
DROP OPERATOR CLASS |
||||
</computeroutput></term> |
||||
<listitem> |
||||
<para> |
||||
The message returned if the command is successful. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</para> |
||||
</refsect2> |
||||
</refsynopsisdiv> |
||||
|
||||
<refsect1 id="R1-SQL-DROPOPCLASS-1"> |
||||
<refsect1info> |
||||
<date>2002-07-28</date> |
||||
</refsect1info> |
||||
<title> |
||||
Description |
||||
</title> |
||||
<para> |
||||
<command>DROP OPERATOR CLASS</command> drops an existing operator class |
||||
from the database. |
||||
To execute this command you must be the owner of the operator class. |
||||
</para> |
||||
|
||||
<refsect2 id="R2-SQL-DROPOPCLASS-3"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
Notes |
||||
</title> |
||||
<para> |
||||
The <command>DROP OPERATOR CLASS</command> statement is a |
||||
<productname>PostgreSQL</productname> |
||||
language extension. |
||||
</para> |
||||
<para> |
||||
Refer to |
||||
<xref linkend="sql-createopclass" endterm="sql-createopclass-title"> |
||||
for information on how to create operator classes. |
||||
</para> |
||||
</refsect2> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="R1-SQL-DROPOPCLASS-2"> |
||||
<title> |
||||
Usage |
||||
</title> |
||||
<para> |
||||
Remove btree operator class <literal>widget_ops</literal>: |
||||
|
||||
<programlisting> |
||||
DROP OPERATOR CLASS widget_ops USING btree; |
||||
</programlisting> |
||||
|
||||
This command will not execute if there are any existing indexes |
||||
that use the operator class. Add <literal>CASCADE</> to drop |
||||
such indexes along with the operator class. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="R1-SQL-DROPOPCLASS-3"> |
||||
<title> |
||||
Compatibility |
||||
</title> |
||||
|
||||
<refsect2 id="R2-SQL-DROPOPCLASS-4"> |
||||
<refsect2info> |
||||
<date>2002-07-28</date> |
||||
</refsect2info> |
||||
<title> |
||||
SQL92 |
||||
</title> |
||||
<para> |
||||
There is no <command>DROP OPERATOR CLASS</command> in |
||||
<acronym>SQL92</acronym>. |
||||
</para> |
||||
</refsect2> |
||||
</refsect1> |
||||
</refentry> |
||||
|
||||
<!-- 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: |
||||
--> |
@ -0,0 +1,639 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* opclasscmds.c |
||||
* |
||||
* Routines for opclass manipulation commands |
||||
* |
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.1 2002/07/29 22:14:10 tgl Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/genam.h" |
||||
#include "access/heapam.h" |
||||
#include "catalog/catname.h" |
||||
#include "catalog/dependency.h" |
||||
#include "catalog/indexing.h" |
||||
#include "catalog/namespace.h" |
||||
#include "catalog/pg_am.h" |
||||
#include "catalog/pg_amop.h" |
||||
#include "catalog/pg_amproc.h" |
||||
#include "catalog/pg_opclass.h" |
||||
#include "commands/defrem.h" |
||||
#include "miscadmin.h" |
||||
#include "parser/parse_func.h" |
||||
#include "parser/parse_oper.h" |
||||
#include "parser/parse_type.h" |
||||
#include "utils/acl.h" |
||||
#include "utils/builtins.h" |
||||
#include "utils/fmgroids.h" |
||||
#include "utils/lsyscache.h" |
||||
#include "utils/syscache.h" |
||||
|
||||
|
||||
static void storeOperators(Oid opclassoid, int numOperators, |
||||
Oid *operators, bool *recheck); |
||||
static void storeProcedures(Oid opclassoid, int numProcs, Oid *procedures); |
||||
|
||||
|
||||
/*
|
||||
* DefineOpClass |
||||
* Define a new index operator class. |
||||
*/ |
||||
void |
||||
DefineOpClass(CreateOpClassStmt *stmt) |
||||
{ |
||||
char *opcname; /* name of opclass we're creating */ |
||||
Oid amoid, /* our AM's oid */ |
||||
typeoid, /* indexable datatype oid */ |
||||
storageoid, /* storage datatype oid, if any */ |
||||
namespaceoid, /* namespace to create opclass in */ |
||||
opclassoid; /* oid of opclass we create */ |
||||
int numOperators, /* amstrategies value */ |
||||
numProcs; /* amsupport value */ |
||||
Oid *operators, /* oids of operators, by strategy num */ |
||||
*procedures; /* oids of support procs */ |
||||
bool *recheck; /* do operators need recheck */ |
||||
List *iteml; |
||||
Relation rel; |
||||
HeapTuple tup; |
||||
Datum values[Natts_pg_opclass]; |
||||
char nulls[Natts_pg_opclass]; |
||||
AclResult aclresult; |
||||
NameData opcName; |
||||
int i; |
||||
ObjectAddress myself, |
||||
referenced; |
||||
|
||||
/* Convert list of names to a name and namespace */ |
||||
namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname, |
||||
&opcname); |
||||
|
||||
/* Check we have creation rights in target namespace */ |
||||
aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE); |
||||
if (aclresult != ACLCHECK_OK) |
||||
aclcheck_error(aclresult, get_namespace_name(namespaceoid)); |
||||
|
||||
/* Get necessary info about access method */ |
||||
tup = SearchSysCache(AMNAME, |
||||
CStringGetDatum(stmt->amname), |
||||
0, 0, 0); |
||||
if (!HeapTupleIsValid(tup)) |
||||
elog(ERROR, "DefineOpClass: access method \"%s\" not found", |
||||
stmt->amname); |
||||
|
||||
amoid = HeapTupleGetOid(tup); |
||||
numOperators = ((Form_pg_am) GETSTRUCT(tup))->amstrategies; |
||||
numProcs = ((Form_pg_am) GETSTRUCT(tup))->amsupport; |
||||
|
||||
/* XXX Should we make any privilege check against the AM? */ |
||||
|
||||
ReleaseSysCache(tup); |
||||
|
||||
/* Look up the datatype */ |
||||
typeoid = typenameTypeId(stmt->datatype); |
||||
|
||||
/* Check we have ownership of the datatype */ |
||||
if (!pg_type_ownercheck(typeoid, GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, format_type_be(typeoid)); |
||||
|
||||
/* Storage datatype is optional */ |
||||
storageoid = InvalidOid; |
||||
|
||||
/*
|
||||
* Create work arrays to hold info about operators and procedures. |
||||
* We do this mainly so that we can detect duplicate strategy |
||||
* numbers and support-proc numbers. |
||||
*/ |
||||
operators = (Oid *) palloc(sizeof(Oid) * numOperators); |
||||
MemSet(operators, 0, sizeof(Oid) * numOperators); |
||||
procedures = (Oid *) palloc(sizeof(Oid) * numProcs); |
||||
MemSet(procedures, 0, sizeof(Oid) * numProcs); |
||||
recheck = (bool *) palloc(sizeof(bool) * numOperators); |
||||
MemSet(recheck, 0, sizeof(bool) * numOperators); |
||||
|
||||
/*
|
||||
* Scan the "items" list to obtain additional info. |
||||
*/ |
||||
foreach(iteml, stmt->items) |
||||
{ |
||||
CreateOpClassItem *item = lfirst(iteml); |
||||
Oid operOid; |
||||
Oid funcOid; |
||||
AclResult aclresult; |
||||
|
||||
Assert(IsA(item, CreateOpClassItem)); |
||||
switch (item->itemtype) |
||||
{ |
||||
case OPCLASS_ITEM_OPERATOR: |
||||
if (item->number <= 0 || item->number > numOperators) |
||||
elog(ERROR, "DefineOpClass: invalid operator number %d," |
||||
" must be between 1 and %d", |
||||
item->number, numOperators); |
||||
if (operators[item->number - 1] != InvalidOid) |
||||
elog(ERROR, "DefineOpClass: operator number %d appears more than once", |
||||
item->number); |
||||
if (item->args != NIL) |
||||
{ |
||||
TypeName *typeName1 = (TypeName *) lfirst(item->args); |
||||
TypeName *typeName2 = (TypeName *) lsecond(item->args); |
||||
|
||||
operOid = LookupOperNameTypeNames(item->name, |
||||
typeName1, typeName2, |
||||
"DefineOpClass"); |
||||
/* No need to check for error */ |
||||
} |
||||
else |
||||
{ |
||||
/* Default to binary op on input datatype */ |
||||
operOid = LookupOperName(item->name, typeoid, typeoid); |
||||
if (!OidIsValid(operOid)) |
||||
elog(ERROR, "DefineOpClass: Operator '%s' for types '%s' and '%s' does not exist", |
||||
NameListToString(item->name), |
||||
format_type_be(typeoid), |
||||
format_type_be(typeoid)); |
||||
} |
||||
/* Caller must have execute permission on operators */ |
||||
funcOid = get_opcode(operOid); |
||||
aclresult = pg_proc_aclcheck(funcOid, GetUserId(), |
||||
ACL_EXECUTE); |
||||
if (aclresult != ACLCHECK_OK) |
||||
aclcheck_error(aclresult, get_func_name(funcOid)); |
||||
operators[item->number - 1] = operOid; |
||||
recheck[item->number - 1] = item->recheck; |
||||
break; |
||||
case OPCLASS_ITEM_FUNCTION: |
||||
if (item->number <= 0 || item->number > numProcs) |
||||
elog(ERROR, "DefineOpClass: invalid procedure number %d," |
||||
" must be between 1 and %d", |
||||
item->number, numProcs); |
||||
if (procedures[item->number - 1] != InvalidOid) |
||||
elog(ERROR, "DefineOpClass: procedure number %d appears more than once", |
||||
item->number); |
||||
funcOid = LookupFuncNameTypeNames(item->name, item->args, |
||||
true, "DefineOpClass"); |
||||
/* Caller must have execute permission on functions */ |
||||
aclresult = pg_proc_aclcheck(funcOid, GetUserId(), |
||||
ACL_EXECUTE); |
||||
if (aclresult != ACLCHECK_OK) |
||||
aclcheck_error(aclresult, get_func_name(funcOid)); |
||||
procedures[item->number - 1] = funcOid; |
||||
break; |
||||
case OPCLASS_ITEM_STORAGETYPE: |
||||
if (OidIsValid(storageoid)) |
||||
elog(ERROR, "DefineOpClass: storage type specified more than once"); |
||||
storageoid = typenameTypeId(item->storedtype); |
||||
break; |
||||
default: |
||||
elog(ERROR, "DefineOpClass: bogus item type %d", |
||||
item->itemtype); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* If storagetype is specified, make sure it's legal. |
||||
*/ |
||||
if (OidIsValid(storageoid)) |
||||
{ |
||||
/* Just drop the spec if same as column datatype */ |
||||
if (storageoid == typeoid) |
||||
storageoid = InvalidOid; |
||||
else |
||||
{ |
||||
/*
|
||||
* Currently, only GiST allows storagetype different from |
||||
* datatype. This hardcoded test should be eliminated in |
||||
* favor of adding another boolean column to pg_am ... |
||||
*/ |
||||
if (amoid != GIST_AM_OID) |
||||
elog(ERROR, "Storage type may not be different from datatype for access method %s", |
||||
stmt->amname); |
||||
} |
||||
} |
||||
|
||||
rel = heap_openr(OperatorClassRelationName, RowExclusiveLock); |
||||
|
||||
/*
|
||||
* Make sure there is no existing opclass of this name (this is |
||||
* just to give a more friendly error message than "duplicate key"). |
||||
*/ |
||||
if (SearchSysCacheExists(CLAAMNAMENSP, |
||||
ObjectIdGetDatum(amoid), |
||||
CStringGetDatum(opcname), |
||||
ObjectIdGetDatum(namespaceoid), |
||||
0)) |
||||
elog(ERROR, "Operator class \"%s\" already exists for access method \"%s\"", |
||||
opcname, stmt->amname); |
||||
|
||||
/*
|
||||
* If we are creating a default opclass, check there isn't one already. |
||||
* (XXX should we restrict this test to visible opclasses?) |
||||
*/ |
||||
if (stmt->isDefault) |
||||
{ |
||||
ScanKeyData skey[1]; |
||||
SysScanDesc scan; |
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0, |
||||
Anum_pg_opclass_opcamid, F_OIDEQ, |
||||
ObjectIdGetDatum(amoid)); |
||||
|
||||
scan = systable_beginscan(rel, OpclassAmNameNspIndex, true, |
||||
SnapshotNow, 1, skey); |
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(scan))) |
||||
{ |
||||
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup); |
||||
|
||||
if (opclass->opcintype == typeoid && opclass->opcdefault) |
||||
elog(ERROR, "Can't add class \"%s\" as default for type %s" |
||||
"\n\tclass \"%s\" already is the default", |
||||
opcname, |
||||
TypeNameToString(stmt->datatype), |
||||
NameStr(opclass->opcname)); |
||||
} |
||||
|
||||
systable_endscan(scan); |
||||
} |
||||
|
||||
/*
|
||||
* Okay, let's create the pg_opclass entry. |
||||
*/ |
||||
for (i = 0; i < Natts_pg_opclass; ++i) |
||||
{ |
||||
nulls[i] = ' '; |
||||
values[i] = (Datum) NULL; /* redundant, but safe */ |
||||
} |
||||
|
||||
i = 0; |
||||
values[i++] = ObjectIdGetDatum(amoid); /* opcamid */ |
||||
namestrcpy(&opcName, opcname); |
||||
values[i++] = NameGetDatum(&opcName); /* opcname */ |
||||
values[i++] = ObjectIdGetDatum(namespaceoid); /* opcnamespace */ |
||||
values[i++] = Int32GetDatum(GetUserId()); /* opcowner */ |
||||
values[i++] = ObjectIdGetDatum(typeoid); /* opcintype */ |
||||
values[i++] = BoolGetDatum(stmt->isDefault); /* opcdefault */ |
||||
values[i++] = ObjectIdGetDatum(storageoid); /* opckeytype */ |
||||
|
||||
tup = heap_formtuple(rel->rd_att, values, nulls); |
||||
|
||||
opclassoid = simple_heap_insert(rel, tup); |
||||
|
||||
if (RelationGetForm(rel)->relhasindex) |
||||
{ |
||||
Relation idescs[Num_pg_opclass_indices]; |
||||
|
||||
CatalogOpenIndices(Num_pg_opclass_indices, Name_pg_opclass_indices, |
||||
idescs); |
||||
CatalogIndexInsert(idescs, Num_pg_opclass_indices, rel, tup); |
||||
CatalogCloseIndices(Num_pg_opclass_indices, idescs); |
||||
} |
||||
|
||||
heap_freetuple(tup); |
||||
|
||||
/*
|
||||
* Now add tuples to pg_amop and pg_amproc tying in the |
||||
* operators and functions. |
||||
*/ |
||||
storeOperators(opclassoid, numOperators, operators, recheck); |
||||
storeProcedures(opclassoid, numProcs, procedures); |
||||
|
||||
/*
|
||||
* Create dependencies. Note: we do not create a dependency link to |
||||
* the AM, because we don't currently support DROP ACCESS METHOD. |
||||
*/ |
||||
myself.classId = RelationGetRelid(rel); |
||||
myself.objectId = opclassoid; |
||||
myself.objectSubId = 0; |
||||
|
||||
/* dependency on namespace */ |
||||
referenced.classId = get_system_catalog_relid(NamespaceRelationName); |
||||
referenced.objectId = namespaceoid; |
||||
referenced.objectSubId = 0; |
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
||||
|
||||
/* dependency on indexed datatype */ |
||||
referenced.classId = RelOid_pg_type; |
||||
referenced.objectId = typeoid; |
||||
referenced.objectSubId = 0; |
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
||||
|
||||
/* dependency on storage datatype */ |
||||
if (OidIsValid(storageoid)) |
||||
{ |
||||
referenced.classId = RelOid_pg_type; |
||||
referenced.objectId = storageoid; |
||||
referenced.objectSubId = 0; |
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
||||
} |
||||
|
||||
/* dependencies on operators */ |
||||
referenced.classId = get_system_catalog_relid(OperatorRelationName); |
||||
for (i = 0; i < numOperators; i++) |
||||
{ |
||||
if (operators[i] == InvalidOid) |
||||
continue; |
||||
referenced.objectId = operators[i]; |
||||
referenced.objectSubId = 0; |
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
||||
} |
||||
|
||||
/* dependencies on procedures */ |
||||
for (i = 0; i < numProcs; i++) |
||||
{ |
||||
if (procedures[i] == InvalidOid) |
||||
continue; |
||||
referenced.classId = RelOid_pg_proc; |
||||
referenced.objectId = procedures[i]; |
||||
referenced.objectSubId = 0; |
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); |
||||
} |
||||
|
||||
heap_close(rel, RowExclusiveLock); |
||||
} |
||||
|
||||
/*
|
||||
* Dump the operators to pg_amop |
||||
*/ |
||||
static void |
||||
storeOperators(Oid opclassoid, int numOperators, |
||||
Oid *operators, bool *recheck) |
||||
{ |
||||
Relation rel; |
||||
Datum values[Natts_pg_amop]; |
||||
char nulls[Natts_pg_amop]; |
||||
HeapTuple tup; |
||||
int i, j; |
||||
|
||||
rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock); |
||||
|
||||
for (j = 0; j < numOperators; j++) |
||||
{ |
||||
if (operators[j] == InvalidOid) |
||||
continue; |
||||
|
||||
for (i = 0; i < Natts_pg_amop; ++i) |
||||
{ |
||||
nulls[i] = ' '; |
||||
values[i] = (Datum) NULL; |
||||
} |
||||
|
||||
i = 0; |
||||
values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */ |
||||
values[i++] = Int16GetDatum(j + 1); /* amopstrategy */ |
||||
values[i++] = BoolGetDatum(recheck[j]); /* amopreqcheck */ |
||||
values[i++] = ObjectIdGetDatum(operators[j]); /* amopopr */ |
||||
|
||||
tup = heap_formtuple(rel->rd_att, values, nulls); |
||||
|
||||
simple_heap_insert(rel, tup); |
||||
|
||||
if (RelationGetForm(rel)->relhasindex) |
||||
{ |
||||
Relation idescs[Num_pg_amop_indices]; |
||||
|
||||
CatalogOpenIndices(Num_pg_amop_indices, Name_pg_amop_indices, |
||||
idescs); |
||||
CatalogIndexInsert(idescs, Num_pg_amop_indices, rel, tup); |
||||
CatalogCloseIndices(Num_pg_amop_indices, idescs); |
||||
} |
||||
heap_freetuple(tup); |
||||
} |
||||
|
||||
heap_close(rel, RowExclusiveLock); |
||||
} |
||||
|
||||
/*
|
||||
* Dump the procedures (support routines) to pg_amproc |
||||
*/ |
||||
static void |
||||
storeProcedures(Oid opclassoid, int numProcs, Oid *procedures) |
||||
{ |
||||
Relation rel; |
||||
Datum values[Natts_pg_amproc]; |
||||
char nulls[Natts_pg_amproc]; |
||||
HeapTuple tup; |
||||
int i, j; |
||||
|
||||
rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock); |
||||
|
||||
for (j = 0; j < numProcs; j++) |
||||
{ |
||||
if (procedures[j] == InvalidOid) |
||||
continue; |
||||
|
||||
for (i = 0; i < Natts_pg_amproc; ++i) |
||||
{ |
||||
nulls[i] = ' '; |
||||
values[i] = (Datum) NULL; |
||||
} |
||||
|
||||
i = 0; |
||||
values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */ |
||||
values[i++] = Int16GetDatum(j + 1); /* amprocnum */ |
||||
values[i++] = ObjectIdGetDatum(procedures[j]); /* amproc */ |
||||
|
||||
tup = heap_formtuple(rel->rd_att, values, nulls); |
||||
|
||||
simple_heap_insert(rel, tup); |
||||
|
||||
if (RelationGetForm(rel)->relhasindex) |
||||
{ |
||||
Relation idescs[Num_pg_amproc_indices]; |
||||
|
||||
CatalogOpenIndices(Num_pg_amproc_indices, Name_pg_amproc_indices, |
||||
idescs); |
||||
CatalogIndexInsert(idescs, Num_pg_amproc_indices, rel, tup); |
||||
CatalogCloseIndices(Num_pg_amproc_indices, idescs); |
||||
} |
||||
heap_freetuple(tup); |
||||
} |
||||
|
||||
heap_close(rel, RowExclusiveLock); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* RemoveOpClass |
||||
* Deletes an opclass. |
||||
*/ |
||||
void |
||||
RemoveOpClass(RemoveOpClassStmt *stmt) |
||||
{ |
||||
Oid amID, opcID; |
||||
char *catalogname; |
||||
char *schemaname = NULL; |
||||
char *opcname = NULL; |
||||
HeapTuple tuple; |
||||
ObjectAddress object; |
||||
|
||||
/*
|
||||
* Get the access method's OID. |
||||
*/ |
||||
amID = GetSysCacheOid(AMNAME, |
||||
CStringGetDatum(stmt->amname), |
||||
0, 0, 0); |
||||
if (!OidIsValid(amID)) |
||||
elog(ERROR, "RemoveOpClass: access method \"%s\" not found", |
||||
stmt->amname); |
||||
|
||||
/*
|
||||
* Look up the opclass. |
||||
*/ |
||||
|
||||
/* deconstruct the name list */ |
||||
switch (length(stmt->opclassname)) |
||||
{ |
||||
case 1: |
||||
opcname = strVal(lfirst(stmt->opclassname)); |
||||
break; |
||||
case 2: |
||||
schemaname = strVal(lfirst(stmt->opclassname)); |
||||
opcname = strVal(lsecond(stmt->opclassname)); |
||||
break; |
||||
case 3: |
||||
catalogname = strVal(lfirst(stmt->opclassname)); |
||||
schemaname = strVal(lsecond(stmt->opclassname)); |
||||
opcname = strVal(lfirst(lnext(lnext(stmt->opclassname)))); |
||||
/*
|
||||
* We check the catalog name and then ignore it. |
||||
*/ |
||||
if (strcmp(catalogname, DatabaseName) != 0) |
||||
elog(ERROR, "Cross-database references are not implemented"); |
||||
break; |
||||
default: |
||||
elog(ERROR, "Improper opclass name (too many dotted names): %s", |
||||
NameListToString(stmt->opclassname)); |
||||
break; |
||||
} |
||||
|
||||
if (schemaname) |
||||
{ |
||||
/* Look in specific schema only */ |
||||
Oid namespaceId; |
||||
|
||||
namespaceId = GetSysCacheOid(NAMESPACENAME, |
||||
CStringGetDatum(schemaname), |
||||
0, 0, 0); |
||||
if (!OidIsValid(namespaceId)) |
||||
elog(ERROR, "Namespace \"%s\" does not exist", |
||||
schemaname); |
||||
tuple = SearchSysCache(CLAAMNAMENSP, |
||||
ObjectIdGetDatum(amID), |
||||
PointerGetDatum(opcname), |
||||
ObjectIdGetDatum(namespaceId), |
||||
0); |
||||
} |
||||
else |
||||
{ |
||||
/* Unqualified opclass name, so search the search path */ |
||||
opcID = OpclassnameGetOpcid(amID, opcname); |
||||
if (!OidIsValid(opcID)) |
||||
elog(ERROR, "RemoveOpClass: operator class \"%s\" not supported by access method \"%s\"", |
||||
opcname, stmt->amname); |
||||
tuple = SearchSysCache(CLAOID, |
||||
ObjectIdGetDatum(opcID), |
||||
0, 0, 0); |
||||
} |
||||
|
||||
if (!HeapTupleIsValid(tuple)) |
||||
elog(ERROR, "RemoveOpClass: operator class \"%s\" not supported by access method \"%s\"", |
||||
NameListToString(stmt->opclassname), stmt->amname); |
||||
|
||||
opcID = HeapTupleGetOid(tuple); |
||||
|
||||
/* Permission check: must own opclass or its namespace */ |
||||
if (!pg_opclass_ownercheck(opcID, GetUserId()) && |
||||
!pg_namespace_ownercheck(((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace, |
||||
GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, |
||||
NameListToString(stmt->opclassname)); |
||||
|
||||
ReleaseSysCache(tuple); |
||||
|
||||
/*
|
||||
* Do the deletion |
||||
*/ |
||||
object.classId = get_system_catalog_relid(OperatorClassRelationName); |
||||
object.objectId = opcID; |
||||
object.objectSubId = 0; |
||||
|
||||
performDeletion(&object, stmt->behavior); |
||||
} |
||||
|
||||
/*
|
||||
* Guts of opclass deletion. |
||||
*/ |
||||
void |
||||
RemoveOpClassById(Oid opclassOid) |
||||
{ |
||||
Relation rel; |
||||
HeapTuple tup; |
||||
ScanKeyData skey[1]; |
||||
SysScanDesc scan; |
||||
|
||||
/*
|
||||
* First remove the pg_opclass entry itself. |
||||
*/ |
||||
rel = heap_openr(OperatorClassRelationName, RowExclusiveLock); |
||||
|
||||
tup = SearchSysCache(CLAOID, |
||||
ObjectIdGetDatum(opclassOid), |
||||
0, 0, 0); |
||||
if (!HeapTupleIsValid(tup)) /* should not happen */ |
||||
elog(ERROR, "RemoveOpClassById: couldn't find pg_class entry %u", |
||||
opclassOid); |
||||
|
||||
simple_heap_delete(rel, &tup->t_self); |
||||
|
||||
ReleaseSysCache(tup); |
||||
|
||||
heap_close(rel, RowExclusiveLock); |
||||
|
||||
/*
|
||||
* Remove associated entries in pg_amop. |
||||
*/ |
||||
ScanKeyEntryInitialize(&skey[0], 0, |
||||
Anum_pg_amop_amopclaid, F_OIDEQ, |
||||
ObjectIdGetDatum(opclassOid)); |
||||
|
||||
rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock); |
||||
|
||||
scan = systable_beginscan(rel, AccessMethodStrategyIndex, true, |
||||
SnapshotNow, 1, skey); |
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(scan))) |
||||
{ |
||||
simple_heap_delete(rel, &tup->t_self); |
||||
} |
||||
|
||||
systable_endscan(scan); |
||||
heap_close(rel, RowExclusiveLock); |
||||
|
||||
/*
|
||||
* Remove associated entries in pg_amproc. |
||||
*/ |
||||
ScanKeyEntryInitialize(&skey[0], 0, |
||||
Anum_pg_amproc_amopclaid, F_OIDEQ, |
||||
ObjectIdGetDatum(opclassOid)); |
||||
|
||||
rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock); |
||||
|
||||
scan = systable_beginscan(rel, AccessMethodProcedureIndex, true, |
||||
SnapshotNow, 1, skey); |
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(scan))) |
||||
{ |
||||
simple_heap_delete(rel, &tup->t_self); |
||||
} |
||||
|
||||
systable_endscan(scan); |
||||
heap_close(rel, RowExclusiveLock); |
||||
} |
Loading…
Reference in new issue