]> granicus.if.org Git - postgresql/commitdiff
Add ALTER OPERATOR command, for changing selectivity estimator functions.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 14 Jul 2015 15:17:55 +0000 (18:17 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 14 Jul 2015 15:17:55 +0000 (18:17 +0300)
Other options cannot be changed, as it's not totally clear if cached plans
would need to be invalidated if one of the other options change. Selectivity
estimator functions only change plan costs, not correctness of plans, so
those should be safe.

Original patch by Uriy Zhuravlev, heavily edited by me.

13 files changed:
doc/src/sgml/ref/alter_operator.sgml
src/backend/commands/operatorcmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/include/commands/defrem.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/test/regress/expected/alter_operator.out [new file with mode: 0644]
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/alter_operator.sql [new file with mode: 0644]

index 8a7af50d6049c479662530b48b02d565dbdf62fc..b2eaa7a263e937351ed36e54bbd733bcfc49bfec 100644 (file)
@@ -26,6 +26,11 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla
 
 ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , { <replaceable>right_type</replaceable> | NONE } )
     SET SCHEMA <replaceable>new_schema</replaceable>
+
+ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</replaceable> | NONE } , { <replaceable>right_type</replaceable> | NONE } )
+    SET ( {  RESTRICT = { <replaceable class="parameter">res_proc</replaceable> | NONE }
+           | JOIN = { <replaceable class="parameter">join_proc</replaceable> | NONE }
+         } [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -34,8 +39,7 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla
 
   <para>
    <command>ALTER OPERATOR</command> changes the definition of
-   an operator.  The only currently available functionality is to change the
-   owner of the operator.
+   an operator.
   </para>
 
   <para>
@@ -98,6 +102,25 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+     <term><replaceable class="parameter">res_proc</replaceable></term>
+     <listitem>
+       <para>
+         The restriction selectivity estimator function for this operator; write NONE to remove existing selectivity estimator.
+       </para>
+      </listitem>
+   </varlistentry>
+
+   <varlistentry>
+     <term><replaceable class="parameter">join_proc</replaceable></term>
+     <listitem>
+       <para>
+         The join selectivity estimator function for this operator; write NONE to remove existing selectivity estimator.
+       </para>
+     </listitem>
+   </varlistentry>
+
   </variablelist>
  </refsect1>
 
@@ -109,6 +132,13 @@ ALTER OPERATOR <replaceable>name</replaceable> ( { <replaceable>left_type</repla
 <programlisting>
 ALTER OPERATOR @@ (text, text) OWNER TO joe;
 </programlisting></para>
+
+  <para>
+    Change the restriction and join selectivity estimator functions of a custom operator <literal>a && b</literal> for type <type>int[]</type>:
+<programlisting>
+ALTER OPERATOR && (_int4, _int4) SET (RESTRICT = _int_contsel, JOIN = _int_contjoinsel);
+</programlisting></para>
+
  </refsect1>
 
  <refsect1>
index b4a1aac3a14f5a6292b0cbc6d0da574affa35b4b..32065185ea4587ef8e051946f9a8f59026ee4296 100644 (file)
@@ -51,6 +51,9 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+static Oid     ValidateRestrictionEstimator(List *restrictionName);
+static Oid     ValidateJoinEstimator(List *joinName);
+
 /*
  * DefineOperator
  *             this function extracts all the information from the
@@ -80,7 +83,7 @@ DefineOperator(List *names, List *parameters)
        Oid                     functionOid;    /* functions converted to OID */
        Oid                     restrictionOid;
        Oid                     joinOid;
-       Oid                     typeId[5];              /* only need up to 5 args here */
+       Oid                     typeId[2];              /* to hold left and right arg */
        int                     nargs;
        ListCell   *pl;
 
@@ -140,10 +143,13 @@ DefineOperator(List *names, List *parameters)
                else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
                        canMerge = true;
                else
+               {
+                       /* WARNING, not ERROR, for historical backwards-compatibility */
                        ereport(WARNING,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("operator attribute \"%s\" not recognized",
                                                        defel->defname)));
+               }
        }
 
        /*
@@ -216,69 +222,14 @@ DefineOperator(List *names, List *parameters)
                aclcheck_error_type(aclresult, rettype);
 
        /*
-        * Look up restriction estimator if specified
+        * Look up restriction and join estimators if specified
         */
        if (restrictionName)
-       {
-               typeId[0] = INTERNALOID;        /* PlannerInfo */
-               typeId[1] = OIDOID;             /* operator OID */
-               typeId[2] = INTERNALOID;        /* args list */
-               typeId[3] = INT4OID;    /* varRelid */
-
-               restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
-
-               /* estimators must return float8 */
-               if (get_func_rettype(restrictionOid) != FLOAT8OID)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                        errmsg("restriction estimator function %s must return type \"float8\"",
-                                                       NameListToString(restrictionName))));
-
-               /* Require EXECUTE rights for the estimator */
-               aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
-               if (aclresult != ACLCHECK_OK)
-                       aclcheck_error(aclresult, ACL_KIND_PROC,
-                                                  NameListToString(restrictionName));
-       }
+               restrictionOid = ValidateRestrictionEstimator(restrictionName);
        else
                restrictionOid = InvalidOid;
-
-       /*
-        * Look up join estimator if specified
-        */
        if (joinName)
-       {
-               typeId[0] = INTERNALOID;        /* PlannerInfo */
-               typeId[1] = OIDOID;             /* operator OID */
-               typeId[2] = INTERNALOID;        /* args list */
-               typeId[3] = INT2OID;    /* jointype */
-               typeId[4] = INTERNALOID;        /* SpecialJoinInfo */
-
-               /*
-                * As of Postgres 8.4, the preferred signature for join estimators has
-                * 5 arguments, but we still allow the old 4-argument form. Try the
-                * preferred form first.
-                */
-               joinOid = LookupFuncName(joinName, 5, typeId, true);
-               if (!OidIsValid(joinOid))
-                       joinOid = LookupFuncName(joinName, 4, typeId, true);
-               /* If not found, reference the 5-argument signature in error msg */
-               if (!OidIsValid(joinOid))
-                       joinOid = LookupFuncName(joinName, 5, typeId, false);
-
-               /* estimators must return float8 */
-               if (get_func_rettype(joinOid) != FLOAT8OID)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                        errmsg("join estimator function %s must return type \"float8\"",
-                                       NameListToString(joinName))));
-
-               /* Require EXECUTE rights for the estimator */
-               aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
-               if (aclresult != ACLCHECK_OK)
-                       aclcheck_error(aclresult, ACL_KIND_PROC,
-                                                  NameListToString(joinName));
-       }
+               joinOid = ValidateJoinEstimator(joinName);
        else
                joinOid = InvalidOid;
 
@@ -299,6 +250,87 @@ DefineOperator(List *names, List *parameters)
                                           canHash);    /* operator hashes */
 }
 
+/*
+ * Look up a restriction estimator function ny name, and verify that it has
+ * the correct signature and we have the permissions to attach it to an
+ * operator.
+ */
+static Oid
+ValidateRestrictionEstimator(List *restrictionName)
+{
+       Oid                     typeId[4];
+       Oid                     restrictionOid;
+       AclResult       aclresult;
+
+       typeId[0] = INTERNALOID;        /* PlannerInfo */
+       typeId[1] = OIDOID;                     /* operator OID */
+       typeId[2] = INTERNALOID;        /* args list */
+       typeId[3] = INT4OID;            /* varRelid */
+
+       restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
+
+       /* estimators must return float8 */
+       if (get_func_rettype(restrictionOid) != FLOAT8OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("restriction estimator function %s must return type \"float8\"",
+                                               NameListToString(restrictionName))));
+
+       /* Require EXECUTE rights for the estimator */
+       aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_PROC,
+                                          NameListToString(restrictionName));
+
+       return restrictionOid;
+}
+
+/*
+ * Look up a join estimator function ny name, and verify that it has the
+ * correct signature and we have the permissions to attach it to an
+ * operator.
+ */
+static Oid
+ValidateJoinEstimator(List *joinName)
+{
+       Oid                     typeId[5];
+       Oid                     joinOid;
+       AclResult       aclresult;
+
+       typeId[0] = INTERNALOID;        /* PlannerInfo */
+       typeId[1] = OIDOID;                     /* operator OID */
+       typeId[2] = INTERNALOID;        /* args list */
+       typeId[3] = INT2OID;            /* jointype */
+       typeId[4] = INTERNALOID;        /* SpecialJoinInfo */
+
+       /*
+        * As of Postgres 8.4, the preferred signature for join estimators has 5
+        * arguments, but we still allow the old 4-argument form. Try the
+        * preferred form first.
+        */
+       joinOid = LookupFuncName(joinName, 5, typeId, true);
+       if (!OidIsValid(joinOid))
+               joinOid = LookupFuncName(joinName, 4, typeId, true);
+       /* If not found, reference the 5-argument signature in error msg */
+       if (!OidIsValid(joinOid))
+               joinOid = LookupFuncName(joinName, 5, typeId, false);
+
+       /* estimators must return float8 */
+       if (get_func_rettype(joinOid) != FLOAT8OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                        errmsg("join estimator function %s must return type \"float8\"",
+                                       NameListToString(joinName))));
+
+       /* Require EXECUTE rights for the estimator */
+       aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_PROC,
+                                          NameListToString(joinName));
+
+       return joinOid;
+}
+
 /*
  * Guts of operator deletion.
  */
@@ -320,3 +352,154 @@ RemoveOperatorById(Oid operOid)
 
        heap_close(relation, RowExclusiveLock);
 }
+
+/*
+ * AlterOperator
+ *             routine implementing ALTER OPERATOR <operator> SET (option = ...).
+ *
+ * Currently, only RESTRICT and JOIN estimator functions can be changed.
+ */
+ObjectAddress
+AlterOperator(AlterOperatorStmt *stmt)
+{
+       ObjectAddress address;
+       Oid                     oprId;
+       Relation        catalog;
+       HeapTuple       tup;
+       Form_pg_operator oprForm;
+       int                     i;
+       ListCell   *pl;
+       Datum           values[Natts_pg_operator];
+       bool            nulls[Natts_pg_operator];
+       bool            replaces[Natts_pg_operator];
+       List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
+       bool            updateRestriction = false;
+       Oid                     restrictionOid;
+       List       *joinName = NIL; /* optional join sel. procedure */
+       bool            updateJoin = false;
+       Oid                     joinOid;
+
+       /* Look up the operator */
+       oprId = LookupOperNameTypeNames(NULL, stmt->opername,
+                                                                       (TypeName *) linitial(stmt->operargs),
+                                                                       (TypeName *) lsecond(stmt->operargs),
+                                                                       false, -1);
+       catalog = heap_open(OperatorRelationId, RowExclusiveLock);
+       tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
+       if (tup == NULL)
+               elog(ERROR, "cache lookup failed for operator %u", oprId);
+       oprForm = (Form_pg_operator) GETSTRUCT(tup);
+
+       /* Process options */
+       foreach(pl, stmt->options)
+       {
+               DefElem    *defel = (DefElem *) lfirst(pl);
+               List       *param;
+
+               if (defel->arg == NULL)
+                       param = NIL;            /* NONE, removes the function */
+               else
+                       param = defGetQualifiedName(defel);
+
+               if (pg_strcasecmp(defel->defname, "restrict") == 0)
+               {
+                       restrictionName = param;
+                       updateRestriction = true;
+               }
+               else if (pg_strcasecmp(defel->defname, "join") == 0)
+               {
+                       joinName = param;
+                       updateJoin = true;
+               }
+
+               /*
+                * The rest of the options that CREATE accepts cannot be changed.
+                * Check for them so that we can give a meaningful error message.
+                */
+               else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
+                                pg_strcasecmp(defel->defname, "rightarg") == 0 ||
+                                pg_strcasecmp(defel->defname, "procedure") == 0 ||
+                                pg_strcasecmp(defel->defname, "commutator") == 0 ||
+                                pg_strcasecmp(defel->defname, "negator") == 0 ||
+                                pg_strcasecmp(defel->defname, "hashes") == 0 ||
+                                pg_strcasecmp(defel->defname, "merges") == 0)
+               {
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("operator attribute \"%s\" can not be changed",
+                                                       defel->defname)));
+               }
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("operator attribute \"%s\" not recognized",
+                                                       defel->defname)));
+       }
+
+       /* Check permissions. Must be owner. */
+       if (!pg_oper_ownercheck(oprId, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+                                          NameStr(oprForm->oprname));
+
+       /*
+        * Look up restriction and join estimators if specified
+        */
+       if (restrictionName)
+               restrictionOid = ValidateRestrictionEstimator(restrictionName);
+       else
+               restrictionOid = InvalidOid;
+       if (joinName)
+               joinOid = ValidateJoinEstimator(joinName);
+       else
+               joinOid = InvalidOid;
+
+       /* Perform additional checks, like OperatorCreate does */
+       if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
+       {
+               /* If it's not a binary op, these things mustn't be set: */
+               if (OidIsValid(joinOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                errmsg("only binary operators can have join selectivity")));
+       }
+
+       if (oprForm->oprresult != BOOLOID)
+       {
+               if (OidIsValid(restrictionOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only boolean operators can have restriction selectivity")));
+               if (OidIsValid(joinOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                               errmsg("only boolean operators can have join selectivity")));
+       }
+
+       /* Update the tuple */
+       for (i = 0; i < Natts_pg_operator; ++i)
+       {
+               values[i] = (Datum) 0;
+               replaces[i] = false;
+               nulls[i] = false;
+       }
+       if (updateRestriction)
+       {
+               replaces[Anum_pg_operator_oprrest - 1] = true;
+               values[Anum_pg_operator_oprrest - 1] = restrictionOid;
+       }
+       if (updateJoin)
+       {
+               replaces[Anum_pg_operator_oprjoin - 1] = true;
+               values[Anum_pg_operator_oprjoin - 1] = joinOid;
+       }
+
+       tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
+                                                       values, nulls, replaces);
+
+       simple_heap_update(catalog, &tup->t_self, tup);
+       CatalogUpdateIndexes(catalog, tup);
+
+       heap_close(catalog, RowExclusiveLock);
+
+       return address;
+}
index 4c363d3d39a9e7b9cbb3ad7018de18e4896436b8..6a08c2db211b4e65a103b4aacf5e52c5f41b5adc 100644 (file)
@@ -3212,6 +3212,18 @@ _copyAlterOwnerStmt(const AlterOwnerStmt *from)
        return newnode;
 }
 
+static AlterOperatorStmt *
+_copyAlterOperatorStmt(const AlterOperatorStmt *from)
+{
+       AlterOperatorStmt *newnode = makeNode(AlterOperatorStmt);
+
+       COPY_NODE_FIELD(opername);
+       COPY_NODE_FIELD(operargs);
+       COPY_NODE_FIELD(options);
+
+       return newnode;
+}
+
 static RuleStmt *
 _copyRuleStmt(const RuleStmt *from)
 {
@@ -4615,6 +4627,9 @@ copyObject(const void *from)
                case T_AlterOwnerStmt:
                        retval = _copyAlterOwnerStmt(from);
                        break;
+               case T_AlterOperatorStmt:
+                       retval = _copyAlterOperatorStmt(from);
+                       break;
                case T_RuleStmt:
                        retval = _copyRuleStmt(from);
                        break;
index f19251e7c415fb3a92b83a2f1e086fb1ea29a8f7..faf5eedab4ed4b7412970b82621d6a42704d008c 100644 (file)
@@ -1338,6 +1338,16 @@ _equalAlterOwnerStmt(const AlterOwnerStmt *a, const AlterOwnerStmt *b)
        return true;
 }
 
+static bool
+_equalAlterOperatorStmt(const AlterOperatorStmt *a, const AlterOperatorStmt *b)
+{
+       COMPARE_NODE_FIELD(opername);
+       COMPARE_NODE_FIELD(operargs);
+       COMPARE_NODE_FIELD(options);
+
+       return true;
+}
+
 static bool
 _equalRuleStmt(const RuleStmt *a, const RuleStmt *b)
 {
@@ -2980,6 +2990,9 @@ equal(const void *a, const void *b)
                case T_AlterOwnerStmt:
                        retval = _equalAlterOwnerStmt(a, b);
                        break;
+               case T_AlterOperatorStmt:
+                       retval = _equalAlterOperatorStmt(a, b);
+                       break;
                case T_RuleStmt:
                        retval = _equalRuleStmt(a, b);
                        break;
index e0ff6f16a2181244aadf3443959a82c21ca6b8d2..2b02a2e523380cf2a12d2171c63b4ed887cb7285 100644 (file)
@@ -232,7 +232,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
                AlterEventTrigStmt
                AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
                AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
-               AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
+               AlterObjectSchemaStmt AlterOwnerStmt AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
                AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
                AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
                AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
@@ -359,7 +359,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
                                any_operator expr_list attrs
                                target_list opt_target_list insert_column_list set_target_list
                                set_clause_list set_clause multiple_set_clause
-                               ctext_expr_list ctext_row def_list indirection opt_indirection
+                               ctext_expr_list ctext_row def_list operator_def_list indirection opt_indirection
                                reloption_list group_clause TriggerFuncArgs select_limit
                                opt_select_limit opclass_item_list opclass_drop_list
                                opclass_purpose opt_opfamily transaction_mode_list_or_empty
@@ -432,7 +432,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <node>   TableElement TypedTableElement ConstraintElem TableFuncElement
 %type <node>   columnDef columnOptions
-%type <defelt> def_elem reloption_elem old_aggr_elem
+%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
 %type <node>   def_arg columnElem where_clause where_or_current_clause
                                a_expr b_expr c_expr AexprConst indirection_el
                                columnref in_expr having_clause func_table array_expr
@@ -769,6 +769,7 @@ stmt :
                        | AlterGroupStmt
                        | AlterObjectSchemaStmt
                        | AlterOwnerStmt
+                       | AlterOperatorStmt
                        | AlterPolicyStmt
                        | AlterSeqStmt
                        | AlterSystemStmt
@@ -8196,6 +8197,33 @@ AlterObjectSchemaStmt:
                                }
                ;
 
+/*****************************************************************************
+ *
+ * ALTER OPERATOR name SET define
+ *
+ *****************************************************************************/
+
+AlterOperatorStmt:
+                       ALTER OPERATOR any_operator oper_argtypes SET '(' operator_def_list ')'
+                               {
+                                       AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+                                       n->opername = $3;
+                                       n->operargs = $4;
+                                       n->options = $7;
+                                       $$ = (Node *)n;
+                               }
+               ;
+
+operator_def_list:     operator_def_elem                                                               { $$ = list_make1($1); }
+                       | operator_def_list ',' operator_def_elem                               { $$ = lappend($1, $3); }
+               ;
+
+operator_def_elem: ColLabel '=' NONE
+                                               { $$ = makeDefElem($1, NULL); }
+                                  | ColLabel '=' def_arg
+                                               { $$ = makeDefElem($1, (Node *) $3); }
+               ;
+
 /*****************************************************************************
  *
  * ALTER THING name OWNER TO newname
index 0dabcc130e013d779e947fb69b55fae21daf6776..e81bbc6c9d19bb9acedc00b92757c2a09d84fcd5 100644 (file)
@@ -149,6 +149,7 @@ check_xact_readonly(Node *parsetree)
                case T_AlterRoleSetStmt:
                case T_AlterObjectSchemaStmt:
                case T_AlterOwnerStmt:
+               case T_AlterOperatorStmt:
                case T_AlterSeqStmt:
                case T_AlterTableMoveAllStmt:
                case T_AlterTableStmt:
@@ -1481,6 +1482,10 @@ ProcessUtilitySlow(Node *parsetree,
                                address = ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
                                break;
 
+                       case T_AlterOperatorStmt:
+                               address = AlterOperator((AlterOperatorStmt *) parsetree);
+                               break;
+
                        case T_CommentStmt:
                                address = CommentObject((CommentStmt *) parsetree);
                                break;
@@ -2494,6 +2499,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "ALTER OPERATOR FAMILY";
                        break;
 
+               case T_AlterOperatorStmt:
+                       tag = "ALTER OPERATOR";
+                       break;
+
                case T_AlterTSDictionaryStmt:
                        tag = "ALTER TEXT SEARCH DICTIONARY";
                        break;
index 9b81c16d8239107c7b7aa40ec210136edbef9feb..adae296f527958483adf0a08fcf8df0534526b1b 100644 (file)
@@ -73,6 +73,7 @@ extern void interpret_function_parameter_list(List *parameters,
 /* commands/operatorcmds.c */
 extern ObjectAddress DefineOperator(List *names, List *parameters);
 extern void RemoveOperatorById(Oid operOid);
+extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
 
 /* commands/aggregatecmds.c */
 extern ObjectAddress DefineAggregate(List *name, List *args, bool oldstyle,
index 290cdb30585170d8f5a6b741b21247a03b90525c..f8acda4eede0190e444d0d37e84cd7743884d4e7 100644 (file)
@@ -347,6 +347,7 @@ typedef enum NodeTag
        T_DropTableSpaceStmt,
        T_AlterObjectSchemaStmt,
        T_AlterOwnerStmt,
+       T_AlterOperatorStmt,
        T_DropOwnedStmt,
        T_ReassignOwnedStmt,
        T_CompositeTypeStmt,
index a567c50da7279c960b4c4ca503b1b81854427cc8..5aaf5ec9e8edd55f567908d97c5b7d05357858ac 100644 (file)
@@ -2543,6 +2543,19 @@ typedef struct AlterOwnerStmt
 } AlterOwnerStmt;
 
 
+/* ----------------------
+ *             Alter Operator Set Restrict, Join
+ * ----------------------
+ */
+typedef struct AlterOperatorStmt
+{
+       NodeTag         type;
+       List       *opername;           /* operator name */
+       List       *operargs;           /* operator's argument TypeNames */
+       List       *options;            /* List of DefElem nodes */
+} AlterOperatorStmt;
+
+
 /* ----------------------
  *             Create Rule Statement
  * ----------------------
diff --git a/src/test/regress/expected/alter_operator.out b/src/test/regress/expected/alter_operator.out
new file mode 100644 (file)
index 0000000..2b99654
--- /dev/null
@@ -0,0 +1,76 @@
+CREATE OR REPLACE FUNCTION alter_op_test_fn(boolean, boolean)
+RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR === (
+    LEFTARG = boolean,
+    RIGHTARG = boolean,
+    PROCEDURE = alter_op_test_fn,
+    COMMUTATOR = ===,
+    NEGATOR = !==,
+    RESTRICT = contsel,
+    JOIN = contjoinsel,
+    HASHES, MERGES
+);
+--
+-- Reset and set params
+--
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = NONE);
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+ oprrest | oprjoin 
+---------+---------
+ -       | -
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel);
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+ oprrest |   oprjoin   
+---------+-------------
+ contsel | contjoinsel
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE, JOIN = NONE);
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+ oprrest | oprjoin 
+---------+---------
+ -       | -
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel, JOIN = contjoinsel);
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+ oprrest |   oprjoin   
+---------+-------------
+ contsel | contjoinsel
+(1 row)
+
+--
+-- Test invalid options.
+--
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====);
+ERROR:  operator attribute "commutator" can not be changed
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====);
+ERROR:  operator attribute "negator" can not be changed
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = non_existent_func);
+ERROR:  function non_existent_func(internal, oid, internal, integer) does not exist
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = non_existent_func);
+ERROR:  function non_existent_func(internal, oid, internal, smallint, internal) does not exist
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==);
+ERROR:  operator attribute "commutator" can not be changed
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==);
+ERROR:  operator attribute "negator" can not be changed
+--
+-- Test permission check. Must be owner to ALTER OPERATOR.
+--
+CREATE USER regtest_alter_user;
+SET SESSION AUTHORIZATION regtest_alter_user_user;
+ERROR:  role "regtest_alter_user_user" does not exist
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE);
+RESET SESSION AUTHORIZATION;
+-- Clean up
+DROP USER regression_alter_user;
+ERROR:  role "regression_alter_user" does not exist
+DROP OPERATOR === (boolean, boolean);
index 91780cdcc7398ac2ca7f9a05e45bab4bb2c93998..4df15def6e25921aa2a67453094869cb4884aca9 100644 (file)
@@ -89,7 +89,7 @@ test: brin gin gist spgist privileges security_label collate matview lock replic
 # ----------
 # Another group of parallel tests
 # ----------
-test: alter_generic misc psql async
+test: alter_generic alter_operator misc psql async
 
 # rules cannot run concurrently with any test that creates a view
 test: rules
index a2e0cebbdb500a0b6dd8d3d7436e7afe6eca021e..3a607cff46c235ff8b15fdbd095c2098ca3ac217 100644 (file)
@@ -111,6 +111,7 @@ test: replica_identity
 test: rowsecurity
 test: object_address
 test: alter_generic
+test: alter_operator
 test: misc
 test: psql
 test: async
diff --git a/src/test/regress/sql/alter_operator.sql b/src/test/regress/sql/alter_operator.sql
new file mode 100644 (file)
index 0000000..5350527
--- /dev/null
@@ -0,0 +1,64 @@
+CREATE OR REPLACE FUNCTION alter_op_test_fn(boolean, boolean)
+RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE sql IMMUTABLE;
+
+CREATE OPERATOR === (
+    LEFTARG = boolean,
+    RIGHTARG = boolean,
+    PROCEDURE = alter_op_test_fn,
+    COMMUTATOR = ===,
+    NEGATOR = !==,
+    RESTRICT = contsel,
+    JOIN = contjoinsel,
+    HASHES, MERGES
+);
+
+--
+-- Reset and set params
+--
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = NONE);
+
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel);
+
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE, JOIN = NONE);
+
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel, JOIN = contjoinsel);
+
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '==='
+  AND oprleft = 'boolean'::regtype AND oprright = 'boolean'::regtype;
+
+--
+-- Test invalid options.
+--
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====);
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = non_existent_func);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = non_existent_func);
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==);
+
+
+--
+-- Test permission check. Must be owner to ALTER OPERATOR.
+--
+CREATE USER regtest_alter_user;
+SET SESSION AUTHORIZATION regtest_alter_user_user;
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NONE);
+
+RESET SESSION AUTHORIZATION;
+
+-- Clean up
+DROP USER regression_alter_user;
+DROP OPERATOR === (boolean, boolean);