Skip to content

Commit 0e4a13d

Browse files
committed
sql: add security_invoker feature flag and grammar support
This change adds the `enable_view_security_invoker` feature flag. It also adds grammar support for the following two statements: ``` CREATE VIEW <view_name> WITH ( security_invoker = true ) AS SELECT * FROM <table_name>; ALTER VIEW <view_name> SET ( security_invoker = false ); ``` Informs: #138918 Epic: CRDB-48807 Release note: None
1 parent ad5c16a commit 0e4a13d

File tree

12 files changed

+279
-9
lines changed

12 files changed

+279
-9
lines changed

docs/generated/sql/bnf/stmt_block.bnf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,7 @@ unreserved_keyword ::=
14591459
| 'SEARCH'
14601460
| 'SECOND'
14611461
| 'SECURITY'
1462+
| 'SECURITY_INVOKER'
14621463
| 'SECONDARY'
14631464
| 'SERIALIZABLE'
14641465
| 'SEQUENCE'
@@ -4385,6 +4386,7 @@ bare_label_keywords ::=
43854386
| 'SEARCH'
43864387
| 'SECONDARY'
43874388
| 'SECURITY'
4389+
| 'SECURITY_INVOKER'
43884390
| 'SELECT'
43894391
| 'SEQUENCE'
43904392
| 'SEQUENCES'

pkg/sql/create_view.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ func (n *createViewNode) startExec(params runParams) error {
7777
return pgerror.Newf(pgcode.ReadOnlySQLTransaction, "schema changes are not allowed on a reader catalog")
7878
}
7979
createView := n.createView
80+
81+
if !params.SessionData().AllowViewWithSecurityInvokerClause && createView.Options != nil && createView.Options.SecurityInvoker {
82+
return pgerror.Newf(pgcode.FeatureNotSupported,
83+
"security invoker views are not supported")
84+
}
85+
8086
tableType := tree.GetTableType(
8187
false /* isSequence */, true /* isView */, createView.Materialized,
8288
)

pkg/sql/exec_util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3720,6 +3720,10 @@ func (m *sessionDataMutator) SetAlterColumnTypeGeneral(val bool) {
37203720
m.data.AlterColumnTypeGeneralEnabled = val
37213721
}
37223722

3723+
func (m *sessionDataMutator) SetAllowViewWithSecurityInvokerClause(val bool) {
3724+
m.data.AllowViewWithSecurityInvokerClause = val
3725+
}
3726+
37233727
func (m *sessionDataMutator) SetEnableSuperRegions(val bool) {
37243728
m.data.EnableSuperRegions = val
37253729
}

pkg/sql/logictest/testdata/logic_test/row_level_security

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5634,4 +5634,27 @@ DROP FUNCTION fail, divbyzero;
56345634
statement ok
56355635
DROP ROLE alice;
56365636

5637+
subtest unimplemented_alter_view_with_security_invoker
5638+
5639+
statement ok
5640+
CREATE TABLE roach_pg_table ( count INT )
5641+
5642+
statement ok
5643+
SET allow_view_with_security_invoker_clause = on
5644+
5645+
statement ok
5646+
CREATE VIEW security_invoker_view WITH ( security_invoker = true ) AS SELECT * FROM roach_pg_table
5647+
5648+
statement error pq: at or near "\)": syntax error: unimplemented: this syntax\nHINT: You have attempted to use a feature that is not yet implemented\.\n\nPlease check the public issue tracker to check whether this problem is\nalready tracked\. If you cannot find it there, please report the error\nwith details by creating a new issue\.\n\nIf you would rather not post publicly, please contact us directly\nusing the support form\.\n\nWe appreciate your feedback\.\n\nDETAIL: source SQL:\nALTER VIEW security_invoker_view SET \( security_invoker = false \)
5649+
ALTER VIEW security_invoker_view SET ( security_invoker = false )
5650+
5651+
statement ok
5652+
DROP VIEW security_invoker_view
5653+
5654+
statement ok
5655+
DROP TABLE roach_pg_table
5656+
5657+
statement ok
5658+
SET allow_view_with_security_invoker_clause = off
5659+
56375660
subtest end

pkg/sql/logictest/testdata/logic_test/views

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1933,9 +1933,21 @@ CREATE VIEW v AS (SELECT 1)
19331933
statement error cross database type references are not supported: db106602a.public.e
19341934
CREATE OR REPLACE VIEW v AS (SELECT 1 FROM (VALUES (1)) val(i) WHERE 'foo'::db106602a.e = 'foo'::db106602a.e)
19351935

1936-
subtest end
1936+
subtest regression_test_128535
19371937

19381938
# Regression test for hitting an assertion error when using an unimplemented
19391939
# builtin in the view (#128535).
19401940
statement error pgcode 0A000 unimplemented
19411941
CREATE VIEW v128535 AS SELECT json_to_tsvector()
1942+
1943+
subtest allow_view_with_security_invoker_clause_feature_flag
1944+
1945+
statement ok
1946+
SET allow_view_with_security_invoker_clause = on;
1947+
1948+
query T
1949+
show session allow_view_with_security_invoker_clause;
1950+
----
1951+
on
1952+
1953+
subtest end

pkg/sql/parser/sql.y

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ func (u *sqlSymUnion) auditMode() tree.AuditMode {
270270
func (u *sqlSymUnion) bool() bool {
271271
return u.val.(bool)
272272
}
273+
func (u *sqlSymUnion) viewOptions() *tree.ViewOptions {
274+
return u.val.(*tree.ViewOptions)
275+
}
273276
func (u *sqlSymUnion) strPtr() *string {
274277
return u.val.(*string)
275278
}
@@ -1056,7 +1059,7 @@ func (u *sqlSymUnion) doBlockOption() tree.DoBlockOption {
10561059
%token <str> REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP ROUTINES ROW ROWS RSHIFT RULE RUNNING
10571060

10581061
%token <str> SAVEPOINT SCANS SCATTER SCHEDULE SCHEDULES SCROLL SCHEMA SCHEMA_ONLY SCHEMAS SCRUB
1059-
%token <str> SEARCH SECOND SECONDARY SECURITY SELECT SEQUENCE SEQUENCES
1062+
%token <str> SEARCH SECOND SECONDARY SECURITY SECURITY_INVOKER SELECT SEQUENCE SEQUENCES
10601063
%token <str> SERIALIZABLE SERVER SERVICE SESSION SESSIONS SESSION_USER SET SETOF SETS SETTING SETTINGS
10611064
%token <str> SHARE SHARED SHOW SIMILAR SIMPLE SIZE SKIP SKIP_LOCALITIES_CHECK SKIP_MISSING_FOREIGN_KEYS
10621065
%token <str> SKIP_MISSING_SEQUENCES SKIP_MISSING_SEQUENCE_OWNERS SKIP_MISSING_VIEWS SKIP_MISSING_UDFS SMALLINT SMALLSERIAL
@@ -1206,6 +1209,7 @@ func (u *sqlSymUnion) doBlockOption() tree.DoBlockOption {
12061209
%type <tree.Statement> alter_rename_view_stmt
12071210
%type <tree.Statement> alter_view_set_schema_stmt
12081211
%type <tree.Statement> alter_view_owner_stmt
1212+
%type <tree.Statement> alter_view_set_options_stmt
12091213

12101214
// ALTER SEQUENCE
12111215
%type <tree.Statement> alter_rename_sequence_stmt
@@ -1801,6 +1805,7 @@ func (u *sqlSymUnion) doBlockOption() tree.DoBlockOption {
18011805
%type <*tree.TriggerTransition> trigger_transition
18021806
%type <[]*tree.TriggerTransition> trigger_transition_list opt_trigger_transition_list
18031807
%type <bool> transition_is_new transition_is_row
1808+
%type <*tree.ViewOptions> opt_view_with
18041809
%type <tree.TriggerForEach> trigger_for_each trigger_for_type
18051810
%type <tree.Expr> trigger_when
18061811
%type <str> trigger_func_arg opt_as function_or_procedure
@@ -2055,6 +2060,7 @@ alter_view_stmt:
20552060
alter_rename_view_stmt
20562061
| alter_view_set_schema_stmt
20572062
| alter_view_owner_stmt
2063+
| alter_view_set_options_stmt
20582064
// ALTER VIEW has its error help token here because the ALTER VIEW
20592065
// prefix is spread over multiple non-terminals.
20602066
| ALTER VIEW error // SHOW HELP: ALTER VIEW
@@ -11901,44 +11907,50 @@ role_or_group_or_user:
1190111907
// %Help: CREATE VIEW - create a new view
1190211908
// %Category: DDL
1190311909
// %Text:
11904-
// CREATE [TEMPORARY | TEMP] VIEW [IF NOT EXISTS] <viewname> [( <colnames...> )] AS <source>
11910+
// CREATE [TEMPORARY | TEMP] VIEW [IF NOT EXISTS] <viewname> [( <colnames...> )] [WITH ( <option> [= <value>] [, ....] )] AS <source>
1190511911
// CREATE [TEMPORARY | TEMP] MATERIALIZED VIEW [IF NOT EXISTS] <viewname> [( <colnames...> )] AS <source> [WITH [NO] DATA]
11912+
//
11913+
// Options:
11914+
// security_invoker [= { true | false | 1 | 0 }]: controls view permissions (defaults to true if specified without value)
1190611915
// %SeeAlso: CREATE TABLE, SHOW CREATE, WEBDOCS/create-view.html
1190711916
create_view_stmt:
11908-
CREATE opt_temp opt_view_recursive VIEW view_name opt_column_list AS select_stmt
11917+
CREATE opt_temp opt_view_recursive VIEW view_name opt_column_list opt_view_with AS select_stmt
1190911918
{
1191011919
name := $5.unresolvedObjectName().ToTableName()
1191111920
$$.val = &tree.CreateView{
1191211921
Name: name,
1191311922
ColumnNames: $6.nameList(),
11914-
AsSource: $8.slct(),
11923+
AsSource: $9.slct(),
1191511924
Persistence: $2.persistence(),
11925+
Options: $7.viewOptions(),
1191611926
IfNotExists: false,
1191711927
Replace: false,
1191811928
}
1191911929
}
1192011930
// We cannot use a rule like opt_or_replace here as that would cause a conflict
1192111931
// with the opt_temp rule.
11922-
| CREATE OR REPLACE opt_temp opt_view_recursive VIEW view_name opt_column_list AS select_stmt
11932+
| CREATE OR REPLACE opt_temp opt_view_recursive VIEW view_name opt_column_list opt_view_with AS select_stmt
1192311933
{
1192411934
name := $7.unresolvedObjectName().ToTableName()
1192511935
$$.val = &tree.CreateView{
1192611936
Name: name,
1192711937
ColumnNames: $8.nameList(),
11928-
AsSource: $10.slct(),
11938+
AsSource: $11.slct(),
1192911939
Persistence: $4.persistence(),
11940+
Options: $9.viewOptions(),
1193011941
IfNotExists: false,
1193111942
Replace: true,
1193211943
}
1193311944
}
11934-
| CREATE opt_temp opt_view_recursive VIEW IF NOT EXISTS view_name opt_column_list AS select_stmt
11945+
| CREATE opt_temp opt_view_recursive VIEW IF NOT EXISTS view_name opt_column_list opt_view_with AS select_stmt
1193511946
{
1193611947
name := $8.unresolvedObjectName().ToTableName()
1193711948
$$.val = &tree.CreateView{
1193811949
Name: name,
1193911950
ColumnNames: $9.nameList(),
11940-
AsSource: $11.slct(),
11951+
AsSource: $12.slct(),
1194111952
Persistence: $2.persistence(),
11953+
Options: $10.viewOptions(),
1194211954
IfNotExists: true,
1194311955
Replace: false,
1194411956
}
@@ -12154,6 +12166,44 @@ opt_view_recursive:
1215412166
/* EMPTY */ { /* no error */ }
1215512167
| RECURSIVE { return unimplemented(sqllex, "create recursive view") }
1215612168

12169+
// View-specific WITH clause that only accepts security_invoker
12170+
opt_view_with:
12171+
/* EMPTY */
12172+
{
12173+
$$.val = (*tree.ViewOptions)(nil)
12174+
}
12175+
| WITH '(' SECURITY_INVOKER ')'
12176+
{
12177+
/* SKIP DOC */
12178+
// security_invoker without value defaults to true
12179+
$$.val = &tree.ViewOptions{SecurityInvoker: true}
12180+
}
12181+
| WITH '(' SECURITY_INVOKER '=' TRUE ')'
12182+
{
12183+
/* SKIP DOC */
12184+
$$.val = &tree.ViewOptions{SecurityInvoker: true}
12185+
}
12186+
| WITH '(' SECURITY_INVOKER '=' FALSE ')'
12187+
{
12188+
/* SKIP DOC */
12189+
$$.val = &tree.ViewOptions{SecurityInvoker: false}
12190+
}
12191+
| WITH '(' SECURITY_INVOKER '=' ICONST ')'
12192+
{
12193+
/* SKIP DOC */
12194+
// Handle integer values: 1 = true, 0 = false
12195+
val, err := $5.numVal().AsInt64()
12196+
if err != nil {
12197+
return setErr(sqllex, err)
12198+
}
12199+
if val == 1 {
12200+
$$.val = &tree.ViewOptions{SecurityInvoker: true}
12201+
} else if val == 0 {
12202+
$$.val = &tree.ViewOptions{SecurityInvoker: false}
12203+
} else {
12204+
return setErr(sqllex, errors.New("security_invoker accepts only true/false or 1/0"))
12205+
}
12206+
}
1215712207

1215812208
// %Help: CREATE TYPE - create a type
1215912209
// %Category: DDL
@@ -12768,6 +12818,16 @@ alter_view_owner_stmt:
1276812818
}
1276912819
}
1277012820

12821+
alter_view_set_options_stmt:
12822+
ALTER VIEW relation_expr SET '(' SECURITY_INVOKER '=' var_value ')'
12823+
{
12824+
return unimplemented(sqllex, "ALTER VIEW ... SET (security_invoker = ...) is not yet implemented.")
12825+
}
12826+
| ALTER VIEW IF EXISTS relation_expr SET '(' SECURITY_INVOKER '=' var_value ')'
12827+
{
12828+
return unimplemented(sqllex, "ALTER VIEW ... IF EXISTS SET (security_invoker = ...) is not yet implemented.")
12829+
}
12830+
1277112831
alter_sequence_set_schema_stmt:
1277212832
ALTER SEQUENCE relation_expr SET SCHEMA schema_name
1277312833
{
@@ -18493,6 +18553,7 @@ unreserved_keyword:
1849318553
| SEARCH
1849418554
| SECOND
1849518555
| SECURITY
18556+
| SECURITY_INVOKER
1849618557
| SECONDARY
1849718558
| SERIALIZABLE
1849818559
| SEQUENCE
@@ -19074,6 +19135,7 @@ bare_label_keywords:
1907419135
| SEARCH
1907519136
| SECONDARY
1907619137
| SECURITY
19138+
| SECURITY_INVOKER
1907719139
| SELECT
1907819140
| SEQUENCE
1907919141
| SEQUENCES

pkg/sql/parser/testdata/alter_view

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,24 @@ ALTER MATERIALIZED VIEW IF EXISTS v RENAME TO v
6161
ALTER MATERIALIZED VIEW IF EXISTS v RENAME TO v -- fully parenthesized
6262
ALTER MATERIALIZED VIEW IF EXISTS v RENAME TO v -- literals removed
6363
ALTER MATERIALIZED VIEW IF EXISTS _ RENAME TO _ -- identifiers removed
64+
65+
error
66+
ALTER VIEW v SET (security_invoker = true)
67+
----
68+
----
69+
at or near ")": syntax error: unimplemented: this syntax
70+
DETAIL: source SQL:
71+
ALTER VIEW v SET (security_invoker = true)
72+
^
73+
HINT: You have attempted to use a feature that is not yet implemented.
74+
75+
Please check the public issue tracker to check whether this problem is
76+
already tracked. If you cannot find it there, please report the error
77+
with details by creating a new issue.
78+
79+
If you would rather not post publicly, please contact us directly
80+
using the support form.
81+
82+
We appreciate your feedback.
83+
----
84+
----

pkg/sql/parser/testdata/create_view

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,84 @@ REFRESH MATERIALIZED VIEW a.b AS OF SYSTEM TIME '2025-01-01 11:11:11' WITH NO DA
215215
REFRESH MATERIALIZED VIEW a.b AS OF SYSTEM TIME ('2025-01-01 11:11:11') WITH NO DATA -- fully parenthesized
216216
REFRESH MATERIALIZED VIEW a.b AS OF SYSTEM TIME '_' WITH NO DATA -- literals removed
217217
REFRESH MATERIALIZED VIEW _._ AS OF SYSTEM TIME '2025-01-01 11:11:11' WITH NO DATA -- identifiers removed
218+
219+
parse
220+
CREATE VIEW a WITH (SECURITY_INVOKER) AS SELECT * FROM b
221+
----
222+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- normalized!
223+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT (*) FROM b -- fully parenthesized
224+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- literals removed
225+
CREATE VIEW _ WITH ( security_invoker = true ) AS SELECT * FROM _ -- identifiers removed
226+
227+
parse
228+
CREATE VIEW a WITH (SECURITY_INVOKER = TRUE) AS SELECT * FROM b
229+
----
230+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- normalized!
231+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT (*) FROM b -- fully parenthesized
232+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- literals removed
233+
CREATE VIEW _ WITH ( security_invoker = true ) AS SELECT * FROM _ -- identifiers removed
234+
235+
parse
236+
CREATE VIEW a WITH (SECURITY_INVOKER = FALSE) AS SELECT * FROM b
237+
----
238+
CREATE VIEW a WITH ( security_invoker = false ) AS SELECT * FROM b -- normalized!
239+
CREATE VIEW a WITH ( security_invoker = false ) AS SELECT (*) FROM b -- fully parenthesized
240+
CREATE VIEW a WITH ( security_invoker = false ) AS SELECT * FROM b -- literals removed
241+
CREATE VIEW _ WITH ( security_invoker = false ) AS SELECT * FROM _ -- identifiers removed
242+
243+
parse
244+
CREATE VIEW a WITH (SECURITY_INVOKER = 1) AS SELECT * FROM b
245+
----
246+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- normalized!
247+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT (*) FROM b -- fully parenthesized
248+
CREATE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- literals removed
249+
CREATE VIEW _ WITH ( security_invoker = true ) AS SELECT * FROM _ -- identifiers removed
250+
251+
parse
252+
CREATE VIEW a WITH (SECURITY_INVOKER = 0) AS SELECT * FROM b
253+
----
254+
CREATE VIEW a WITH ( security_invoker = false ) AS SELECT * FROM b -- normalized!
255+
CREATE VIEW a WITH ( security_invoker = false ) AS SELECT (*) FROM b -- fully parenthesized
256+
CREATE VIEW a WITH ( security_invoker = false ) AS SELECT * FROM b -- literals removed
257+
CREATE VIEW _ WITH ( security_invoker = false ) AS SELECT * FROM _ -- identifiers removed
258+
259+
parse
260+
CREATE VIEW a (x, y) WITH (SECURITY_INVOKER) AS SELECT c, d FROM b
261+
----
262+
CREATE VIEW a (x, y) WITH ( security_invoker = true ) AS SELECT c, d FROM b -- normalized!
263+
CREATE VIEW a (x, y) WITH ( security_invoker = true ) AS SELECT (c), (d) FROM b -- fully parenthesized
264+
CREATE VIEW a (x, y) WITH ( security_invoker = true ) AS SELECT c, d FROM b -- literals removed
265+
CREATE VIEW _ (_, _) WITH ( security_invoker = true ) AS SELECT _, _ FROM _ -- identifiers removed
266+
267+
parse
268+
CREATE OR REPLACE VIEW a WITH (SECURITY_INVOKER) AS SELECT * FROM b
269+
----
270+
CREATE OR REPLACE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- normalized!
271+
CREATE OR REPLACE VIEW a WITH ( security_invoker = true ) AS SELECT (*) FROM b -- fully parenthesized
272+
CREATE OR REPLACE VIEW a WITH ( security_invoker = true ) AS SELECT * FROM b -- literals removed
273+
CREATE OR REPLACE VIEW _ WITH ( security_invoker = true ) AS SELECT * FROM _ -- identifiers removed
274+
275+
parse
276+
CREATE VIEW IF NOT EXISTS a WITH (SECURITY_INVOKER) AS SELECT * FROM b
277+
----
278+
CREATE VIEW IF NOT EXISTS a WITH ( security_invoker = true ) AS SELECT * FROM b -- normalized!
279+
CREATE VIEW IF NOT EXISTS a WITH ( security_invoker = true ) AS SELECT (*) FROM b -- fully parenthesized
280+
CREATE VIEW IF NOT EXISTS a WITH ( security_invoker = true ) AS SELECT * FROM b -- literals removed
281+
CREATE VIEW IF NOT EXISTS _ WITH ( security_invoker = true ) AS SELECT * FROM _ -- identifiers removed
282+
283+
error
284+
CREATE VIEW a WITH (SECURITY_INVOKER = 2) AS SELECT * FROM b
285+
----
286+
at or near ")": syntax error: security_invoker accepts only true/false or 1/0
287+
DETAIL: source SQL:
288+
CREATE VIEW a WITH (SECURITY_INVOKER = 2) AS SELECT * FROM b
289+
^
290+
291+
error
292+
CREATE VIEW a WITH (SECURITY_INVOKER = 'invalid') AS SELECT * FROM b
293+
----
294+
at or near "invalid": syntax error
295+
DETAIL: source SQL:
296+
CREATE VIEW a WITH (SECURITY_INVOKER = 'invalid') AS SELECT * FROM b
297+
^
298+
HINT: try \h CREATE VIEW

0 commit comments

Comments
 (0)