Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit dd7e54a

Browse files
committed
Automatic view update rules
Bernd Helmle
1 parent 5841aa8 commit dd7e54a

30 files changed

+2289
-41
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.189 2009/01/16 13:27:23 heikki Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.190 2009/01/22 17:27:54 petere Exp $ -->
22
<!--
33
Documentation of the system catalogs, directed toward PostgreSQL developers
44
-->
@@ -4144,6 +4144,13 @@
41444144
<entry>True if the rule is an <literal>INSTEAD</literal> rule</entry>
41454145
</row>
41464146

4147+
<row>
4148+
<entry><structfield>is_auto</structfield></entry>
4149+
<entry><type>bool</type></entry>
4150+
<entry></entry>
4151+
<entry>True if the rule was automatically generated</entry>
4152+
</row>
4153+
41474154
<row>
41484155
<entry><structfield>ev_qual</structfield></entry>
41494156
<entry><type>text</type></entry>

doc/src/sgml/intro.sgml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/intro.sgml,v 1.32 2007/01/31 20:56:17 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/intro.sgml,v 1.33 2009/01/22 17:27:54 petere Exp $ -->
22

33
<preface id="preface">
44
<title>Preface</title>
@@ -110,7 +110,7 @@
110110
<simpara>triggers</simpara>
111111
</listitem>
112112
<listitem>
113-
<simpara>views</simpara>
113+
<simpara>updatable views</simpara>
114114
</listitem>
115115
<listitem>
116116
<simpara>transactional integrity</simpara>

doc/src/sgml/ref/create_view.sgml

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.39 2008/12/15 21:35:31 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.40 2009/01/22 17:27:54 petere Exp $
33
PostgreSQL documentation
44
-->
55

@@ -115,11 +115,99 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
115115
<title>Notes</title>
116116

117117
<para>
118-
Currently, views are read only: the system will not allow an insert,
119-
update, or delete on a view. You can get the effect of an updatable
120-
view by creating rules that rewrite inserts, etc. on the view into
121-
appropriate actions on other tables. For more information see
122-
<xref linkend="sql-createrule" endterm="sql-createrule-title">.
118+
Some views are updatable, which means that the
119+
commands <command>INSERT</command>, <command>UPDATE</command>,
120+
and <command>DELETE</command> can be used on the view as if it
121+
were a regular table. A view is updatable if it
122+
does <emphasis>not</emphasis> contain:
123+
124+
<itemizedlist>
125+
<listitem>
126+
<para>
127+
more than one underlying table (joins) or no underlying table at all
128+
</para>
129+
</listitem>
130+
131+
<listitem>
132+
<para>
133+
underlying tables/views that are themselves not updatable,
134+
including table value constructors and table functions
135+
</para>
136+
</listitem>
137+
138+
<listitem>
139+
<para>
140+
subqueries in the <literal>FROM</literal> list
141+
</para>
142+
</listitem>
143+
144+
<listitem>
145+
<para>
146+
items in the select list that are not direct references to a
147+
column of the underlying table, such as literals or any
148+
nontrivial value expression
149+
</para>
150+
</listitem>
151+
152+
<listitem>
153+
<para>
154+
references to system columns in the select list
155+
</para>
156+
</listitem>
157+
158+
<listitem>
159+
<para>
160+
more than one reference to the same column in the select list
161+
</para>
162+
</listitem>
163+
164+
<listitem>
165+
<para>aggregate function calls</para>
166+
</listitem>
167+
168+
<listitem>
169+
<para>window function calls</para>
170+
</listitem>
171+
172+
<listitem>
173+
<para>
174+
<literal>WITH</literal> or <literal>WITH RECURSIVE</literal> clauses
175+
</para>
176+
</listitem>
177+
178+
<listitem>
179+
<para>
180+
<literal>DISTINCT</literal>, <literal>GROUP BY</literal>, or
181+
<literal>HAVING</literal> clauses
182+
</para>
183+
</listitem>
184+
185+
<listitem>
186+
<para>
187+
<literal>UNION</literal>, <literal>INTERSECT</literal>, or
188+
<literal>EXCEPT</literal> clauses
189+
</para>
190+
</listitem>
191+
192+
<listitem>
193+
<para>
194+
<literal>LIMIT</literal> or <literal>OFFSET</literal> clauses
195+
(or other equivalent spellings thereof)
196+
</para>
197+
</listitem>
198+
</itemizedlist>
199+
</para>
200+
201+
<para>
202+
The updatable views implementation is based on the rule system.
203+
Because of this, you can also make more complex views updatable or
204+
insertable by creating your own rules that rewrite
205+
the <command>INSERT</command>,
206+
<command>UPDATE</command>, and <command>DELETE</command> actions
207+
on the view into appropriate actions on other tables. You can
208+
also replace the automatically generated rules by your own rules.
209+
For more information on the rule system, refer
210+
to <xref linkend="sql-createrule" endterm="sql-createrule-title">.
123211
</para>
124212

125213
<para>

src/backend/commands/view.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.111 2009/01/01 17:23:40 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.112 2009/01/22 17:27:54 petere Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -27,7 +27,9 @@
2727
#include "parser/parse_relation.h"
2828
#include "rewrite/rewriteDefine.h"
2929
#include "rewrite/rewriteManip.h"
30+
#include "rewrite/rewriteRemove.h"
3031
#include "rewrite/rewriteSupport.h"
32+
#include "rewrite/viewUpdate.h"
3133
#include "utils/acl.h"
3234
#include "utils/builtins.h"
3335
#include "utils/lsyscache.h"
@@ -308,13 +310,28 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
308310
viewOid,
309311
NULL,
310312
CMD_SELECT,
311-
true,
313+
true, /* is_instead */
314+
true, /* is_auto */
312315
replace,
313316
list_make1(viewParse));
314317

315318
/*
316-
* Someday: automatic ON INSERT, etc
319+
* Delete all implicit rules on replace. CreateViewUpdateRules()
320+
* below will re-create them if appropriate for the new view
321+
* definition.
317322
*/
323+
if (replace)
324+
{
325+
Relation rel = heap_open(viewOid, AccessExclusiveLock);
326+
RemoveAutomaticRulesOnEvent(rel, CMD_INSERT);
327+
RemoveAutomaticRulesOnEvent(rel, CMD_DELETE);
328+
RemoveAutomaticRulesOnEvent(rel, CMD_UPDATE);
329+
heap_close(rel, NoLock);
330+
}
331+
332+
CommandCounterIncrement();
333+
334+
CreateViewUpdateRules(viewOid, viewParse);
318335
}
319336

320337
/*---------------------------------------------------------------

src/backend/rewrite/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Makefile for rewrite
55
#
66
# IDENTIFICATION
7-
# $PostgreSQL: pgsql/src/backend/rewrite/Makefile,v 1.17 2008/02/19 10:30:08 petere Exp $
7+
# $PostgreSQL: pgsql/src/backend/rewrite/Makefile,v 1.18 2009/01/22 17:27:54 petere Exp $
88
#
99
#-------------------------------------------------------------------------
1010

@@ -13,6 +13,7 @@ top_builddir = ../../..
1313
include $(top_builddir)/src/Makefile.global
1414

1515
OBJS = rewriteRemove.o rewriteDefine.o \
16-
rewriteHandler.o rewriteManip.o rewriteSupport.o
16+
rewriteHandler.o rewriteManip.o rewriteSupport.o \
17+
viewUpdate.o
1718

1819
include $(top_srcdir)/src/backend/common.mk

src/backend/rewrite/rewriteDefine.c

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.134 2009/01/01 17:23:47 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.135 2009/01/22 17:27:54 petere Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515
#include "postgres.h"
1616

1717
#include "access/heapam.h"
18+
#include "access/xact.h"
1819
#include "catalog/dependency.h"
1920
#include "catalog/indexing.h"
2021
#include "catalog/namespace.h"
@@ -25,6 +26,7 @@
2526
#include "parser/parse_utilcmd.h"
2627
#include "rewrite/rewriteDefine.h"
2728
#include "rewrite/rewriteManip.h"
29+
#include "rewrite/rewriteRemove.h"
2830
#include "rewrite/rewriteSupport.h"
2931
#include "utils/acl.h"
3032
#include "utils/builtins.h"
@@ -39,6 +41,7 @@ static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
3941
bool isSelect);
4042
static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
4143
static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
44+
static const char *rule_event_string(CmdType evtype);
4245

4346

4447
/*
@@ -52,6 +55,7 @@ InsertRule(char *rulname,
5255
Oid eventrel_oid,
5356
AttrNumber evslot_index,
5457
bool evinstead,
58+
bool is_auto,
5559
Node *event_qual,
5660
List *action,
5761
bool replace)
@@ -84,6 +88,7 @@ InsertRule(char *rulname,
8488
values[i++] = CharGetDatum(evtype + '0'); /* ev_type */
8589
values[i++] = CharGetDatum(RULE_FIRES_ON_ORIGIN); /* ev_enabled */
8690
values[i++] = BoolGetDatum(evinstead); /* is_instead */
91+
values[i++] = BoolGetDatum(is_auto); /* is_auto */
8792
values[i++] = CStringGetTextDatum(evqual); /* ev_qual */
8893
values[i++] = CStringGetTextDatum(actiontree); /* ev_action */
8994

@@ -102,7 +107,11 @@ InsertRule(char *rulname,
102107

103108
if (HeapTupleIsValid(oldtup))
104109
{
105-
if (!replace)
110+
/*
111+
* If REPLACE was not used we still check if the old rule is
112+
* automatic: Then we replace it anyway.
113+
*/
114+
if (!replace && !((Form_pg_rewrite) GETSTRUCT(oldtup))->is_auto)
106115
ereport(ERROR,
107116
(errcode(ERRCODE_DUPLICATE_OBJECT),
108117
errmsg("rule \"%s\" for relation \"%s\" already exists",
@@ -115,6 +124,7 @@ InsertRule(char *rulname,
115124
replaces[Anum_pg_rewrite_ev_attr - 1] = true;
116125
replaces[Anum_pg_rewrite_ev_type - 1] = true;
117126
replaces[Anum_pg_rewrite_is_instead - 1] = true;
127+
replaces[Anum_pg_rewrite_is_auto - 1] = true;
118128
replaces[Anum_pg_rewrite_ev_qual - 1] = true;
119129
replaces[Anum_pg_rewrite_ev_action - 1] = true;
120130

@@ -205,6 +215,7 @@ DefineRule(RuleStmt *stmt, const char *queryString)
205215
whereClause,
206216
stmt->event,
207217
stmt->instead,
218+
false, /* not is_auto */
208219
stmt->replace,
209220
actions);
210221
}
@@ -223,6 +234,7 @@ DefineQueryRewrite(char *rulename,
223234
Node *event_qual,
224235
CmdType event_type,
225236
bool is_instead,
237+
bool is_auto,
226238
bool replace,
227239
List *action)
228240
{
@@ -446,6 +458,42 @@ DefineQueryRewrite(char *rulename,
446458
RelationGetDescr(event_relation),
447459
false);
448460
}
461+
462+
/*
463+
* If defining a non-automatic DO INSTEAD rule, drop all
464+
* automatic rules on the same event.
465+
*/
466+
if (!is_auto && is_instead)
467+
{
468+
RemoveAutomaticRulesOnEvent(event_relation, event_type);
469+
CommandCounterIncrement();
470+
}
471+
472+
/*
473+
* If defining an automatic rule and there is a manual rule on
474+
* the same event, warn and don't do it.
475+
*/
476+
if (is_auto && event_relation->rd_rules != NULL)
477+
{
478+
int i;
479+
480+
for (i = 0; i < event_relation->rd_rules->numLocks; i++)
481+
{
482+
RewriteRule *rule = event_relation->rd_rules->rules[i];
483+
484+
if (rule->event == event_type && !rule->is_auto && rule->isInstead == is_instead)
485+
{
486+
ereport(WARNING,
487+
(errmsg("automatic %s rule not created because manually created %s rule exists",
488+
rule_event_string(event_type), rule_event_string(event_type)),
489+
errhint("If you prefer to have the automatic rule, drop the manually created rule and run CREATE OR REPLACE VIEW again.")));
490+
491+
heap_close(event_relation, NoLock);
492+
return;
493+
}
494+
}
495+
}
496+
449497
}
450498

451499
/*
@@ -461,6 +509,7 @@ DefineQueryRewrite(char *rulename,
461509
event_relid,
462510
event_attno,
463511
is_instead,
512+
is_auto,
464513
event_qual,
465514
action,
466515
replace);
@@ -754,3 +803,16 @@ RenameRewriteRule(Oid owningRel, const char *oldName,
754803
}
755804

756805
#endif
806+
807+
808+
static const char *
809+
rule_event_string(CmdType type)
810+
{
811+
if (type == CMD_INSERT)
812+
return "INSERT";
813+
if (type == CMD_UPDATE)
814+
return "UPDATE";
815+
if (type == CMD_DELETE)
816+
return "DELETE";
817+
return "???";
818+
}

0 commit comments

Comments
 (0)