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

Commit 4cbe3ac

Browse files
committed
WITH CHECK OPTION support for auto-updatable VIEWs
For simple views which are automatically updatable, this patch allows the user to specify what level of checking should be done on records being inserted or updated. For 'LOCAL CHECK', new tuples are validated against the conditionals of the view they are being inserted into, while for 'CASCADED CHECK' the new tuples are validated against the conditionals for all views involved (from the top down). This option is part of the SQL specification. Dean Rasheed, reviewed by Pavel Stehule
1 parent 6f9e39b commit 4cbe3ac

33 files changed

+1245
-107
lines changed

doc/src/sgml/ref/alter_view.sgml

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RENAM
2828
ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
2929
ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET ( <replaceable class="parameter">view_option_name</replaceable> [= <replaceable class="parameter">view_option_value</replaceable>] [, ... ] )
3030
ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET ( <replaceable class="parameter">view_option_name</replaceable> [, ... ] )
31+
32+
<phrase>where <replaceable class="parameter">view_option_name</replaceable> can be one of:</phrase>
33+
34+
security_barrier [ <replaceable class="parameter">boolean</replaceable> ]
35+
check_option [ <replaceable class="parameter">text</replaceable> (<literal>local</literal> or <literal>cascaded</literal>) ]
3136
</synopsis>
3237
</refsynopsisdiv>
3338

doc/src/sgml/ref/create_view.sgml

+140-59
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ PostgreSQL documentation
2424
CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
2525
[ WITH ( <replaceable class="PARAMETER">view_option_name</replaceable> [= <replaceable class="PARAMETER">view_option_value</replaceable>] [, ... ] ) ]
2626
AS <replaceable class="PARAMETER">query</replaceable>
27+
[ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
28+
29+
<phrase>where <replaceable class="parameter">view_option_name</replaceable> can be one of:</phrase>
30+
31+
security_barrier [ <replaceable class="parameter">boolean</replaceable> ]
32+
check_option [ <replaceable class="parameter">text</replaceable> (<literal>local</literal> or <literal>cascaded</literal>) ]
2733
</synopsis>
2834
</refsynopsisdiv>
2935

@@ -120,10 +126,33 @@ CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
120126
<term><literal>WITH ( <replaceable class="PARAMETER">view_option_name</replaceable> [= <replaceable class="PARAMETER">view_option_value</replaceable>] [, ... ] )</literal></term>
121127
<listitem>
122128
<para>
123-
This clause specifies optional parameters for a view; currently, the
124-
only supported parameter name is <literal>security_barrier</literal>,
125-
which should be enabled when a view is intended to provide row-level
126-
security. See <xref linkend="rules-privileges"> for full details.
129+
This clause specifies optional parameters for a view; the following
130+
parameters are supported:
131+
132+
<variablelist>
133+
<varlistentry>
134+
<term><literal>security_barrier(boolean)</literal></term>
135+
<listitem>
136+
<para>
137+
This should be used if the view is intended to provide row-level
138+
security. See <xref linkend="rules-privileges"> for full details.
139+
</para>
140+
</listitem>
141+
</varlistentry>
142+
143+
<varlistentry>
144+
<term><literal>check_option(text)</literal></term>
145+
<listitem>
146+
<para>
147+
This parameter may be either <literal>local</> or
148+
<literal>cascaded</>, and is equivalent to specifying
149+
<literal>WITH [ CASCADED | LOCAL ] CHECK OPTION</> (see below).
150+
This option can be changed on existing views using <xref
151+
linkend="sql-alterview">.
152+
</para>
153+
</listitem>
154+
</varlistentry>
155+
</variablelist>
127156
</para>
128157
</listitem>
129158
</varlistentry>
@@ -138,6 +167,77 @@ CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
138167
</para>
139168
</listitem>
140169
</varlistentry>
170+
171+
<varlistentry>
172+
<term><literal>WITH [ CASCADED | LOCAL ] CHECK OPTION</literal></term>
173+
<listitem>
174+
<para>
175+
<indexterm zone="SQL-CREATEVIEW">
176+
<primary>CHECK OPTION</primary>
177+
</indexterm>
178+
<indexterm zone="SQL-CREATEVIEW">
179+
<primary>WITH CHECK OPTION</primary>
180+
</indexterm>
181+
This option controls the behavior of automatically updatable views. When
182+
this option is specified, <command>INSERT</> and <command>UPDATE</>
183+
commands on the view will be checked to ensure that new rows satisfy the
184+
view-defining condition (that is, the new rows are checked to ensure that
185+
they are visible through the view). If they are not, the update will be
186+
rejected. If the <literal>CHECK OPTION</> is not specified,
187+
<command>INSERT</> and <command>UPDATE</> commands on the view are
188+
allowed to create rows that are not visible through the view. The
189+
following check options are supported:
190+
191+
<variablelist>
192+
<varlistentry>
193+
<term><literal>LOCAL</literal></term>
194+
<listitem>
195+
<para>
196+
New rows are only checked against the conditions defined directly in
197+
the view itself. Any conditions defined on underlying base views are
198+
not checked (unless they also specify the <literal>CHECK OPTION</>).
199+
</para>
200+
</listitem>
201+
</varlistentry>
202+
203+
<varlistentry>
204+
<term><literal>CASCADED</literal></term>
205+
<listitem>
206+
<para>
207+
New rows are checked against the conditions of the view and all
208+
underlying base views. If the <literal>CHECK OPTION</> is specified,
209+
and neither <literal>LOCAL</> nor <literal>CASCADED</> is specified,
210+
then <literal>CASCADED</> is assumed.
211+
</para>
212+
</listitem>
213+
</varlistentry>
214+
</variablelist>
215+
</para>
216+
217+
<para>
218+
The <literal>CHECK OPTION</> may not be used with <literal>RECURSIVE</>
219+
views.
220+
</para>
221+
222+
<para>
223+
Note that the <literal>CHECK OPTION</> is only supported on views that
224+
are automatically updatable, and do not have <literal>INSTEAD OF</>
225+
triggers or <literal>INSTEAD</> rules. If an automatically updatable
226+
view is defined on top of a base view that has <literal>INSTEAD OF</>
227+
triggers, then the <literal>LOCAL CHECK OPTION</> may be used to check
228+
the conditions on the automatically updatable view, but the conditions
229+
on the base view with <literal>INSTEAD OF</> triggers will not be
230+
checked (a cascaded check option will not cascade down to a
231+
trigger-updatable view, and any check options defined directly on a
232+
trigger-updatable view will be ignored). If the view or any of its base
233+
relations has an <literal>INSTEAD</> rule that causes the
234+
<command>INSERT</> or <command>UPDATE</> command to be rewritten, then
235+
all check options will be ignored in the rewritten query, including any
236+
checks from automatically updatable views defined on top of the relation
237+
with the <literal>INSTEAD</> rule.
238+
</para>
239+
</listitem>
240+
</varlistentry>
141241
</variablelist>
142242
</refsect1>
143243

@@ -256,7 +356,9 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
256356
condition, and thus is no longer visible through the view. Similarly,
257357
an <command>INSERT</> command can potentially insert base-relation rows
258358
that do not satisfy the <literal>WHERE</> condition and thus are not
259-
visible through the view.
359+
visible through the view. The <literal>CHECK OPTION</> may be used to
360+
prevent <command>INSERT</> and <command>UPDATE</> commands from creating
361+
such rows that are not visible through the view.
260362
</para>
261363

262364
<para>
@@ -300,6 +402,38 @@ CREATE VIEW comedies AS
300402
the table will not be part of the view.
301403
</para>
302404

405+
<para>
406+
Create a view with <literal>LOCAL CHECK OPTION</>:
407+
408+
<programlisting>
409+
CREATE VIEW universal_comedies AS
410+
SELECT *
411+
FROM comedies
412+
WHERE classification = 'U'
413+
WITH LOCAL CHECK OPTION;
414+
</programlisting>
415+
This will create a view based on the <literal>comedies</> view, showing
416+
only films with <literal>kind = 'Comedy'</> and
417+
<literal>classification = 'U'</>. Any attempt to <command>INSERT</> or
418+
<command>UPDATE</> a row in the view will be rejected if the new row
419+
doesn't have <literal>classification = 'U'</>, but the film
420+
<literal>kind</> will not be checked.
421+
</para>
422+
423+
<para>
424+
Create a view with <literal>CASCADED CHECK OPTION</>:
425+
426+
<programlisting>
427+
CREATE VIEW pg_comedies AS
428+
SELECT *
429+
FROM comedies
430+
WHERE classification = 'PG'
431+
WITH CASCADED CHECK OPTION;
432+
</programlisting>
433+
This will create a view that checks both the <literal>kind</> and
434+
<literal>classification</> of new rows.
435+
</para>
436+
303437
<para>
304438
Create a recursive view consisting of the numbers from 1 to 100:
305439
<programlisting>
@@ -313,64 +447,11 @@ UNION ALL
313447
<refsect1>
314448
<title>Compatibility</title>
315449

316-
<para>
317-
The SQL standard specifies some additional capabilities for the
318-
<command>CREATE VIEW</command> statement:
319-
<synopsis>
320-
CREATE VIEW <replaceable class="parameter">name</replaceable> [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
321-
AS <replaceable class="PARAMETER">query</replaceable>
322-
[ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
323-
</synopsis>
324-
</para>
325-
326-
<para>
327-
The optional clauses for the full SQL command are:
328-
329-
<variablelist>
330-
<varlistentry>
331-
<term><literal>CHECK OPTION</literal></term>
332-
<listitem>
333-
<para>
334-
This option controls the behavior of automatically updatable views.
335-
When given, <command>INSERT</> and <command>UPDATE</> commands on
336-
the view will be checked to ensure new rows satisfy the
337-
view-defining condition (that is, the new rows would be visible
338-
through the view). If they do not, the update will be rejected.
339-
Without <literal>CHECK OPTION</literal>, <command>INSERT</> and
340-
<command>UPDATE</> commands on the view are allowed to create rows
341-
that are not visible through the view. (The latter behavior is the
342-
only one currently provided by <productname>PostgreSQL</>.)
343-
</para>
344-
</listitem>
345-
</varlistentry>
346-
347-
<varlistentry>
348-
<term><literal>LOCAL</literal></term>
349-
<listitem>
350-
<para>
351-
Check for integrity on this view.
352-
</para>
353-
</listitem>
354-
</varlistentry>
355-
356-
<varlistentry>
357-
<term><literal>CASCADED</literal></term>
358-
<listitem>
359-
<para>
360-
Check for integrity on this view and on any dependent
361-
view. <literal>CASCADED</> is assumed if neither
362-
<literal>CASCADED</> nor <literal>LOCAL</> is specified.
363-
</para>
364-
</listitem>
365-
</varlistentry>
366-
</variablelist>
367-
</para>
368-
369450
<para>
370451
<command>CREATE OR REPLACE VIEW</command> is a
371452
<productname>PostgreSQL</productname> language extension.
372453
So is the concept of a temporary view.
373-
The <literal>WITH</> clause is an extension as well.
454+
The <literal>WITH ( ... )</> clause is an extension as well.
374455
</para>
375456
</refsect1>
376457

src/backend/access/common/reloptions.c

+14
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "catalog/pg_type.h"
2525
#include "commands/defrem.h"
2626
#include "commands/tablespace.h"
27+
#include "commands/view.h"
2728
#include "nodes/makefuncs.h"
2829
#include "utils/array.h"
2930
#include "utils/attoptcache.h"
@@ -248,6 +249,17 @@ static relopt_string stringRelOpts[] =
248249
gistValidateBufferingOption,
249250
"auto"
250251
},
252+
{
253+
{
254+
"check_option",
255+
"View has WITH CHECK OPTION defined (local or cascaded).",
256+
RELOPT_KIND_VIEW
257+
},
258+
0,
259+
true,
260+
validateWithCheckOption,
261+
NULL
262+
},
251263
/* list terminator */
252264
{{NULL}}
253265
};
@@ -1152,6 +1164,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
11521164
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
11531165
{"security_barrier", RELOPT_TYPE_BOOL,
11541166
offsetof(StdRdOptions, security_barrier)},
1167+
{"check_option", RELOPT_TYPE_STRING,
1168+
offsetof(StdRdOptions, check_option_offset)},
11551169
};
11561170

11571171
options = parseRelOptions(reloptions, validate, kind, &numoptions);

src/backend/catalog/information_schema.sql

+7-1
Original file line numberDiff line numberDiff line change
@@ -2494,7 +2494,13 @@ CREATE VIEW views AS
24942494
ELSE null END
24952495
AS character_data) AS view_definition,
24962496

2497-
CAST('NONE' AS character_data) AS check_option,
2497+
CAST(
2498+
CASE WHEN 'check_option=cascaded' = ANY (c.reloptions)
2499+
THEN 'CASCADED'
2500+
WHEN 'check_option=local' = ANY (c.reloptions)
2501+
THEN 'LOCAL'
2502+
ELSE 'NONE' END
2503+
AS character_data) AS check_option,
24982504

24992505
CAST(
25002506
-- (1 << CMD_UPDATE) + (1 << CMD_DELETE)

src/backend/catalog/sql_features.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ F311 Schema definition statement NO
227227
F311 Schema definition statement 01 CREATE SCHEMA YES
228228
F311 Schema definition statement 02 CREATE TABLE for persistent base tables YES
229229
F311 Schema definition statement 03 CREATE VIEW YES
230-
F311 Schema definition statement 04 CREATE VIEW: WITH CHECK OPTION NO
230+
F311 Schema definition statement 04 CREATE VIEW: WITH CHECK OPTION YES
231231
F311 Schema definition statement 05 GRANT statement YES
232232
F312 MERGE statement NO
233233
F313 Enhanced MERGE statement NO
@@ -301,7 +301,7 @@ F711 ALTER domain YES
301301
F721 Deferrable constraints NO foreign and unique keys only
302302
F731 INSERT column privileges YES
303303
F741 Referential MATCH types NO no partial match yet
304-
F751 View CHECK enhancements NO
304+
F751 View CHECK enhancements YES
305305
F761 Session management YES
306306
F762 CURRENT_CATALOG YES
307307
F763 CURRENT_SCHEMA YES

src/backend/commands/tablecmds.c

+36
Original file line numberDiff line numberDiff line change
@@ -8774,6 +8774,42 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
87748774
break;
87758775
}
87768776

8777+
/* Special-case validation of view options */
8778+
if (rel->rd_rel->relkind == RELKIND_VIEW)
8779+
{
8780+
Query *view_query = get_view_query(rel);
8781+
List *view_options = untransformRelOptions(newOptions);
8782+
ListCell *cell;
8783+
bool check_option = false;
8784+
bool security_barrier = false;
8785+
8786+
foreach(cell, view_options)
8787+
{
8788+
DefElem *defel = (DefElem *) lfirst(cell);
8789+
8790+
if (pg_strcasecmp(defel->defname, "check_option") == 0)
8791+
check_option = true;
8792+
if (pg_strcasecmp(defel->defname, "security_barrier") == 0)
8793+
security_barrier = defGetBoolean(defel);
8794+
}
8795+
8796+
/*
8797+
* If the check option is specified, look to see if the view is
8798+
* actually auto-updatable or not.
8799+
*/
8800+
if (check_option)
8801+
{
8802+
const char *view_updatable_error =
8803+
view_query_is_auto_updatable(view_query, security_barrier);
8804+
8805+
if (view_updatable_error)
8806+
ereport(ERROR,
8807+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8808+
errmsg("WITH CHECK OPTION is supported only on auto-updatable views"),
8809+
errhint("%s", view_updatable_error)));
8810+
}
8811+
}
8812+
87778813
/*
87788814
* All we need do here is update the pg_class row; the new options will be
87798815
* propagated into relcaches during post-commit cache inval.

0 commit comments

Comments
 (0)