diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index b3fc57d..c61c62f 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -25,6 +25,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml index 381ea3e..7e36aef 100644 --- a/doc/src/sgml/ref/create_rule.sgml +++ b/doc/src/sgml/ref/create_rule.sgml @@ -284,4 +284,12 @@ UPDATE mytable SET name = 'foo' WHERE id = 42; entire query rewrite system. + + + See Also + + + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index fe90227..5b0c774 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -53,6 +53,7 @@ &alterOperatorClass; &alterOperatorFamily; &alterRole; + &alterRule; &alterSchema; &alterSequence; &alterServer; diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index c2d4bb3..3cc6e9a 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -51,6 +51,7 @@ #include "commands/user.h" #include "parser/parse_func.h" #include "miscadmin.h" +#include "rewrite/rewriteDefine.h" #include "tcop/utility.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -307,6 +308,9 @@ ExecRenameStmt(RenameStmt *stmt) case OBJECT_ROLE: return RenameRole(stmt->subname, stmt->newname); + case OBJECT_RULE: + return RenameRewriteRule(stmt->relation, stmt->subname, stmt->newname); + case OBJECT_SCHEMA: return RenameSchema(stmt->subname, stmt->newname); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 828e110..33ac554 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -7029,6 +7029,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = false; $$ = (Node *)n; } + | ALTER RULE name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_RULE; + n->relation = $5; + n->subname = $3; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } | ALTER USER RoleId RENAME TO RoleId { RenameStmt *n = makeNode(RenameStmt); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index ac724c3..dbdad19 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -753,35 +753,69 @@ EnableDisableRule(Relation rel, const char *rulename, /* * Rename an existing rewrite rule. - * - * This is unused code at the moment. Note that it lacks a permissions check. */ -#ifdef NOT_USED -void -RenameRewriteRule(Oid owningRel, const char *oldName, +Oid +RenameRewriteRule(RangeVar* relation, const char *oldName, const char *newName) { Relation pg_rewrite_desc; HeapTuple ruletup; + Oid owningRel; + char owningRelKind; + Oid ruleOid = InvalidOid; + + owningRel = RangeVarGetRelid(relation, AccessExclusiveLock, false); + + owningRelKind = get_rel_relkind(owningRel); + + /* + * verify relation is of a type that rules can be applied to + */ + if (owningRelKind != RELKIND_RELATION && + owningRelKind != RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or view", + get_rel_name(owningRel)))); + + /* + * check if user has permission to rename rule on this relation + */ + if (!pg_class_ownercheck(owningRel, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(owningRel)); pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); ruletup = SearchSysCacheCopy2(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(oldName)); + + /* old rule should already exist */ if (!HeapTupleIsValid(ruletup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", oldName, get_rel_name(owningRel)))); - /* should not already exist */ + ruleOid = HeapTupleGetOid(ruletup); + + /* rule with the new name should not already exist */ if (IsDefinedRewriteRule(owningRel, newName)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", newName, get_rel_name(owningRel)))); + /* + * rename is not allowed for ON SELECT rule, because it's always + * named "_RETURN". + */ + if (((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_type - '0' == CMD_SELECT) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("rename not allowed for ON SELECT rule"))); + namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName); simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); @@ -791,6 +825,6 @@ RenameRewriteRule(Oid owningRel, const char *oldName, heap_freetuple(ruletup); heap_close(pg_rewrite_desc, RowExclusiveLock); -} -#endif + return ruleOid; +} diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index f7d8466..0dbb669 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -925,7 +925,7 @@ psql_completion(char *text, int start, int end) {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION", "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", - "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", + "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL}; @@ -1681,6 +1681,22 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "USER") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_roles); + /* ALTER RULE */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + pg_strcasecmp(prev2_wd, "RULE") == 0) + COMPLETE_WITH_CONST("ON"); + + /* ALTER RULE ON */ + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "RULE") == 0) + COMPLETE_WITH_CONST("RENAME TO"); + + /* If we have ALTER RULE ON, then add the correct viewname */ + else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && + pg_strcasecmp(prev3_wd, "RULE") == 0 && + pg_strcasecmp(prev_wd, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + /* BEGIN, END, ABORT */ else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 || pg_strcasecmp(prev_wd, "END") == 0 || diff --git a/src/include/rewrite/rewriteDefine.h b/src/include/rewrite/rewriteDefine.h index dda267b..72921c3 100644 --- a/src/include/rewrite/rewriteDefine.h +++ b/src/include/rewrite/rewriteDefine.h @@ -32,7 +32,7 @@ extern Oid DefineQueryRewrite(char *rulename, bool replace, List *action); -extern void RenameRewriteRule(Oid owningRel, const char *oldName, +extern Oid RenameRewriteRule(RangeVar* relation, const char *oldName, const char *newName); extern void setRuleCheckAsUser(Node *node, Oid userid); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index a235571..7e79815 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1693,3 +1693,42 @@ Rules: ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text) Has OIDs: no +-- +-- check alter rename rule +-- +CREATE TABLE rule_t1 (a INT); +CREATE TABLE rule_t2 (a INT); +CREATE RULE "_RETURN" AS + ON SELECT TO rule_t1 + DO INSTEAD + SELECT * FROM rule_t2; +CREATE RULE InsertRule AS + ON INSERT TO rule_t1 + DO INSTEAD + INSERT INTO rule_t2 VALUES(new.a); +ALTER RULE InsertRule ON rule_t1 RENAME to NewInsertRule; +INSERT INTO rule_t1 VALUES(1); +SELECT * FROM rule_t1; + a +--- + 1 +(1 row) + +-- +-- error conditions for alter rename rule +-- +ALTER RULE NewInsertRule ON rule_t1 RENAME TO "_RETURN"; -- rule already exists error +ERROR: rule "_RETURN" for relation "rule_t1" already exists +ALTER RULE "_RETURN" ON rule_t1 RENAME TO abc; -- ON SELECT rule can not be renamed error +ERROR: rename not allowed for ON SELECT rule +ALTER RULE InsertRule ON rule_t1 RENAME TO NewInsertRule; -- NO rule exists error +ERROR: rule "insertrule" for relation "rule_t1" does not exist +ALTER RULE NewInsertRule ON notable RENAME TO InsertRule; -- table does not exist error +ERROR: relation "notable" does not exist +CREATE TYPE item1 as(); +ALTER RULE InsertRule ON item1 RENAME to NewInsertRule; -- target item should be view or table +ERROR: "item1" is not a table or view +DROP RULE NewInsertRule ON rule_t1; -- drop altered rule +DROP TYPE item1; +DROP VIEW rule_t1; +DROP TABLE rule_t2; diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql index 458c2f0..abdd763 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -968,3 +968,40 @@ update rules_src set f2 = f2 / 10; select * from rules_src; select * from rules_log; \d+ rules_src + + +-- +-- check alter rename rule +-- +CREATE TABLE rule_t1 (a INT); +CREATE TABLE rule_t2 (a INT); + +CREATE RULE "_RETURN" AS + ON SELECT TO rule_t1 + DO INSTEAD + SELECT * FROM rule_t2; + +CREATE RULE InsertRule AS + ON INSERT TO rule_t1 + DO INSTEAD + INSERT INTO rule_t2 VALUES(new.a); + +ALTER RULE InsertRule ON rule_t1 RENAME to NewInsertRule; + +INSERT INTO rule_t1 VALUES(1); +SELECT * FROM rule_t1; + +-- +-- error conditions for alter rename rule +-- +ALTER RULE NewInsertRule ON rule_t1 RENAME TO "_RETURN"; -- rule already exists error +ALTER RULE "_RETURN" ON rule_t1 RENAME TO abc; -- ON SELECT rule can not be renamed error +ALTER RULE InsertRule ON rule_t1 RENAME TO NewInsertRule; -- NO rule exists error +ALTER RULE NewInsertRule ON notable RENAME TO InsertRule; -- table does not exist error +CREATE TYPE item1 as(); +ALTER RULE InsertRule ON item1 RENAME to NewInsertRule; -- target item should be view or table + +DROP RULE NewInsertRule ON rule_t1; -- drop altered rule +DROP TYPE item1; +DROP VIEW rule_t1; +DROP TABLE rule_t2; \ No newline at end of file diff --git a/doc/src/sgml/ref/alter_rule.sgml b/doc/src/sgml/ref/alter_rule.sgml new file mode 100644 index 0000000..3861e4a --- /dev/null +++ b/doc/src/sgml/ref/alter_rule.sgml @@ -0,0 +1,103 @@ + + + + + ALTER RULE + 7 + SQL - Language Statements + + + + ALTER RULE + change the definition of a rule + + + + ALTER RULE + + + + +ALTER RULE name ON table_name RENAME TO new_name + + + + + Description + + + ALTER RULE changes properties of an existing + rule. The RENAME clause changes the name of + the given rule without otherwise changing the rule + definition. + + + + You must own the table on which the rule acts to be allowed to change its properties. + + + + + Parameters + + + + name + + + The name of an existing rule to alter. + + + + + + table_name + + + The name of the table on which this rule acts. + + + + + + new_name + + + The new name for the rule. + + + + + + + + Examples + + + To rename an existing rule: + +ALTER RULE notify_all ON emp RENAME TO notify_me; + + + + + Compatibility + + + ALTER RULE is a + PostgreSQL language extension, as is the + entire query rewrite system. + + + + + See Also + + + + + +