diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index a481500525..3b9b8b952e 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ @@ -16,6 +16,7 @@ Complete list of usable sgml source files in this directory. + @@ -45,6 +46,7 @@ Complete list of usable sgml source files in this directory. + @@ -70,6 +72,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/alter_opclass.sgml b/doc/src/sgml/ref/alter_opclass.sgml index aa79f5704a..586d54940c 100644 --- a/doc/src/sgml/ref/alter_opclass.sgml +++ b/doc/src/sgml/ref/alter_opclass.sgml @@ -1,5 +1,5 @@ @@ -102,6 +102,7 @@ ALTER OPERATOR CLASS name USING + diff --git a/doc/src/sgml/ref/alter_opfamily.sgml b/doc/src/sgml/ref/alter_opfamily.sgml new file mode 100644 index 0000000000..09906488e9 --- /dev/null +++ b/doc/src/sgml/ref/alter_opfamily.sgml @@ -0,0 +1,312 @@ + + + + + ALTER OPERATOR FAMILY + SQL - Language Statements + + + + ALTER OPERATOR FAMILY + change the definition of an operator family + + + + ALTER OPERATOR FAMILY + + + + +ALTER OPERATOR FAMILY name USING index_method ADD + { OPERATOR strategy_number operator_name ( op_type, op_type ) [ RECHECK ] + | FUNCTION support_number [ ( op_type [ , op_type ] ) ] funcname ( argument_type [, ...] ) + } [, ... ] +ALTER OPERATOR FAMILY name USING index_method DROP + { OPERATOR strategy_number ( op_type [ , op_type ] ) + | FUNCTION support_number ( op_type [ , op_type ] ) + } [, ... ] +ALTER OPERATOR FAMILY name USING index_method RENAME TO newname +ALTER OPERATOR FAMILY name USING index_method OWNER TO newowner + + + + + Description + + + ALTER OPERATOR FAMILY changes the definition of + an operator family. You can add operators and support functions + to the family, remove them from the family, + or change the family's name or owner. + + + + When operators and support functions are added to a family with + ALTER OPERATOR FAMILY, they are not part of any + specific operator class within the family, but are just loose + within the family. This indicates that these operators and functions + are compatible with the family's semantics, but are not required for + correct functioning of any specific index. (Operators and functions + that are so required should be declared as part of an operator class, + instead; see .) + PostgreSQL will allow loose members of a + family to be dropped from the family at any time, but members of an + operator class cannot be dropped without dropping the whole class and + any indexes that depend on it. + Typically, single-data-type operators + and functions are part of operator classes because they are needed to + support an index on that specific data type, while cross-data-type + operators and functions are made loose members of the family. + + + + You must be a superuser to use ALTER OPERATOR FAMILY. + + + + + Parameters + + + + name + + + The name (optionally schema-qualified) of an existing operator + family. + + + + + + index_method + + + The name of the index method this operator family is for. + + + + + + strategy_number + + + The index method's strategy number for an operator + associated with the operator family. + + + + + + operator_name + + + The name (optionally schema-qualified) of an operator associated + with the operator family. + + + + + + op_type + + + In an OPERATOR clause, + the operand data type(s) of the operator, or NONE to + signify a left-unary or right-unary operator. Unlike the comparable + syntax in CREATE OPERATOR CLASS, the operand data types + must always be specified. + + + + In an ADD FUNCTION clause, the operand data type(s) the + function is intended to support, if different from + the input data type(s) of the function. For B-tree and hash indexes + it is not necessary to specify op_type since the function's input + data type(s) are always the correct ones to use. For GIN and GiST + indexes it is necessary to specify the input data type the function + is to be used with. + + + + In a DROP FUNCTION clause, the operand data type(s) the + function is intended to support must be specified. + + + + + + RECHECK + + + If present, the index is lossy for this operator, and + so the rows retrieved using the index must be rechecked to + verify that they actually satisfy the qualification clause + involving this operator. + + + + + + support_number + + + The index method's support procedure number for a + function associated with the operator family. + + + + + + funcname + + + The name (optionally schema-qualified) of a function that is an + index method support procedure for the operator family. + + + + + + argument_types + + + The parameter data type(s) of the function. + + + + + + newname + + + The new name of the operator family. + + + + + + newowner + + + The new owner of the operator family. + + + + + + + The OPERATOR and FUNCTION + clauses may appear in any order. + + + + + + Notes + + + Notice that the DROP syntax only specifies the slot + in the operator family, by strategy or support number and input data + type(s). The name of the operator or function occupying the slot is not + mentioned. Also, for DROP FUNCTION the type(s) to specify + are the input data type(s) the function is intended to support; for + GIN and GiST indexes this may have nothing to do with the actual input + argument types of the function. + + + + Because the index machinery does not check access permissions on functions + before using them, including a function or operator in an operator family + is tantamount to granting public execute permission on it. This is usually + not an issue for the sorts of functions that are useful in an operator + family. + + + + The operators should not be defined by SQL functions. A SQL function + is likely to be inlined into the calling query, which will prevent + the optimizer from recognizing that the query matches an index. + + + + + Examples + + + The following example command adds cross-data-type operators and + support functions to an operator family that already contains B-tree + operator classes for data types int4 and int2. + + + +ALTER OPERATOR FAMILY integer_ops USING btree ADD + + -- int4 vs int2 + OPERATOR 1 < (int4, int2) , + OPERATOR 2 <= (int4, int2) , + OPERATOR 3 = (int4, int2) , + OPERATOR 4 >= (int4, int2) , + OPERATOR 5 > (int4, int2) , + FUNCTION 1 btint42cmp(int4, int2) , + + -- int2 vs int4 + OPERATOR 1 < (int2, int4) , + OPERATOR 2 <= (int2, int4) , + OPERATOR 3 = (int2, int4) , + OPERATOR 4 >= (int2, int4) , + OPERATOR 5 > (int2, int4) , + FUNCTION 1 btint24cmp(int2, int4) ; + + + + To remove these entries again: + + + +ALTER OPERATOR FAMILY integer_ops USING btree DROP + + -- int4 vs int2 + OPERATOR 1 (int4, int2) , + OPERATOR 2 (int4, int2) , + OPERATOR 3 (int4, int2) , + OPERATOR 4 (int4, int2) , + OPERATOR 5 (int4, int2) , + FUNCTION 1 (int4, int2) , + + -- int2 vs int4 + OPERATOR 1 (int2, int4) , + OPERATOR 2 (int2, int4) , + OPERATOR 3 (int2, int4) , + OPERATOR 4 (int2, int4) , + OPERATOR 5 (int2, int4) , + FUNCTION 1 (int2, int4) ; + + + + + Compatibility + + + There is no ALTER OPERATOR FAMILY statement in + the SQL standard. + + + + + See Also + + + + + + + + + + diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index d2cf39a20c..bc3ab8f26d 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -1,5 +1,5 @@ @@ -35,6 +35,7 @@ COMMENT ON LARGE OBJECT large_object_oid | OPERATOR op (leftoperand_type, rightoperand_type) | OPERATOR CLASS object_name USING index_method | + OPERATOR FAMILY object_name USING index_method | [ PROCEDURAL ] LANGUAGE object_name | ROLE object_name | RULE rule_name ON table_name | @@ -92,7 +93,7 @@ COMMENT ON The name of the object to be commented. Names of tables, aggregates, domains, functions, indexes, operators, operator classes, - sequences, types, and views may be schema-qualified. + operator families, sequences, types, and views may be schema-qualified. @@ -247,6 +248,7 @@ COMMENT ON LARGE OBJECT 346344 IS 'Planning document'; COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two texts'; COMMENT ON OPERATOR - (NONE, text) IS 'This is a prefix operator on text'; COMMENT ON OPERATOR CLASS int4ops USING btree IS '4 byte integer operators for btrees'; +COMMENT ON OPERATOR FAMILY integer_ops USING btree IS 'all integer operators for btrees'; COMMENT ON ROLE my_role IS 'Administration group for finance tables'; COMMENT ON RULE my_rule ON my_table IS 'Logs updates of employee records'; COMMENT ON SCHEMA my_schema IS 'Departmental data'; diff --git a/doc/src/sgml/ref/create_opclass.sgml b/doc/src/sgml/ref/create_opclass.sgml index 524be85f97..85a31e3b19 100644 --- a/doc/src/sgml/ref/create_opclass.sgml +++ b/doc/src/sgml/ref/create_opclass.sgml @@ -1,5 +1,5 @@ @@ -20,9 +20,10 @@ PostgreSQL documentation -CREATE OPERATOR CLASS name [ DEFAULT ] FOR TYPE data_type USING index_method AS +CREATE OPERATOR CLASS name [ DEFAULT ] FOR TYPE data_type + USING index_method [ FAMILY family_name ] AS { OPERATOR strategy_number operator_name [ ( op_type, op_type ) ] [ RECHECK ] - | FUNCTION support_number funcname ( argument_type [, ...] ) + | FUNCTION support_number [ ( op_type [ , op_type ] ) ] funcname ( argument_type [, ...] ) | STORAGE storage_type } [, ... ] @@ -40,7 +41,7 @@ CREATE OPERATOR CLASS name [ DEFAUL be used by the index 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. + class must be defined before the operator class can be created. @@ -65,6 +66,15 @@ CREATE OPERATOR CLASS name [ DEFAUL responsibility to define a valid operator class. + + Related operator classes can be grouped into operator + families. To add a new operator class to an existing family, + specify the FAMILY option in CREATE OPERATOR + CLASS. Without this option, the new class is placed into + a family named the same as the new class (creating that family if + it doesn't already exist). + + Refer to for further information. @@ -113,6 +123,17 @@ CREATE OPERATOR CLASS name [ DEFAUL + + family_name + + + The name of the existing operator family to add this operator class to. + If not specified, a family named the same as the operator class is + used (creating it, if it doesn't already exist). + + + + strategy_number @@ -137,11 +158,24 @@ CREATE OPERATOR CLASS name [ DEFAUL op_type - The operand data type(s) of an operator, or NONE to + In an OPERATOR clause, + the operand data type(s) of the operator, or NONE to signify a left-unary or right-unary operator. The operand data types may be omitted in the normal case where they are the same as the operator class's data type. + + + In a FUNCTION clause, the operand data type(s) the + function is intended to support, if different from + the input data type(s) of the function (for B-tree and hash indexes) + or the class's data type (for GIN and GiST indexes). These defaults + are always correct, so there is no point in specifying op_type in a FUNCTION clause + in CREATE OPERATOR CLASS, but the option is provided + for consistency with the comparable syntax in + ALTER OPERATOR FAMILY. + @@ -192,7 +226,7 @@ CREATE OPERATOR CLASS name [ DEFAUL The data type actually stored in the index. Normally this is the same as the column data type, but some index methods - (GIN and GiST for now) allow it to be different. The + (currently GIN and GiST) allow it to be different. The STORAGE clause must be omitted unless the index method allows a different type to be used. @@ -268,6 +302,8 @@ CREATE OPERATOR CLASS gist__int_ops + + diff --git a/doc/src/sgml/ref/create_opfamily.sgml b/doc/src/sgml/ref/create_opfamily.sgml new file mode 100644 index 0000000000..d8ddb683b9 --- /dev/null +++ b/doc/src/sgml/ref/create_opfamily.sgml @@ -0,0 +1,125 @@ + + + + + CREATE OPERATOR FAMILY + SQL - Language Statements + + + + CREATE OPERATOR FAMILY + define a new operator family + + + + CREATE OPERATOR FAMILY + + + + +CREATE OPERATOR FAMILY name USING index_method + + + + + Description + + + CREATE OPERATOR FAMILY creates a new operator family. + An operator family defines a collection of related operator classes, + and perhaps some additional operators and support functions that are + compatible with these operator classes but not essential for the + functioning of any individual index. (Operators and functions that + are essential to indexes should be grouped within the relevant operator + class, rather than being loose in the operator family. + Typically, single-data-type operators are bound to operator classes, + while cross-data-type operators can be loose in an operator family + containing operator classes for both data types.) + + + + The new operator family is initially empty. It should be populated + by issuing subsequent CREATE OPERATOR CLASS commands + to add contained operator classes, and optionally + ALTER OPERATOR FAMILY commands to add loose + operators and their corresponding support functions. + + + + If a schema name is given then the operator family is created in the + specified schema. Otherwise it is created in the current schema. + Two operator families in the same schema can have the same name only if they + are for different index methods. + + + + The user who defines an operator family becomes its owner. Presently, + the creating user must be a superuser. (This restriction is made because + an erroneous operator family definition could confuse or even crash the + server.) + + + + CREATE OPERATOR FAMILY does not presently check + whether the operator family definition includes all the operators and + functions required by the index method, nor whether the operators and + functions form a self-consistent set. It is the user's + responsibility to define a valid operator family. + + + + Refer to for further information. + + + + + Parameters + + + + name + + + The name of the operator family to be created. The name may be + schema-qualified. + + + + + + index_method + + + The name of the index method this operator family is for. + + + + + + + + Compatibility + + + CREATE OPERATOR FAMILY is a + PostgreSQL extension. There is no + CREATE OPERATOR FAMILY statement in the SQL + standard. + + + + + See Also + + + + + + + + + + diff --git a/doc/src/sgml/ref/drop_opclass.sgml b/doc/src/sgml/ref/drop_opclass.sgml index 42cc1cd0f8..ce00ff40fd 100644 --- a/doc/src/sgml/ref/drop_opclass.sgml +++ b/doc/src/sgml/ref/drop_opclass.sgml @@ -1,5 +1,5 @@ @@ -31,6 +31,13 @@ DROP OPERATOR CLASS [ IF EXISTS ] nameDROP OPERATOR CLASS drops an existing operator class. To execute this command you must be the owner of the operator class. + + + DROP OPERATOR CLASS does not drop any of the operators + or functions referenced by the class. If there are any indexes depending + on the operator class, you will need to specify + CASCADE for the drop to complete. + @@ -86,6 +93,20 @@ DROP OPERATOR CLASS [ IF EXISTS ] name + + + Notes + + + DROP OPERATOR CLASS will not drop the operator family + containing the class, even if there is nothing else left in the + family (in particular, in the case where the family was implicitly + created by CREATE OPERATOR CLASS). An empty operator + family is harmless, but for the sake of tidiness you may wish to + remove the family with DROP OPERATOR FAMILY; or perhaps + better, use DROP OPERATOR FAMILY in the first place. + + Examples @@ -118,6 +139,7 @@ DROP OPERATOR CLASS widget_ops USING btree; + diff --git a/doc/src/sgml/ref/drop_opfamily.sgml b/doc/src/sgml/ref/drop_opfamily.sgml new file mode 100644 index 0000000000..e2dcaf72af --- /dev/null +++ b/doc/src/sgml/ref/drop_opfamily.sgml @@ -0,0 +1,135 @@ + + + + + DROP OPERATOR FAMILY + SQL - Language Statements + + + + DROP OPERATOR FAMILY + remove an operator family + + + + DROP OPERATOR FAMILY + + + + +DROP OPERATOR FAMILY [ IF EXISTS ] name USING index_method [ CASCADE | RESTRICT ] + + + + + Description + + + DROP OPERATOR FAMILY drops an existing operator family. + To execute this command you must be the owner of the operator family. + + + + DROP OPERATOR FAMILY includes dropping any operator + classes contained in the family, but it does not drop any of the operators + or functions referenced by the family. If there are any indexes depending + on operator classes within the family, you will need to specify + CASCADE for the drop to complete. + + + + + Parameters + + + + + IF EXISTS + + + Do not throw an error if the operator family does not exist. + A notice is issued in this case. + + + + + + name + + + The name (optionally schema-qualified) of an existing operator family. + + + + + + index_method + + + The name of the index access method the operator family is for. + + + + + + CASCADE + + + Automatically drop objects that depend on the operator family. + + + + + + RESTRICT + + + Refuse to drop the operator family if any objects depend on it. + This is the default. + + + + + + + + Examples + + + Remove the B-tree operator family float_ops: + + +DROP OPERATOR FAMILY float_ops USING btree; + + + This command will not succeed if there are any existing indexes + that use operator classes within the family. Add CASCADE to + drop such indexes along with the operator family. + + + + + Compatibility + + + There is no DROP OPERATOR FAMILY statement in the + SQL standard. + + + + + See Also + + + + + + + + + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 64330928c4..9b3cbda575 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,4 +1,4 @@ - + Reference @@ -44,6 +44,7 @@ &alterLanguage; &alterOperator; &alterOperatorClass; + &alterOperatorFamily; &alterRole; &alterSchema; &alterSequence; @@ -73,6 +74,7 @@ &createLanguage; &createOperator; &createOperatorClass; + &createOperatorFamily; &createRole; &createRule; &createSchema; @@ -98,6 +100,7 @@ &dropLanguage; &dropOperator; &dropOperatorClass; + &dropOperatorFamily; &dropOwned; &dropRole; &dropRule; diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index fcef0593b0..328f8ccfda 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.134 2007/01/05 22:19:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.135 2007/01/23 05:07:17 tgl Exp $ * * NOTES * See acl.h. @@ -30,6 +30,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" @@ -1413,6 +1414,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = gettext_noop("permission denied for schema %s"), /* ACL_KIND_OPCLASS */ gettext_noop("permission denied for operator class %s"), + /* ACL_KIND_OPFAMILY */ + gettext_noop("permission denied for operator family %s"), /* ACL_KIND_CONVERSION */ gettext_noop("permission denied for conversion %s"), /* ACL_KIND_TABLESPACE */ @@ -1439,6 +1442,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = gettext_noop("must be owner of schema %s"), /* ACL_KIND_OPCLASS */ gettext_noop("must be owner of operator class %s"), + /* ACL_KIND_OPFAMILY */ + gettext_noop("must be owner of operator family %s"), /* ACL_KIND_CONVERSION */ gettext_noop("must be owner of conversion %s"), /* ACL_KIND_TABLESPACE */ @@ -2239,6 +2244,35 @@ pg_opclass_ownercheck(Oid opc_oid, Oid roleid) return has_privs_of_role(roleid, ownerId); } +/* + * Ownership check for an operator family (specified by OID). + */ +bool +pg_opfamily_ownercheck(Oid opf_oid, Oid roleid) +{ + HeapTuple tuple; + Oid ownerId; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + tuple = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(opf_oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family with OID %u does not exist", + opf_oid))); + + ownerId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfowner; + + ReleaseSysCache(tuple); + + return has_privs_of_role(roleid, ownerId); +} + /* * Ownership check for a database (specified by OID). */ diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index bf806f76fc..30b7ebde00 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.21 2007/01/05 22:19:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.22 2007/01/23 05:07:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,6 +66,10 @@ ExecRenameStmt(RenameStmt *stmt) RenameOpClass(stmt->object, stmt->subname, stmt->newname); break; + case OBJECT_OPFAMILY: + RenameOpFamily(stmt->object, stmt->subname, stmt->newname); + break; + case OBJECT_ROLE: RenameRole(stmt->subname, stmt->newname); break; @@ -211,6 +215,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterOpClassOwner(stmt->object, stmt->addname, newowner); break; + case OBJECT_OPFAMILY: + AlterOpFamilyOwner(stmt->object, stmt->addname, newowner); + break; + case OBJECT_SCHEMA: AlterSchemaOwner((char *) linitial(stmt->object), newowner); break; diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 6d6eff7f48..2f6a38d42d 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -7,7 +7,7 @@ * Copyright (c) 1996-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.94 2007/01/05 22:19:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.95 2007/01/23 05:07:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_shdescription.h" @@ -72,6 +73,7 @@ static void CommentConstraint(List *qualname, char *comment); static void CommentConversion(List *qualname, char *comment); static void CommentLanguage(List *qualname, char *comment); static void CommentOpClass(List *qualname, List *arguments, char *comment); +static void CommentOpFamily(List *qualname, List *arguments, char *comment); static void CommentLargeObject(List *qualname, char *comment); static void CommentCast(List *qualname, List *arguments, char *comment); static void CommentTablespace(List *qualname, char *comment); @@ -134,6 +136,9 @@ CommentObject(CommentStmt *stmt) case OBJECT_OPCLASS: CommentOpClass(stmt->objname, stmt->objargs, stmt->comment); break; + case OBJECT_OPFAMILY: + CommentOpFamily(stmt->objname, stmt->objargs, stmt->comment); + break; case OBJECT_LARGEOBJECT: CommentLargeObject(stmt->objname, stmt->comment); break; @@ -1263,6 +1268,92 @@ CommentOpClass(List *qualname, List *arguments, char *comment) CreateComments(opcID, OperatorClassRelationId, 0, comment); } +/* + * CommentOpFamily -- + * + * This routine is used to allow a user to provide comments on an + * operator family. The operator family for commenting is determined by both + * its name and its argument list which defines the index method + * the operator family is used for. The argument list is expected to contain + * a single name (represented as a string Value node). + */ +static void +CommentOpFamily(List *qualname, List *arguments, char *comment) +{ + char *amname; + char *schemaname; + char *opfname; + Oid amID; + Oid opfID; + HeapTuple tuple; + + Assert(list_length(arguments) == 1); + amname = strVal(linitial(arguments)); + + /* + * Get the access method's OID. + */ + amID = GetSysCacheOid(AMNAME, + CStringGetDatum(amname), + 0, 0, 0); + if (!OidIsValid(amID)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + amname))); + + /* + * Look up the opfamily. + */ + + /* deconstruct the name list */ + DeconstructQualifiedName(qualname, &schemaname, &opfname); + + if (schemaname) + { + /* Look in specific schema only */ + Oid namespaceId; + + namespaceId = LookupExplicitNamespace(schemaname); + tuple = SearchSysCache(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amID), + PointerGetDatum(opfname), + ObjectIdGetDatum(namespaceId), + 0); + } + else + { + /* Unqualified opfamily name, so search the search path */ + opfID = OpfamilynameGetOpfid(amID, opfname); + if (!OidIsValid(opfID)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + opfname, amname))); + tuple = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(opfID), + 0, 0, 0); + } + + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(qualname), amname))); + + opfID = HeapTupleGetOid(tuple); + + /* Permission check: must own opfamily */ + if (!pg_opfamily_ownercheck(opfID, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(qualname)); + + ReleaseSysCache(tuple); + + /* Call CreateComments() to create/drop the comments */ + CreateComments(opfID, OperatorFamilyRelationId, 0, comment); +} + /* * CommentLargeObject -- * diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index 24e4f832c4..802b690952 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.52 2007/01/05 22:19:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.53 2007/01/23 05:07:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,15 +55,30 @@ typedef struct } OpFamilyMember; +static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, + int maxOpNumber, int maxProcNumber, + List *items); +static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid, + int maxOpNumber, int maxProcNumber, + List *items); +static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype); static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid); static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid); static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc); -static void storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, - List *operators); -static void storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, - List *procedures); +static void storeOperators(List *opfamilyname, Oid amoid, + Oid opfamilyoid, Oid opclassoid, + List *operators, bool isAdd); +static void storeProcedures(List *opfamilyname, Oid amoid, + Oid opfamilyoid, Oid opclassoid, + List *procedures, bool isAdd); +static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, + List *operators); +static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, + List *procedures); static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple, Oid newOwnerId); +static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple, + Oid newOwnerId); /* @@ -452,6 +467,12 @@ DefineOpClass(CreateOpClassStmt *stmt) member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); member->object = funcOid; member->number = item->number; + + /* allow overriding of the function's actual arg types */ + if (item->class_args) + processTypesSpec(item->class_args, + &member->lefttype, &member->righttype); + assignProcTypes(member, amoid, typeoid); addFamilyMember(&procedures, member, true); break; @@ -570,8 +591,10 @@ DefineOpClass(CreateOpClassStmt *stmt) * Now add tuples to pg_amop and pg_amproc tying in the operators and * functions. Dependencies on them are inserted, too. */ - storeOperators(amoid, opfamilyoid, opclassoid, operators); - storeProcedures(amoid, opfamilyoid, opclassoid, procedures); + storeOperators(stmt->opfamilyname, amoid, opfamilyoid, + opclassoid, operators, false); + storeProcedures(stmt->opfamilyname, amoid, opfamilyoid, + opclassoid, procedures, false); /* * Create dependencies for the opclass proper. Note: we do not create a @@ -615,6 +638,420 @@ DefineOpClass(CreateOpClassStmt *stmt) heap_close(rel, RowExclusiveLock); } + +/* + * DefineOpFamily + * Define a new index operator family. + */ +void +DefineOpFamily(CreateOpFamilyStmt *stmt) +{ + char *opfname; /* name of opfamily we're creating */ + Oid amoid, /* our AM's oid */ + namespaceoid, /* namespace to create opfamily in */ + opfamilyoid; /* oid of opfamily we create */ + Relation rel; + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + char nulls[Natts_pg_opfamily]; + AclResult aclresult; + NameData opfName; + ObjectAddress myself, + referenced; + + /* Convert list of names to a name and namespace */ + namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, + &opfname); + + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + + /* Get necessary info about access method */ + tup = SearchSysCache(AMNAME, + CStringGetDatum(stmt->amname), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + stmt->amname))); + + amoid = HeapTupleGetOid(tup); + + /* XXX Should we make any privilege check against the AM? */ + + ReleaseSysCache(tup); + + /* + * Currently, we require superuser privileges to create an opfamily. + * See comments in DefineOpClass. + * + * XXX re-enable NOT_USED code sections below if you remove this test. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create an operator family"))); + + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); + + /* + * Make sure there is no existing opfamily of this name (this is just to + * give a more friendly error message than "duplicate key"). + */ + if (SearchSysCacheExists(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amoid), + CStringGetDatum(opfname), + ObjectIdGetDatum(namespaceoid), + 0)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("operator family \"%s\" for access method \"%s\" already exists", + opfname, stmt->amname))); + + /* + * Okay, let's create the pg_opfamily entry. + */ + memset(values, 0, sizeof(values)); + memset(nulls, ' ', sizeof(nulls)); + + values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid); + namestrcpy(&opfName, opfname); + values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName); + values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid); + values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); + + tup = heap_formtuple(rel->rd_att, values, nulls); + + opfamilyoid = simple_heap_insert(rel, tup); + + CatalogUpdateIndexes(rel, tup); + + heap_freetuple(tup); + + /* + * Create dependencies for the opfamily proper. Note: we do not create a + * dependency link to the AM, because we don't currently support DROP + * ACCESS METHOD. + */ + myself.classId = OperatorFamilyRelationId; + myself.objectId = opfamilyoid; + myself.objectSubId = 0; + + /* dependency on namespace */ + referenced.classId = NamespaceRelationId; + referenced.objectId = namespaceoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on owner */ + recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId()); + + heap_close(rel, RowExclusiveLock); +} + + +/* + * AlterOpFamily + * Add or remove operators/procedures within an existing operator family. + * + * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP. Some + * other commands called ALTER OPERATOR FAMILY exist, but go through + * different code paths. + */ +void +AlterOpFamily(AlterOpFamilyStmt *stmt) +{ + Oid amoid, /* our AM's oid */ + opfamilyoid; /* oid of opfamily */ + int maxOpNumber, /* amstrategies value */ + maxProcNumber; /* amsupport value */ + HeapTuple tup; + Form_pg_am pg_am; + + /* Get necessary info about access method */ + tup = SearchSysCache(AMNAME, + CStringGetDatum(stmt->amname), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + stmt->amname))); + + amoid = HeapTupleGetOid(tup); + pg_am = (Form_pg_am) GETSTRUCT(tup); + maxOpNumber = pg_am->amstrategies; + /* if amstrategies is zero, just enforce that op numbers fit in int16 */ + if (maxOpNumber <= 0) + maxOpNumber = SHRT_MAX; + maxProcNumber = pg_am->amsupport; + + /* XXX Should we make any privilege check against the AM? */ + + ReleaseSysCache(tup); + + /* Look up the opfamily */ + tup = OpFamilyCacheLookup(amoid, stmt->opfamilyname); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); + ReleaseSysCache(tup); + + /* + * Currently, we require superuser privileges to alter an opfamily. + * + * XXX re-enable NOT_USED code sections below if you remove this test. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to alter an operator family"))); + + /* + * ADD and DROP cases need separate code from here on down. + */ + if (stmt->isDrop) + AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid, + maxOpNumber, maxProcNumber, + stmt->items); + else + AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid, + maxOpNumber, maxProcNumber, + stmt->items); +} + +/* + * ADD part of ALTER OP FAMILY + */ +static void +AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, + int maxOpNumber, int maxProcNumber, + List *items) +{ + List *operators; /* OpFamilyMember list for operators */ + List *procedures; /* OpFamilyMember list for support procs */ + ListCell *l; + + operators = NIL; + procedures = NIL; + + /* + * Scan the "items" list to obtain additional info. + */ + foreach(l, items) + { + CreateOpClassItem *item = lfirst(l); + Oid operOid; + Oid funcOid; + OpFamilyMember *member; + + Assert(IsA(item, CreateOpClassItem)); + switch (item->itemtype) + { + case OPCLASS_ITEM_OPERATOR: + if (item->number <= 0 || item->number > maxOpNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("invalid operator number %d," + " must be between 1 and %d", + item->number, maxOpNumber))); + if (item->args != NIL) + { + TypeName *typeName1 = (TypeName *) linitial(item->args); + TypeName *typeName2 = (TypeName *) lsecond(item->args); + + operOid = LookupOperNameTypeNames(NULL, item->name, + typeName1, typeName2, + false, -1); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY"))); + operOid = InvalidOid; /* keep compiler quiet */ + } + +#ifdef NOT_USED + /* XXX this is unnecessary given the superuser check above */ + /* Caller must own operator and its underlying function */ + if (!pg_oper_ownercheck(operOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + get_opname(operOid)); + funcOid = get_opcode(operOid); + if (!pg_proc_ownercheck(funcOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); +#endif + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); + member->object = operOid; + member->number = item->number; + member->recheck = item->recheck; + assignOperTypes(member, amoid, InvalidOid); + addFamilyMember(&operators, member, false); + break; + case OPCLASS_ITEM_FUNCTION: + if (item->number <= 0 || item->number > maxProcNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("invalid procedure number %d," + " must be between 1 and %d", + item->number, maxProcNumber))); + funcOid = LookupFuncNameTypeNames(item->name, item->args, + false); +#ifdef NOT_USED + /* XXX this is unnecessary given the superuser check above */ + /* Caller must own function */ + if (!pg_proc_ownercheck(funcOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); +#endif + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); + member->object = funcOid; + member->number = item->number; + + /* allow overriding of the function's actual arg types */ + if (item->class_args) + processTypesSpec(item->class_args, + &member->lefttype, &member->righttype); + + assignProcTypes(member, amoid, InvalidOid); + addFamilyMember(&procedures, member, true); + break; + case OPCLASS_ITEM_STORAGETYPE: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STORAGE may not be specified in ALTER OPERATOR FAMILY"))); + break; + default: + elog(ERROR, "unrecognized item type: %d", item->itemtype); + break; + } + } + + /* + * Add tuples to pg_amop and pg_amproc tying in the operators and + * functions. Dependencies on them are inserted, too. + */ + storeOperators(opfamilyname, amoid, opfamilyoid, + InvalidOid, operators, true); + storeProcedures(opfamilyname, amoid, opfamilyoid, + InvalidOid, procedures, true); +} + +/* + * DROP part of ALTER OP FAMILY + */ +static void +AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid, + int maxOpNumber, int maxProcNumber, + List *items) +{ + List *operators; /* OpFamilyMember list for operators */ + List *procedures; /* OpFamilyMember list for support procs */ + ListCell *l; + + operators = NIL; + procedures = NIL; + + /* + * Scan the "items" list to obtain additional info. + */ + foreach(l, items) + { + CreateOpClassItem *item = lfirst(l); + Oid lefttype, + righttype; + OpFamilyMember *member; + + Assert(IsA(item, CreateOpClassItem)); + switch (item->itemtype) + { + case OPCLASS_ITEM_OPERATOR: + if (item->number <= 0 || item->number > maxOpNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("invalid operator number %d," + " must be between 1 and %d", + item->number, maxOpNumber))); + processTypesSpec(item->args, &lefttype, &righttype); + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); + member->number = item->number; + member->lefttype = lefttype; + member->righttype = righttype; + addFamilyMember(&operators, member, false); + break; + case OPCLASS_ITEM_FUNCTION: + if (item->number <= 0 || item->number > maxProcNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("invalid procedure number %d," + " must be between 1 and %d", + item->number, maxProcNumber))); + processTypesSpec(item->args, &lefttype, &righttype); + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); + member->number = item->number; + member->lefttype = lefttype; + member->righttype = righttype; + addFamilyMember(&procedures, member, true); + break; + case OPCLASS_ITEM_STORAGETYPE: + /* grammar prevents this from appearing */ + default: + elog(ERROR, "unrecognized item type: %d", item->itemtype); + break; + } + } + + /* + * Remove tuples from pg_amop and pg_amproc. + */ + dropOperators(opfamilyname, amoid, opfamilyoid, operators); + dropProcedures(opfamilyname, amoid, opfamilyoid, procedures); +} + + +/* + * Deal with explicit arg types used in ALTER ADD/DROP + */ +static void +processTypesSpec(List *args, Oid *lefttype, Oid *righttype) +{ + TypeName *typeName; + + Assert(args != NIL); + + typeName = (TypeName *) linitial(args); + *lefttype = typenameTypeId(NULL, typeName); + + if (list_length(args) > 1) + { + typeName = (TypeName *) lsecond(args); + *righttype = typenameTypeId(NULL, typeName); + } + else + *righttype = *lefttype; + + if (list_length(args) > 2) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("one or two argument types must be specified"))); +} + + /* * Determine the lefttype/righttype to assign to an operator, * and do any validity checking we can manage. @@ -781,7 +1218,9 @@ addFamilyMember(List **list, OpFamilyMember *member, bool isProc) * else make an AUTO dependency on the opfamily. */ static void -storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators) +storeOperators(List *opfamilyname, Oid amoid, + Oid opfamilyoid, Oid opclassoid, + List *operators, bool isAdd) { Relation rel; Datum values[Natts_pg_amop]; @@ -798,6 +1237,24 @@ storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators) { OpFamilyMember *op = (OpFamilyMember *) lfirst(l); + /* + * If adding to an existing family, check for conflict with an + * existing pg_amop entry (just to give a nicer error message) + */ + if (isAdd && + SearchSysCacheExists(AMOPSTRATEGY, + ObjectIdGetDatum(opfamilyoid), + ObjectIdGetDatum(op->lefttype), + ObjectIdGetDatum(op->righttype), + Int16GetDatum(op->number))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("operator %d(%s,%s) already exists in operator family \"%s\"", + op->number, + format_type_be(op->lefttype), + format_type_be(op->righttype), + NameListToString(opfamilyname)))); + /* Create the pg_amop entry */ memset(values, 0, sizeof(values)); memset(nulls, ' ', sizeof(nulls)); @@ -862,7 +1319,9 @@ storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators) * else make an AUTO dependency on the opfamily. */ static void -storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures) +storeProcedures(List *opfamilyname, Oid amoid, + Oid opfamilyoid, Oid opclassoid, + List *procedures, bool isAdd) { Relation rel; Datum values[Natts_pg_amproc]; @@ -879,6 +1338,24 @@ storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures) { OpFamilyMember *proc = (OpFamilyMember *) lfirst(l); + /* + * If adding to an existing family, check for conflict with an + * existing pg_amproc entry (just to give a nicer error message) + */ + if (isAdd && + SearchSysCacheExists(AMPROCNUM, + ObjectIdGetDatum(opfamilyoid), + ObjectIdGetDatum(proc->lefttype), + ObjectIdGetDatum(proc->righttype), + Int16GetDatum(proc->number))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("function %d(%s,%s) already exists in operator family \"%s\"", + proc->number, + format_type_be(proc->lefttype), + format_type_be(proc->righttype), + NameListToString(opfamilyname)))); + /* Create the pg_amproc entry */ memset(values, 0, sizeof(values)); memset(nulls, ' ', sizeof(nulls)); @@ -934,6 +1411,87 @@ storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures) } +/* + * Remove operator entries from an opfamily. + * + * Note: this is only allowed for "loose" members of an opfamily, hence + * behavior is always RESTRICT. + */ +static void +dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, + List *operators) +{ + ListCell *l; + + foreach(l, operators) + { + OpFamilyMember *op = (OpFamilyMember *) lfirst(l); + Oid amopid; + ObjectAddress object; + + amopid = GetSysCacheOid(AMOPSTRATEGY, + ObjectIdGetDatum(opfamilyoid), + ObjectIdGetDatum(op->lefttype), + ObjectIdGetDatum(op->righttype), + Int16GetDatum(op->number)); + if (!OidIsValid(amopid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"", + op->number, + format_type_be(op->lefttype), + format_type_be(op->righttype), + NameListToString(opfamilyname)))); + + object.classId = AccessMethodOperatorRelationId; + object.objectId = amopid; + object.objectSubId = 0; + + performDeletion(&object, DROP_RESTRICT); + } +} + +/* + * Remove procedure entries from an opfamily. + * + * Note: this is only allowed for "loose" members of an opfamily, hence + * behavior is always RESTRICT. + */ +static void +dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, + List *procedures) +{ + ListCell *l; + + foreach(l, procedures) + { + OpFamilyMember *op = (OpFamilyMember *) lfirst(l); + Oid amprocid; + ObjectAddress object; + + amprocid = GetSysCacheOid(AMPROCNUM, + ObjectIdGetDatum(opfamilyoid), + ObjectIdGetDatum(op->lefttype), + ObjectIdGetDatum(op->righttype), + Int16GetDatum(op->number)); + if (!OidIsValid(amprocid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("function %d(%s,%s) does not exist in operator family \"%s\"", + op->number, + format_type_be(op->lefttype), + format_type_be(op->righttype), + NameListToString(opfamilyname)))); + + object.classId = AccessMethodProcedureRelationId; + object.objectId = amprocid; + object.objectSubId = 0; + + performDeletion(&object, DROP_RESTRICT); + } +} + + /* * RemoveOpClass * Deletes an opclass. @@ -997,6 +1555,70 @@ RemoveOpClass(RemoveOpClassStmt *stmt) performDeletion(&object, stmt->behavior); } +/* + * RemoveOpFamily + * Deletes an opfamily. + */ +void +RemoveOpFamily(RemoveOpFamilyStmt *stmt) +{ + Oid amID, + opfID; + HeapTuple tuple; + ObjectAddress object; + + /* + * Get the access method's OID. + */ + amID = GetSysCacheOid(AMNAME, + CStringGetDatum(stmt->amname), + 0, 0, 0); + if (!OidIsValid(amID)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + stmt->amname))); + + /* + * Look up the opfamily. + */ + tuple = OpFamilyCacheLookup(amID, stmt->opfamilyname); + if (!HeapTupleIsValid(tuple)) + { + if (!stmt->missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(stmt->opfamilyname), stmt->amname))); + else + ereport(NOTICE, + (errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(stmt->opfamilyname), stmt->amname))); + return; + } + + opfID = HeapTupleGetOid(tuple); + + /* Permission check: must own opfamily or its namespace */ + if (!pg_opfamily_ownercheck(opfID, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(stmt->opfamilyname)); + + ReleaseSysCache(tuple); + + /* + * Do the deletion + */ + object.classId = OperatorFamilyRelationId; + object.objectId = opfID; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + + /* * Deletion subroutines for use by dependency.c. */ @@ -1202,29 +1824,104 @@ RenameOpClass(List *name, const char *access_method, const char *newname) } /* - * Change opclass owner by oid + * Rename opfamily */ -#ifdef NOT_USED void -AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId) +RenameOpFamily(List *name, const char *access_method, const char *newname) { - Relation rel; + Oid opfOid; + Oid amOid; + Oid namespaceOid; + char *schemaname; + char *opfname; HeapTuple tup; + Relation rel; + AclResult aclresult; - rel = heap_open(OperatorClassRelationId, RowExclusiveLock); + amOid = GetSysCacheOid(AMNAME, + CStringGetDatum(access_method), + 0, 0, 0); + if (!OidIsValid(amOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + access_method))); - tup = SearchSysCacheCopy(CLAOID, - ObjectIdGetDatum(opcOid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* shouldn't happen */ - elog(ERROR, "cache lookup failed for opclass %u", opcOid); + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); - AlterOpClassOwner_internal(rel, tup, newOwnerId); + /* + * Look up the opfamily + */ + DeconstructQualifiedName(name, &schemaname, &opfname); + + if (schemaname) + { + namespaceOid = LookupExplicitNamespace(schemaname); + + tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amOid), + PointerGetDatum(opfname), + ObjectIdGetDatum(namespaceOid), + 0); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + opfname, access_method))); + + opfOid = HeapTupleGetOid(tup); + } + else + { + opfOid = OpfamilynameGetOpfid(amOid, opfname); + if (!OidIsValid(opfOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + opfname, access_method))); + + tup = SearchSysCacheCopy(OPFAMILYOID, + ObjectIdGetDatum(opfOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for opfamily %u", opfOid); + + namespaceOid = ((Form_pg_opfamily) GETSTRUCT(tup))->opfnamespace; + } + + /* make sure the new name doesn't exist */ + if (SearchSysCacheExists(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amOid), + CStringGetDatum(newname), + ObjectIdGetDatum(namespaceOid), + 0)) + { + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"", + newname, access_method, + get_namespace_name(namespaceOid)))); + } + + /* must be owner */ + if (!pg_opfamily_ownercheck(opfOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameListToString(name)); + + /* must have CREATE privilege on namespace */ + aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + + /* rename */ + namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); - heap_freetuple(tup); heap_close(rel, NoLock); + heap_freetuple(tup); } -#endif /* * Change opclass owner by name @@ -1352,3 +2049,130 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) newOwnerId); } } + +/* + * Change opfamily owner by name + */ +void +AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId) +{ + Oid amOid; + Relation rel; + HeapTuple tup; + char *opfname; + char *schemaname; + + amOid = GetSysCacheOid(AMNAME, + CStringGetDatum(access_method), + 0, 0, 0); + if (!OidIsValid(amOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + access_method))); + + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); + + /* + * Look up the opfamily + */ + DeconstructQualifiedName(name, &schemaname, &opfname); + + if (schemaname) + { + Oid namespaceOid; + + namespaceOid = LookupExplicitNamespace(schemaname); + + tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amOid), + PointerGetDatum(opfname), + ObjectIdGetDatum(namespaceOid), + 0); + if (!HeapTupleIsValid(tup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + opfname, access_method))); + } + else + { + Oid opfOid; + + opfOid = OpfamilynameGetOpfid(amOid, opfname); + if (!OidIsValid(opfOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + opfname, access_method))); + + tup = SearchSysCacheCopy(OPFAMILYOID, + ObjectIdGetDatum(opfOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for opfamily %u", opfOid); + } + + AlterOpFamilyOwner_internal(rel, tup, newOwnerId); + + heap_freetuple(tup); + heap_close(rel, NoLock); +} + +/* + * The first parameter is pg_opfamily, opened and suitably locked. The second + * parameter is a copy of the tuple from pg_opfamily we want to modify. + */ +static void +AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) +{ + Oid namespaceOid; + AclResult aclresult; + Form_pg_opfamily opfForm; + + Assert(tup->t_tableOid == OperatorFamilyRelationId); + Assert(RelationGetRelid(rel) == OperatorFamilyRelationId); + + opfForm = (Form_pg_opfamily) GETSTRUCT(tup); + + namespaceOid = opfForm->opfnamespace; + + /* + * If the new owner is the same as the existing owner, consider the + * command to have succeeded. This is for dump restoration purposes. + */ + if (opfForm->opfowner != newOwnerId) + { + /* Superusers can always do it */ + if (!superuser()) + { + /* Otherwise, must be owner of the existing object */ + if (!pg_opfamily_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, + NameStr(opfForm->opfname)); + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + + /* New owner must have CREATE privilege on namespace */ + aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } + + /* + * Modify the owner --- okay to scribble on tup because it's a copy + */ + opfForm->opfowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); + + CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup), + newOwnerId); + } +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 1237dc7fe6..f213f216de 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.363 2007/01/22 20:00:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.364 2007/01/23 05:07:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2158,6 +2158,19 @@ _copyRemoveOpClassStmt(RemoveOpClassStmt *from) return newnode; } +static RemoveOpFamilyStmt * +_copyRemoveOpFamilyStmt(RemoveOpFamilyStmt *from) +{ + RemoveOpFamilyStmt *newnode = makeNode(RemoveOpFamilyStmt); + + COPY_NODE_FIELD(opfamilyname); + COPY_STRING_FIELD(amname); + COPY_SCALAR_FIELD(behavior); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + static RenameStmt * _copyRenameStmt(RenameStmt *from) { @@ -2332,11 +2345,36 @@ _copyCreateOpClassItem(CreateOpClassItem *from) COPY_NODE_FIELD(args); COPY_SCALAR_FIELD(number); COPY_SCALAR_FIELD(recheck); + COPY_NODE_FIELD(class_args); COPY_NODE_FIELD(storedtype); return newnode; } +static CreateOpFamilyStmt * +_copyCreateOpFamilyStmt(CreateOpFamilyStmt *from) +{ + CreateOpFamilyStmt *newnode = makeNode(CreateOpFamilyStmt); + + COPY_NODE_FIELD(opfamilyname); + COPY_STRING_FIELD(amname); + + return newnode; +} + +static AlterOpFamilyStmt * +_copyAlterOpFamilyStmt(AlterOpFamilyStmt *from) +{ + AlterOpFamilyStmt *newnode = makeNode(AlterOpFamilyStmt); + + COPY_NODE_FIELD(opfamilyname); + COPY_STRING_FIELD(amname); + COPY_SCALAR_FIELD(isDrop); + COPY_NODE_FIELD(items); + + return newnode; +} + static CreatedbStmt * _copyCreatedbStmt(CreatedbStmt *from) { @@ -3163,6 +3201,9 @@ copyObject(void *from) case T_RemoveOpClassStmt: retval = _copyRemoveOpClassStmt(from); break; + case T_RemoveOpFamilyStmt: + retval = _copyRemoveOpFamilyStmt(from); + break; case T_RenameStmt: retval = _copyRenameStmt(from); break; @@ -3205,6 +3246,12 @@ copyObject(void *from) case T_CreateOpClassItem: retval = _copyCreateOpClassItem(from); break; + case T_CreateOpFamilyStmt: + retval = _copyCreateOpFamilyStmt(from); + break; + case T_AlterOpFamilyStmt: + retval = _copyAlterOpFamilyStmt(from); + break; case T_CreatedbStmt: retval = _copyCreatedbStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 40a91a2b0e..31754a7bc0 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.296 2007/01/20 20:45:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.297 2007/01/23 05:07:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1053,6 +1053,17 @@ _equalRemoveOpClassStmt(RemoveOpClassStmt *a, RemoveOpClassStmt *b) return true; } +static bool +_equalRemoveOpFamilyStmt(RemoveOpFamilyStmt *a, RemoveOpFamilyStmt *b) +{ + COMPARE_NODE_FIELD(opfamilyname); + COMPARE_STRING_FIELD(amname); + COMPARE_SCALAR_FIELD(behavior); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + static bool _equalRenameStmt(RenameStmt *a, RenameStmt *b) { @@ -1199,11 +1210,32 @@ _equalCreateOpClassItem(CreateOpClassItem *a, CreateOpClassItem *b) COMPARE_NODE_FIELD(args); COMPARE_SCALAR_FIELD(number); COMPARE_SCALAR_FIELD(recheck); + COMPARE_NODE_FIELD(class_args); COMPARE_NODE_FIELD(storedtype); return true; } +static bool +_equalCreateOpFamilyStmt(CreateOpFamilyStmt *a, CreateOpFamilyStmt *b) +{ + COMPARE_NODE_FIELD(opfamilyname); + COMPARE_STRING_FIELD(amname); + + return true; +} + +static bool +_equalAlterOpFamilyStmt(AlterOpFamilyStmt *a, AlterOpFamilyStmt *b) +{ + COMPARE_NODE_FIELD(opfamilyname); + COMPARE_STRING_FIELD(amname); + COMPARE_SCALAR_FIELD(isDrop); + COMPARE_NODE_FIELD(items); + + return true; +} + static bool _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b) { @@ -2148,6 +2180,9 @@ equal(void *a, void *b) case T_RemoveOpClassStmt: retval = _equalRemoveOpClassStmt(a, b); break; + case T_RemoveOpFamilyStmt: + retval = _equalRemoveOpFamilyStmt(a, b); + break; case T_RenameStmt: retval = _equalRenameStmt(a, b); break; @@ -2190,6 +2225,12 @@ equal(void *a, void *b) case T_CreateOpClassItem: retval = _equalCreateOpClassItem(a, b); break; + case T_CreateOpFamilyStmt: + retval = _equalCreateOpFamilyStmt(a, b); + break; + case T_AlterOpFamilyStmt: + retval = _equalAlterOpFamilyStmt(a, b); + break; case T_CreatedbStmt: retval = _equalCreatedbStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index cf32e91253..217b1a0465 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.575 2007/01/22 01:35:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.576 2007/01/23 05:07:17 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -152,11 +152,12 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt - CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt + CreateDomainStmt CreateGroupStmt CreateOpClassStmt + CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateAssertStmt CreateTrigStmt CreateUserStmt CreateRoleStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt - DropGroupStmt DropOpClassStmt DropPLangStmt DropStmt + DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt @@ -174,7 +175,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %type select_no_parens select_with_parens select_clause simple_select values_clause -%type alter_column_default opclass_item alter_using +%type alter_column_default opclass_item opclass_drop alter_using %type add_drop opt_asc_desc opt_nulls_order %type alter_table_cmd alter_rel_cmd @@ -229,7 +230,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) OptTableElementList TableElementList OptInherit definition OptWith opt_distinct opt_definition func_args func_args_list func_as createfunc_opt_list alterfunc_opt_list - aggr_args aggr_args_list old_aggr_definition old_aggr_list + aggr_args old_aggr_definition old_aggr_list oper_argtypes RuleActionList RuleActionMulti opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params @@ -240,10 +241,10 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) set_clause_list set_clause multiple_set_clause ctext_expr_list ctext_row def_list indirection opt_indirection group_clause TriggerFuncArgs select_limit - opt_select_limit opclass_item_list - transaction_mode_list_or_empty + opt_select_limit opclass_item_list opclass_drop_list + opt_opfamily transaction_mode_list_or_empty TableFuncElementList opt_type_modifiers - prep_type_clause prep_type_list + prep_type_clause execute_param_clause using_clause returning_clause %type into_clause OptTempTableName @@ -381,7 +382,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT - FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD + FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION GLOBAL GRANT GRANTED GREATEST GROUP_P @@ -548,6 +549,8 @@ stmt : | CreateFunctionStmt | CreateGroupStmt | CreateOpClassStmt + | CreateOpFamilyStmt + | AlterOpFamilyStmt | CreatePLangStmt | CreateSchemaStmt | CreateSeqStmt @@ -565,6 +568,7 @@ stmt : | DropCastStmt | DropGroupStmt | DropOpClassStmt + | DropOpFamilyStmt | DropOwnedStmt | DropPLangStmt | DropRuleStmt @@ -929,7 +933,7 @@ AlterGroupStmt: } ; -add_drop: ADD_P { $$ = +1; } +add_drop: ADD_P { $$ = +1; } | DROP { $$ = -1; } ; @@ -2879,15 +2883,10 @@ def_arg: func_type { $$ = (Node *)$1; } | Sconst { $$ = (Node *)makeString($1); } ; -aggr_args: '(' aggr_args_list ')' { $$ = $2; } +aggr_args: '(' type_list ')' { $$ = $2; } | '(' '*' ')' { $$ = NIL; } ; -aggr_args_list: - Typename { $$ = list_make1($1); } - | aggr_args_list ',' Typename { $$ = lappend($1, $3); } - ; - old_aggr_definition: '(' old_aggr_list ')' { $$ = $2; } ; @@ -2906,20 +2905,24 @@ old_aggr_elem: IDENT '=' def_arg * * QUERIES : * CREATE OPERATOR CLASS ... + * CREATE OPERATOR FAMILY ... + * ALTER OPERATOR FAMILY ... * DROP OPERATOR CLASS ... + * DROP OPERATOR FAMILY ... * *****************************************************************************/ CreateOpClassStmt: CREATE OPERATOR CLASS any_name opt_default FOR TYPE_P Typename - USING access_method AS opclass_item_list + USING access_method opt_opfamily AS opclass_item_list { CreateOpClassStmt *n = makeNode(CreateOpClassStmt); n->opclassname = $4; n->isDefault = $5; n->datatype = $8; n->amname = $10; - n->items = $12; + n->opfamilyname = $11; + n->items = $13; $$ = (Node *) n; } ; @@ -2959,6 +2962,16 @@ opclass_item: n->number = $2; $$ = (Node *) n; } + | FUNCTION Iconst '(' type_list ')' func_name func_args + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_FUNCTION; + n->name = $6; + n->args = extractArgTypes($7); + n->number = $2; + n->class_args = $4; + $$ = (Node *) n; + } | STORAGE Typename { CreateOpClassItem *n = makeNode(CreateOpClassItem); @@ -2968,12 +2981,72 @@ opclass_item: } ; -opt_default: DEFAULT { $$ = TRUE; } - | /*EMPTY*/ { $$ = FALSE; } +opt_default: DEFAULT { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } ; -opt_recheck: RECHECK { $$ = TRUE; } - | /*EMPTY*/ { $$ = FALSE; } +opt_opfamily: FAMILY any_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_recheck: RECHECK { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } + ; + + +CreateOpFamilyStmt: + CREATE OPERATOR FAMILY any_name USING access_method + { + CreateOpFamilyStmt *n = makeNode(CreateOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + $$ = (Node *) n; + } + ; + +AlterOpFamilyStmt: + ALTER OPERATOR FAMILY any_name USING access_method ADD_P opclass_item_list + { + AlterOpFamilyStmt *n = makeNode(AlterOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + n->isDrop = false; + n->items = $8; + $$ = (Node *) n; + } + | ALTER OPERATOR FAMILY any_name USING access_method DROP opclass_drop_list + { + AlterOpFamilyStmt *n = makeNode(AlterOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + n->isDrop = true; + n->items = $8; + $$ = (Node *) n; + } + ; + +opclass_drop_list: + opclass_drop { $$ = list_make1($1); } + | opclass_drop_list ',' opclass_drop { $$ = lappend($1, $3); } + ; + +opclass_drop: + OPERATOR Iconst '(' type_list ')' + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_OPERATOR; + n->number = $2; + n->args = $4; + $$ = (Node *) n; + } + | FUNCTION Iconst '(' type_list ')' + { + CreateOpClassItem *n = makeNode(CreateOpClassItem); + n->itemtype = OPCLASS_ITEM_FUNCTION; + n->number = $2; + n->args = $4; + $$ = (Node *) n; + } ; @@ -2998,6 +3071,28 @@ DropOpClassStmt: } ; +DropOpFamilyStmt: + DROP OPERATOR FAMILY any_name USING access_method opt_drop_behavior + { + RemoveOpFamilyStmt *n = makeNode(RemoveOpFamilyStmt); + n->opfamilyname = $4; + n->amname = $6; + n->behavior = $7; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior + { + RemoveOpFamilyStmt *n = makeNode(RemoveOpFamilyStmt); + n->opfamilyname = $6; + n->amname = $8; + n->behavior = $9; + n->missing_ok = true; + $$ = (Node *) n; + } + ; + + /***************************************************************************** * * QUERY: @@ -3201,6 +3296,15 @@ CommentStmt: n->comment = $9; $$ = (Node *) n; } + | COMMENT ON OPERATOR FAMILY any_name USING access_method IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_OPFAMILY; + n->objname = $5; + n->objargs = list_make1(makeString($7)); + n->comment = $9; + $$ = (Node *) n; + } | COMMENT ON LARGE_P OBJECT_P NumericOnly IS comment_text { CommentStmt *n = makeNode(CommentStmt); @@ -4115,9 +4219,9 @@ oper_argtypes: } | Typename ',' Typename { $$ = list_make2($1, $3); } - | NONE ',' Typename /* left unary */ + | NONE ',' Typename /* left unary */ { $$ = list_make2(NULL, $3); } - | Typename ',' NONE /* right unary */ + | Typename ',' NONE /* right unary */ { $$ = list_make2($1, NULL); } ; @@ -4174,8 +4278,8 @@ DropCastStmt: DROP CAST opt_if_exists '(' Typename AS Typename ')' opt_drop_beha } ; -opt_if_exists: IF_P EXISTS { $$ = true; } - | /*EMPTY*/ { $$ = false; } +opt_if_exists: IF_P EXISTS { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } ; @@ -4294,6 +4398,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->newname = $9; $$ = (Node *)n; } + | ALTER OPERATOR FAMILY any_name USING access_method RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_OPFAMILY; + n->object = $4; + n->subname = $6; + n->newname = $9; + $$ = (Node *)n; + } | ALTER SCHEMA name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -4493,6 +4606,15 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $9; $$ = (Node *)n; } + | ALTER OPERATOR FAMILY any_name USING access_method OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_OPFAMILY; + n->object = $4; + n->addname = $6; + n->newowner = $9; + $$ = (Node *)n; + } | ALTER SCHEMA name OWNER TO RoleId { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); @@ -5302,15 +5424,10 @@ PrepareStmt: PREPARE name prep_type_clause AS PreparableStmt } ; -prep_type_clause: '(' prep_type_list ')' { $$ = $2; } +prep_type_clause: '(' type_list ')' { $$ = $2; } | /* EMPTY */ { $$ = NIL; } ; -prep_type_list: Typename { $$ = list_make1($1); } - | prep_type_list ',' Typename - { $$ = lappend($1, $3); } - ; - PreparableStmt: SelectStmt | InsertStmt @@ -7968,14 +8085,8 @@ extract_list: | /*EMPTY*/ { $$ = NIL; } ; -type_list: type_list ',' Typename - { - $$ = lappend($1, $3); - } - | Typename - { - $$ = list_make1($1); - } +type_list: Typename { $$ = list_make1($1); } + | type_list ',' Typename { $$ = lappend($1, $3); } ; array_expr_list: array_expr @@ -8604,6 +8715,7 @@ unreserved_keyword: | EXECUTE | EXPLAIN | EXTERNAL + | FAMILY | FETCH | FIRST_P | FORCE diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 5e858d10b0..b8607c7c00 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.182 2007/01/22 01:35:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.183 2007/01/23 05:07:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -145,6 +145,7 @@ static const ScanKeyword ScanKeywords[] = { {"external", EXTERNAL}, {"extract", EXTRACT}, {"false", FALSE_P}, + {"family", FAMILY}, {"fetch", FETCH}, {"first", FIRST_P}, {"float", FLOAT_P}, diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 19b89916bf..613ef653d1 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.270 2007/01/05 22:19:39 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.271 2007/01/23 05:07:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -322,6 +322,8 @@ check_xact_readonly(Node *parsetree) case T_IndexStmt: case T_CreatePLangStmt: case T_CreateOpClassStmt: + case T_CreateOpFamilyStmt: + case T_AlterOpFamilyStmt: case T_RuleStmt: case T_CreateSchemaStmt: case T_CreateSeqStmt: @@ -338,6 +340,7 @@ check_xact_readonly(Node *parsetree) case T_DropRoleStmt: case T_DropPLangStmt: case T_RemoveOpClassStmt: + case T_RemoveOpFamilyStmt: case T_DropPropertyStmt: case T_GrantStmt: case T_GrantRoleStmt: @@ -1099,10 +1102,22 @@ ProcessUtility(Node *parsetree, DefineOpClass((CreateOpClassStmt *) parsetree); break; + case T_CreateOpFamilyStmt: + DefineOpFamily((CreateOpFamilyStmt *) parsetree); + break; + + case T_AlterOpFamilyStmt: + AlterOpFamily((AlterOpFamilyStmt *) parsetree); + break; + case T_RemoveOpClassStmt: RemoveOpClass((RemoveOpClassStmt *) parsetree); break; + case T_RemoveOpFamilyStmt: + RemoveOpFamily((RemoveOpFamilyStmt *) parsetree); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(parsetree)); @@ -1445,6 +1460,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_OPCLASS: tag = "ALTER OPERATOR CLASS"; break; + case OBJECT_OPFAMILY: + tag = "ALTER OPERATOR FAMILY"; + break; case OBJECT_ROLE: tag = "ALTER ROLE"; break; @@ -1518,6 +1536,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_OPCLASS: tag = "ALTER OPERATOR CLASS"; break; + case OBJECT_OPFAMILY: + tag = "ALTER OPERATOR FAMILY"; + break; case OBJECT_SCHEMA: tag = "ALTER SCHEMA"; break; @@ -1777,10 +1798,22 @@ CreateCommandTag(Node *parsetree) tag = "CREATE OPERATOR CLASS"; break; + case T_CreateOpFamilyStmt: + tag = "CREATE OPERATOR FAMILY"; + break; + + case T_AlterOpFamilyStmt: + tag = "ALTER OPERATOR FAMILY"; + break; + case T_RemoveOpClassStmt: tag = "DROP OPERATOR CLASS"; break; + case T_RemoveOpFamilyStmt: + tag = "DROP OPERATOR FAMILY"; + break; + case T_PrepareStmt: tag = "PREPARE"; break; @@ -2147,10 +2180,22 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_CreateOpFamilyStmt: + lev = LOGSTMT_DDL; + break; + + case T_AlterOpFamilyStmt: + lev = LOGSTMT_DDL; + break; + case T_RemoveOpClassStmt: lev = LOGSTMT_DDL; break; + case T_RemoveOpFamilyStmt: + lev = LOGSTMT_DDL; + break; + case T_PrepareStmt: { PrepareStmt *stmt = (PrepareStmt *) parsetree; diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 9c254b0b48..3d665ff5c2 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.79 2007/01/05 22:19:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.80 2007/01/23 05:07:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,13 +79,18 @@ extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId); /* commands/opclasscmds.c */ extern void DefineOpClass(CreateOpClassStmt *stmt); +extern void DefineOpFamily(CreateOpFamilyStmt *stmt); +extern void AlterOpFamily(AlterOpFamilyStmt *stmt); extern void RemoveOpClass(RemoveOpClassStmt *stmt); +extern void RemoveOpFamily(RemoveOpFamilyStmt *stmt); extern void RemoveOpClassById(Oid opclassOid); extern void RemoveOpFamilyById(Oid opfamilyOid); extern void RemoveAmOpEntryById(Oid entryOid); extern void RemoveAmProcEntryById(Oid entryOid); extern void RenameOpClass(List *name, const char *access_method, const char *newname); +extern void RenameOpFamily(List *name, const char *access_method, const char *newname); extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId); +extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId); /* support routines in commands/define.c */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index d3e84bdf69..f3762facdd 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.192 2007/01/20 20:45:40 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.193 2007/01/23 05:07:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -286,7 +286,10 @@ typedef enum NodeTag T_CreateCastStmt, T_DropCastStmt, T_CreateOpClassStmt, + T_CreateOpFamilyStmt, + T_AlterOpFamilyStmt, T_RemoveOpClassStmt, + T_RemoveOpFamilyStmt, T_PrepareStmt, T_ExecuteStmt, T_DeallocateStmt, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d11f9ae6ea..a252308bdb 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.338 2007/01/09 02:14:15 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.339 2007/01/23 05:07:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -852,6 +852,7 @@ typedef enum ObjectType OBJECT_LARGEOBJECT, OBJECT_OPCLASS, OBJECT_OPERATOR, + OBJECT_OPFAMILY, OBJECT_ROLE, OBJECT_RULE, OBJECT_SCHEMA, @@ -1194,7 +1195,7 @@ typedef struct DropTableSpaceStmt { NodeTag type; char *tablespacename; - bool missing_ok; /* skip error if a missing? */ + bool missing_ok; /* skip error if missing? */ } DropTableSpaceStmt; /* ---------------------- @@ -1362,10 +1363,35 @@ typedef struct CreateOpClassItem List *args; /* argument types */ int number; /* strategy num or support proc num */ bool recheck; /* only used for operators */ + List *class_args; /* only used for functions */ /* fields used for a storagetype item: */ TypeName *storedtype; /* datatype stored in index */ } CreateOpClassItem; +/* ---------------------- + * Create Operator Family Statement + * ---------------------- + */ +typedef struct CreateOpFamilyStmt +{ + NodeTag type; + List *opfamilyname; /* qualified name (list of Value strings) */ + char *amname; /* name of index AM opfamily is for */ +} CreateOpFamilyStmt; + +/* ---------------------- + * Alter Operator Family Statement + * ---------------------- + */ +typedef struct AlterOpFamilyStmt +{ + NodeTag type; + List *opfamilyname; /* qualified name (list of Value strings) */ + char *amname; /* name of index AM opfamily is for */ + bool isDrop; /* ADD or DROP the items? */ + List *items; /* List of CreateOpClassItem nodes */ +} AlterOpFamilyStmt; + /* ---------------------- * Drop Table|Sequence|View|Index|Type|Domain|Conversion|Schema Statement * ---------------------- @@ -1395,7 +1421,7 @@ typedef struct DropPropertyStmt char *property; /* name of rule, trigger, etc */ ObjectType removeType; /* OBJECT_RULE or OBJECT_TRIGGER */ DropBehavior behavior; /* RESTRICT or CASCADE behavior */ - bool missing_ok; /* skip error if a missing? */ + bool missing_ok; /* skip error if missing? */ } DropPropertyStmt; /* ---------------------- @@ -1546,7 +1572,7 @@ typedef struct RemoveFuncStmt List *name; /* qualified name of object to drop */ List *args; /* types of the arguments */ DropBehavior behavior; /* RESTRICT or CASCADE behavior */ - bool missing_ok; /* skip error if a missing? */ + bool missing_ok; /* skip error if missing? */ } RemoveFuncStmt; /* ---------------------- @@ -1559,9 +1585,22 @@ typedef struct RemoveOpClassStmt List *opclassname; /* qualified name (list of Value strings) */ char *amname; /* name of index AM opclass is for */ DropBehavior behavior; /* RESTRICT or CASCADE behavior */ - bool missing_ok; /* skip error if a missing? */ + bool missing_ok; /* skip error if missing? */ } RemoveOpClassStmt; +/* ---------------------- + * Drop Operator Family Statement + * ---------------------- + */ +typedef struct RemoveOpFamilyStmt +{ + NodeTag type; + List *opfamilyname; /* qualified name (list of Value strings) */ + char *amname; /* name of index AM opfamily is for */ + DropBehavior behavior; /* RESTRICT or CASCADE behavior */ + bool missing_ok; /* skip error if missing? */ +} RemoveOpFamilyStmt; + /* ---------------------- * Alter Object Rename Statement * ---------------------- @@ -1917,7 +1956,7 @@ typedef struct DropCastStmt TypeName *sourcetype; TypeName *targettype; DropBehavior behavior; - bool missing_ok; /* skip error if a missing? */ + bool missing_ok; /* skip error if missing? */ } DropCastStmt; diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 2076a69f3d..14522bc6fb 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.99 2007/01/05 22:19:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.100 2007/01/23 05:07:18 tgl Exp $ * * NOTES * An ACL array is simply an array of AclItems, representing the union @@ -178,6 +178,7 @@ typedef enum AclObjectKind ACL_KIND_LANGUAGE, /* pg_language */ ACL_KIND_NAMESPACE, /* pg_namespace */ ACL_KIND_OPCLASS, /* pg_opclass */ + ACL_KIND_OPFAMILY, /* pg_opfamily */ ACL_KIND_CONVERSION, /* pg_conversion */ ACL_KIND_TABLESPACE, /* pg_tablespace */ MAX_ACL_KIND /* MUST BE LAST */ @@ -276,6 +277,7 @@ extern bool pg_proc_ownercheck(Oid proc_oid, Oid roleid); extern bool pg_namespace_ownercheck(Oid nsp_oid, Oid roleid); extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid); extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid); +extern bool pg_opfamily_ownercheck(Oid opf_oid, Oid roleid); extern bool pg_database_ownercheck(Oid db_oid, Oid roleid); extern bool pg_conversion_ownercheck(Oid conv_oid, Oid roleid);