diff --git a/doc/src/sgml/ref/alter_operator.sgml b/doc/src/sgml/ref/alter_operator.sgml index 8a7af50d604..b2eaa7a263e 100644 --- a/doc/src/sgml/ref/alter_operator.sgml +++ b/doc/src/sgml/ref/alter_operator.sgml @@ -26,6 +26,11 @@ ALTER OPERATOR name ( { left_typename ( { left_type | NONE } , { right_type | NONE } ) SET SCHEMA new_schema + +ALTER OPERATOR name ( { left_type | NONE } , { right_type | NONE } ) + SET ( { RESTRICT = { res_proc | NONE } + | JOIN = { join_proc | NONE } + } [, ... ] ) @@ -34,8 +39,7 @@ ALTER OPERATOR name ( { left_type ALTER OPERATOR changes the definition of - an operator. The only currently available functionality is to change the - owner of the operator. + an operator. @@ -98,6 +102,25 @@ ALTER OPERATOR name ( { left_type + + + res_proc + + + The restriction selectivity estimator function for this operator; write NONE to remove existing selectivity estimator. + + + + + + join_proc + + + The join selectivity estimator function for this operator; write NONE to remove existing selectivity estimator. + + + + @@ -109,6 +132,13 @@ ALTER OPERATOR name ( { left_type ALTER OPERATOR @@ (text, text) OWNER TO joe; + + + Change the restriction and join selectivity estimator functions of a custom operator a && b for type int[]: + +ALTER OPERATOR && (_int4, _int4) SET (RESTRICT = _int_contsel, JOIN = _int_contjoinsel); + + diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index b4a1aac3a14..32065185ea4 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -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 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; +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 4c363d3d39a..6a08c2db211 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index f19251e7c41..faf5eedab4e 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e0ff6f16a21..2b02a2e5233 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 TableElement TypedTableElement ConstraintElem TableFuncElement %type columnDef columnOptions -%type def_elem reloption_elem old_aggr_elem +%type def_elem reloption_elem old_aggr_elem operator_def_elem %type 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 diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 0dabcc130e0..e81bbc6c9d1 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -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; diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 9b81c16d823..adae296f527 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -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, diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 290cdb30585..f8acda4eede 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -347,6 +347,7 @@ typedef enum NodeTag T_DropTableSpaceStmt, T_AlterObjectSchemaStmt, T_AlterOwnerStmt, + T_AlterOperatorStmt, T_DropOwnedStmt, T_ReassignOwnedStmt, T_CompositeTypeStmt, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index a567c50da72..5aaf5ec9e8e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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 index 00000000000..2b996544c1c --- /dev/null +++ b/src/test/regress/expected/alter_operator.out @@ -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); diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 91780cdcc73..4df15def6e2 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -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 diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index a2e0cebbdb5..3a607cff46c 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -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 index 00000000000..535052731a4 --- /dev/null +++ b/src/test/regress/sql/alter_operator.sql @@ -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);