mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-07 19:47:50 +08:00
Add support for ALTER RULE ... RENAME TO.
Ali Dar, reviewed by Dean Rasheed.
This commit is contained in:
parent
f806c191a3
commit
c61e26ee3e
@ -25,6 +25,7 @@ Complete list of usable sgml source files in this directory.
|
||||
<!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
|
||||
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
|
||||
<!ENTITY alterRole SYSTEM "alter_role.sgml">
|
||||
<!ENTITY alterRule SYSTEM "alter_rule.sgml">
|
||||
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
|
||||
<!ENTITY alterServer SYSTEM "alter_server.sgml">
|
||||
<!ENTITY alterSequence SYSTEM "alter_sequence.sgml">
|
||||
|
105
doc/src/sgml/ref/alter_rule.sgml
Normal file
105
doc/src/sgml/ref/alter_rule.sgml
Normal file
@ -0,0 +1,105 @@
|
||||
<!--
|
||||
doc/src/sgml/ref/alter_rule.sgml
|
||||
PostgreSQL documentation
|
||||
-->
|
||||
|
||||
<refentry id="SQL-ALTERRULE">
|
||||
<refmeta>
|
||||
<refentrytitle>ALTER RULE</refentrytitle>
|
||||
<manvolnum>7</manvolnum>
|
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>ALTER RULE</refname>
|
||||
<refpurpose>change the definition of a rule</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<indexterm zone="sql-alterrule">
|
||||
<primary>ALTER RULE</primary>
|
||||
</indexterm>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
ALTER RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>
|
||||
<command>ALTER RULE</command> changes properties of an existing
|
||||
rule. Currently, the only available action is to change the rule's name.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To use <command>ALTER RULE</command>, you must own the table or view that
|
||||
the rule applies to.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Parameters</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">name</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The name of an existing rule to alter.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">table_name</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The name (optionally schema-qualified) of the table or view that the
|
||||
rule applies to.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">new_name</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The new name for the rule.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Examples</title>
|
||||
|
||||
<para>
|
||||
To rename an existing rule:
|
||||
<programlisting>
|
||||
ALTER RULE notify_all ON emp RENAME TO notify_me;
|
||||
</programlisting></para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Compatibility</title>
|
||||
|
||||
<para>
|
||||
<command>ALTER RULE</command> is a
|
||||
<productname>PostgreSQL</productname> language extension, as is the
|
||||
entire query rewrite system.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
|
||||
<simplelist type="inline">
|
||||
<member><xref linkend="sql-createrule"></member>
|
||||
<member><xref linkend="sql-droprule"></member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
@ -284,4 +284,14 @@ UPDATE mytable SET name = 'foo' WHERE id = 42;
|
||||
entire query rewrite system.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
|
||||
<simplelist type="inline">
|
||||
<member><xref linkend="sql-alterrule"></member>
|
||||
<member><xref linkend="sql-droprule"></member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
|
@ -103,7 +103,9 @@ DROP RULE newrule ON mytable;
|
||||
<title>Compatibility</title>
|
||||
|
||||
<para>
|
||||
There is no <command>DROP RULE</command> statement in the SQL standard.
|
||||
<command>DROP RULE</command> is a
|
||||
<productname>PostgreSQL</productname> language extension, as is the
|
||||
entire query rewrite system.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
@ -112,6 +114,7 @@ DROP RULE newrule ON mytable;
|
||||
|
||||
<simplelist type="inline">
|
||||
<member><xref linkend="sql-createrule"></member>
|
||||
<member><xref linkend="sql-alterrule"></member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
||||
&alterOperatorClass;
|
||||
&alterOperatorFamily;
|
||||
&alterRole;
|
||||
&alterRule;
|
||||
&alterSchema;
|
||||
&alterSequence;
|
||||
&alterServer;
|
||||
|
@ -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"
|
||||
@ -324,6 +325,10 @@ ExecRenameStmt(RenameStmt *stmt)
|
||||
case OBJECT_ATTRIBUTE:
|
||||
return renameatt(stmt);
|
||||
|
||||
case OBJECT_RULE:
|
||||
return RenameRewriteRule(stmt->relation, stmt->subname,
|
||||
stmt->newname);
|
||||
|
||||
case OBJECT_TRIGGER:
|
||||
return renametrig(stmt);
|
||||
|
||||
|
@ -7003,6 +7003,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
|
||||
n->missing_ok = true;
|
||||
$$ = (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 TRIGGER name ON qualified_name RENAME TO name
|
||||
{
|
||||
RenameStmt *n = makeNode(RenameStmt);
|
||||
|
@ -752,37 +752,98 @@ 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.
|
||||
* Perform permissions and integrity checks before acquiring a relation lock.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
RenameRewriteRule(Oid owningRel, const char *oldName,
|
||||
static void
|
||||
RangeVarCallbackForRenameRule(const RangeVar *rv, Oid relid, Oid oldrelid,
|
||||
void *arg)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Form_pg_class form;
|
||||
|
||||
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
return; /* concurrently dropped */
|
||||
form = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
/* only tables and views can have rules */
|
||||
if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("\"%s\" is not a table or view", rv->relname)));
|
||||
|
||||
if (!allowSystemTableMods && IsSystemClass(form))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("permission denied: \"%s\" is a system catalog",
|
||||
rv->relname)));
|
||||
|
||||
/* you must own the table to rename one of its rules */
|
||||
if (!pg_class_ownercheck(relid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rename an existing rewrite rule.
|
||||
*/
|
||||
Oid
|
||||
RenameRewriteRule(RangeVar *relation, const char *oldName,
|
||||
const char *newName)
|
||||
{
|
||||
Oid relid;
|
||||
Relation targetrel;
|
||||
Relation pg_rewrite_desc;
|
||||
HeapTuple ruletup;
|
||||
Form_pg_rewrite ruleform;
|
||||
Oid ruleOid;
|
||||
|
||||
/*
|
||||
* Look up name, check permissions, and acquire lock (which we will NOT
|
||||
* release until end of transaction).
|
||||
*/
|
||||
relid = RangeVarGetRelidExtended(relation, AccessExclusiveLock,
|
||||
false, false,
|
||||
RangeVarCallbackForRenameRule,
|
||||
NULL);
|
||||
|
||||
/* Have lock already, so just need to build relcache entry. */
|
||||
targetrel = relation_open(relid, NoLock);
|
||||
|
||||
/* Prepare to modify pg_rewrite */
|
||||
pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
|
||||
|
||||
/* Fetch the rule's entry (it had better exist) */
|
||||
ruletup = SearchSysCacheCopy2(RULERELNAME,
|
||||
ObjectIdGetDatum(owningRel),
|
||||
ObjectIdGetDatum(relid),
|
||||
PointerGetDatum(oldName));
|
||||
if (!HeapTupleIsValid(ruletup))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("rule \"%s\" for relation \"%s\" does not exist",
|
||||
oldName, get_rel_name(owningRel))));
|
||||
oldName, RelationGetRelationName(targetrel))));
|
||||
ruleform = (Form_pg_rewrite) GETSTRUCT(ruletup);
|
||||
ruleOid = HeapTupleGetOid(ruletup);
|
||||
|
||||
/* should not already exist */
|
||||
if (IsDefinedRewriteRule(owningRel, newName))
|
||||
/* rule with the new name should not already exist */
|
||||
if (IsDefinedRewriteRule(relid, newName))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("rule \"%s\" for relation \"%s\" already exists",
|
||||
newName, get_rel_name(owningRel))));
|
||||
newName, RelationGetRelationName(targetrel))));
|
||||
|
||||
namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName);
|
||||
/*
|
||||
* We disallow renaming ON SELECT rules, because they should always be
|
||||
* named "_RETURN".
|
||||
*/
|
||||
if (ruleform->ev_type == CMD_SELECT + '0')
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("renaming an ON SELECT rule is not allowed")));
|
||||
|
||||
/* OK, do the update */
|
||||
namestrcpy(&(ruleform->rulename), newName);
|
||||
|
||||
simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup);
|
||||
|
||||
@ -791,6 +852,18 @@ RenameRewriteRule(Oid owningRel, const char *oldName,
|
||||
|
||||
heap_freetuple(ruletup);
|
||||
heap_close(pg_rewrite_desc, RowExclusiveLock);
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Invalidate relation's relcache entry so that other backends (and this
|
||||
* one too!) are sent SI message to make them rebuild relcache entries.
|
||||
* (Ideally this should happen automatically...)
|
||||
*/
|
||||
CacheInvalidateRelcache(targetrel);
|
||||
|
||||
/*
|
||||
* Close rel, but keep exclusive lock!
|
||||
*/
|
||||
relation_close(targetrel, NoLock);
|
||||
|
||||
return ruleOid;
|
||||
}
|
||||
|
@ -624,6 +624,15 @@ static const SchemaQuery Query_for_list_of_views = {
|
||||
" (SELECT conrelid FROM pg_catalog.pg_constraint "\
|
||||
" WHERE pg_catalog.quote_ident(conname)='%s')"
|
||||
|
||||
/* the silly-looking length condition is just to eat up the current word */
|
||||
#define Query_for_list_of_tables_for_rule \
|
||||
"SELECT pg_catalog.quote_ident(relname) "\
|
||||
" FROM pg_catalog.pg_class"\
|
||||
" WHERE (%d = pg_catalog.length('%s'))"\
|
||||
" AND oid IN "\
|
||||
" (SELECT ev_class FROM pg_catalog.pg_rewrite "\
|
||||
" WHERE pg_catalog.quote_ident(rulename)='%s')"
|
||||
|
||||
/* the silly-looking length condition is just to eat up the current word */
|
||||
#define Query_for_list_of_tables_for_trigger \
|
||||
"SELECT pg_catalog.quote_ident(relname) "\
|
||||
@ -925,7 +934,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};
|
||||
|
||||
@ -1259,6 +1268,26 @@ psql_completion(char *text, int start, int end)
|
||||
|
||||
COMPLETE_WITH_LIST(list_ALTERVIEW);
|
||||
}
|
||||
|
||||
/* ALTER RULE <name>, add ON */
|
||||
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
|
||||
pg_strcasecmp(prev2_wd, "RULE") == 0)
|
||||
COMPLETE_WITH_CONST("ON");
|
||||
|
||||
/* If we have ALTER RULE <name> ON, then add the correct tablename */
|
||||
else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
|
||||
pg_strcasecmp(prev3_wd, "RULE") == 0 &&
|
||||
pg_strcasecmp(prev_wd, "ON") == 0)
|
||||
{
|
||||
completion_info_charp = prev2_wd;
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
|
||||
}
|
||||
|
||||
/* ALTER RULE <name> ON <name> */
|
||||
else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
|
||||
pg_strcasecmp(prev4_wd, "RULE") == 0)
|
||||
COMPLETE_WITH_CONST("RENAME TO");
|
||||
|
||||
/* ALTER TRIGGER <name>, add ON */
|
||||
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
|
||||
pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
|
||||
|
@ -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);
|
||||
|
@ -2559,3 +2559,44 @@ 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 VIEW rule_v1 AS SELECT * FROM rule_t1;
|
||||
CREATE RULE InsertRule AS
|
||||
ON INSERT TO rule_v1
|
||||
DO INSTEAD
|
||||
INSERT INTO rule_t1 VALUES(new.a);
|
||||
ALTER RULE InsertRule ON rule_v1 RENAME to NewInsertRule;
|
||||
INSERT INTO rule_v1 VALUES(1);
|
||||
SELECT * FROM rule_v1;
|
||||
a
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
\d+ rule_v1
|
||||
View "public.rule_v1"
|
||||
Column | Type | Modifiers | Storage | Description
|
||||
--------+---------+-----------+---------+-------------
|
||||
a | integer | | plain |
|
||||
View definition:
|
||||
SELECT rule_t1.a
|
||||
FROM rule_t1;
|
||||
Rules:
|
||||
newinsertrule AS
|
||||
ON INSERT TO rule_v1 DO INSTEAD INSERT INTO rule_t1 (a)
|
||||
VALUES (new.a)
|
||||
|
||||
--
|
||||
-- error conditions for alter rename rule
|
||||
--
|
||||
ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; -- doesn't exist
|
||||
ERROR: rule "insertrule" for relation "rule_v1" does not exist
|
||||
ALTER RULE NewInsertRule ON rule_v1 RENAME TO "_RETURN"; -- already exists
|
||||
ERROR: rule "_RETURN" for relation "rule_v1" already exists
|
||||
ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renamed
|
||||
ERROR: renaming an ON SELECT rule is not allowed
|
||||
DROP VIEW rule_v1;
|
||||
DROP TABLE rule_t1;
|
||||
|
@ -968,3 +968,31 @@ 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 VIEW rule_v1 AS SELECT * FROM rule_t1;
|
||||
|
||||
CREATE RULE InsertRule AS
|
||||
ON INSERT TO rule_v1
|
||||
DO INSTEAD
|
||||
INSERT INTO rule_t1 VALUES(new.a);
|
||||
|
||||
ALTER RULE InsertRule ON rule_v1 RENAME to NewInsertRule;
|
||||
|
||||
INSERT INTO rule_v1 VALUES(1);
|
||||
SELECT * FROM rule_v1;
|
||||
|
||||
\d+ rule_v1
|
||||
|
||||
--
|
||||
-- error conditions for alter rename rule
|
||||
--
|
||||
ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; -- doesn't exist
|
||||
ALTER RULE NewInsertRule ON rule_v1 RENAME TO "_RETURN"; -- already exists
|
||||
ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renamed
|
||||
|
||||
DROP VIEW rule_v1;
|
||||
DROP TABLE rule_t1;
|
||||
|
Loading…
Reference in New Issue
Block a user