From 8c2e04d17c11478aa7655d1765495d65468f0fc7 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Thu, 10 Sep 2020 23:01:06 -0700 Subject: [PATCH] sql: allow CONNECTION LIMIT syntax for CREATE DATABASE Release note (sql change): Added the ability to parse the `CONNECTION LIMIT [=] iconst32` syntax for `CREATE DATABASE`. This is currently a no-op for -1 (default, unlimited connections), and errors for any other value. --- .../sql/bnf/create_database_stmt.bnf | 8 +++--- docs/generated/sql/bnf/stmt_block.bnf | 17 +++++++----- pkg/sql/create_database.go | 9 +++++++ .../logictest/testdata/logic_test/database | 10 ++++++- pkg/sql/parser/parse_test.go | 9 +++++++ pkg/sql/parser/sql.y | 26 ++++++++++++++++--- pkg/sql/sem/tree/create.go | 17 +++++++----- 7 files changed, 75 insertions(+), 21 deletions(-) diff --git a/docs/generated/sql/bnf/create_database_stmt.bnf b/docs/generated/sql/bnf/create_database_stmt.bnf index 20c76a82cfd1..d8cb84a05764 100644 --- a/docs/generated/sql/bnf/create_database_stmt.bnf +++ b/docs/generated/sql/bnf/create_database_stmt.bnf @@ -1,5 +1,5 @@ create_database_stmt ::= - 'CREATE' 'DATABASE' database_name opt_with opt_template_clause 'ENCODING' opt_equal non_reserved_word_or_sconst opt_lc_collate_clause opt_lc_ctype_clause - | 'CREATE' 'DATABASE' database_name opt_with opt_template_clause opt_lc_collate_clause opt_lc_ctype_clause - | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause 'ENCODING' opt_equal non_reserved_word_or_sconst opt_lc_collate_clause opt_lc_ctype_clause - | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause opt_lc_collate_clause opt_lc_ctype_clause + 'CREATE' 'DATABASE' database_name opt_with opt_template_clause 'ENCODING' opt_equal non_reserved_word_or_sconst opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit + | 'CREATE' 'DATABASE' database_name opt_with opt_template_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit + | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause 'ENCODING' opt_equal non_reserved_word_or_sconst opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit + | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index f36e3a6c4d22..7c599644d6bb 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -765,6 +765,7 @@ unreserved_keyword ::= | 'CONFIGURATION' | 'CONFIGURATIONS' | 'CONFIGURE' + | 'CONNECTION' | 'CONSTRAINTS' | 'CONTROLCHANGEFEED' | 'CONTROLJOB' @@ -1225,8 +1226,8 @@ create_changefeed_stmt ::= 'CREATE' 'CHANGEFEED' 'FOR' changefeed_targets opt_changefeed_sink opt_with_options create_database_stmt ::= - 'CREATE' 'DATABASE' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause - | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause + 'CREATE' 'DATABASE' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit + | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit create_index_stmt ::= 'CREATE' opt_unique 'INDEX' opt_concurrently opt_index_name 'ON' table_name opt_using_gin_btree '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by opt_where_clause @@ -1684,6 +1685,10 @@ opt_lc_ctype_clause ::= 'LC_CTYPE' opt_equal non_reserved_word_or_sconst | +opt_connection_limit ::= + 'CONNECTION' 'LIMIT' opt_equal signed_iconst + | + opt_unique ::= 'UNIQUE' | @@ -2100,6 +2105,10 @@ opt_equal ::= '=' | +signed_iconst ::= + 'ICONST' + | only_signed_iconst + opt_name ::= name | @@ -2730,10 +2739,6 @@ geo_shape_type ::= | 'GEOMETRYCOLLECTION' | 'GEOMETRY' -signed_iconst ::= - 'ICONST' - | only_signed_iconst - char_aliases ::= 'CHAR' | 'CHARACTER' diff --git a/pkg/sql/create_database.go b/pkg/sql/create_database.go index be6521f54149..11cfe514087d 100644 --- a/pkg/sql/create_database.go +++ b/pkg/sql/create_database.go @@ -68,6 +68,15 @@ func (p *planner) CreateDatabase(ctx context.Context, n *tree.CreateDatabase) (p } } + if n.ConnectionLimit != -1 { + return nil, unimplemented.NewWithIssueDetailf( + 54241, + "create.db.connection_limit", + "only connection limit -1 is supported, got: %d", + n.ConnectionLimit, + ) + } + hasCreateDB, err := p.HasRoleOption(ctx, roleoption.CREATEDB) if err != nil { return nil, err diff --git a/pkg/sql/logictest/testdata/logic_test/database b/pkg/sql/logictest/testdata/logic_test/database index ec89a4bd45f5..a0ed585d9da1 100644 --- a/pkg/sql/logictest/testdata/logic_test/database +++ b/pkg/sql/logictest/testdata/logic_test/database @@ -89,6 +89,12 @@ CREATE DATABASE b5 TEMPLATE=template0 ENCODING='UTF8' LC_COLLATE='C.UTF-8' LC_CT statement ok CREATE DATABASE b6 TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'C.UTF-8' LC_CTYPE 'C.UTF-8' +statement ok +CREATE DATABASE b7 WITH CONNECTION LIMIT -1 + +statement error only connection limit -1 is supported, got: 1 +CREATE DATABASE b8 WITH CONNECTION LIMIT = 1 + statement ok CREATE DATABASE c @@ -102,6 +108,7 @@ b3 b4 b5 b6 +b7 c defaultdb postgres @@ -141,7 +148,8 @@ DROP DATABASE b2 CASCADE; DROP DATABASE b3 CASCADE; DROP DATABASE b4 CASCADE; DROP DATABASE b5 CASCADE; - DROP DATABASE b6 CASCADE + DROP DATABASE b6 CASCADE; + DROP DATABASE b7 CASCADE statement error pgcode 42601 empty database name DROP DATABASE "" diff --git a/pkg/sql/parser/parse_test.go b/pkg/sql/parser/parse_test.go index 605d05a92dd4..c992dff34ea2 100644 --- a/pkg/sql/parser/parse_test.go +++ b/pkg/sql/parser/parse_test.go @@ -69,6 +69,7 @@ func TestParse(t *testing.T) { {`CREATE DATABASE a LC_CTYPE = 'C.UTF-8'`}, {`CREATE DATABASE a LC_CTYPE = 'INVALID'`}, {`CREATE DATABASE a TEMPLATE = 'template0' ENCODING = 'UTF8' LC_COLLATE = 'C.UTF-8' LC_CTYPE = 'INVALID'`}, + {`CREATE DATABASE a CONNECTION LIMIT = 13`}, {`CREATE DATABASE IF NOT EXISTS a`}, {`CREATE DATABASE IF NOT EXISTS a TEMPLATE = 'template0'`}, {`CREATE DATABASE IF NOT EXISTS a TEMPLATE = 'invalid'`}, @@ -1674,6 +1675,14 @@ func TestParse2(t *testing.T) { `CREATE DATABASE a TEMPLATE = 'template0'`}, {`CREATE DATABASE a TEMPLATE = invalid`, `CREATE DATABASE a TEMPLATE = 'invalid'`}, + { + `CREATE DATABASE a WITH CONNECTION LIMIT = 13`, + `CREATE DATABASE a CONNECTION LIMIT = 13`, + }, + { + `CREATE DATABASE a WITH CONNECTION LIMIT -1`, + `CREATE DATABASE a`, + }, {`CREATE TABLE a (b INT) WITH (fillfactor=100)`, `CREATE TABLE a (b INT8)`}, {`CREATE TABLE a (b INT, UNIQUE INDEX foo (b))`, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 1274a60f95dd..003a8d14fc5c 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -583,7 +583,7 @@ func (u *sqlSymUnion) refreshDataOption() tree.RefreshDataOption { %token CHARACTER CHARACTERISTICS CHECK CLOSE %token CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT %token COMMITTED COMPACT COMPLETE CONCAT CONCURRENTLY CONFIGURATION CONFIGURATIONS CONFIGURE -%token CONFLICT CONSTRAINT CONSTRAINTS CONTAINS CONTROLCHANGEFEED CONTROLJOB +%token CONFLICT CONNECTION CONSTRAINT CONSTRAINTS CONTAINS CONTROLCHANGEFEED CONTROLJOB %token CONVERSION CONVERT COPY COVERING CREATE CREATEDB CREATELOGIN CREATEROLE %token CROSS CUBE CURRENT CURRENT_CATALOG CURRENT_DATE CURRENT_SCHEMA %token CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP @@ -922,6 +922,7 @@ func (u *sqlSymUnion) refreshDataOption() tree.RefreshDataOption { %type opt_validate_behavior %type opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause +%type opt_connection_limit %type transaction_iso_level %type transaction_user_priority @@ -7213,7 +7214,7 @@ transaction_deferrable_mode: // %Text: CREATE DATABASE [IF NOT EXISTS] // %SeeAlso: WEBDOCS/create-database.html create_database_stmt: - CREATE DATABASE database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause + CREATE DATABASE database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit { $$.val = &tree.CreateDatabase{ Name: tree.Name($3), @@ -7221,9 +7222,10 @@ create_database_stmt: Encoding: $6, Collate: $7, CType: $8, + ConnectionLimit: $9.int32(), } } -| CREATE DATABASE IF NOT EXISTS database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause +| CREATE DATABASE IF NOT EXISTS database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit { $$.val = &tree.CreateDatabase{ IfNotExists: true, @@ -7232,8 +7234,9 @@ create_database_stmt: Encoding: $9, Collate: $10, CType: $11, + ConnectionLimit: $12.int32(), } - } + } | CREATE DATABASE error // SHOW HELP: CREATE DATABASE opt_template_clause: @@ -7276,6 +7279,20 @@ opt_lc_ctype_clause: $$ = "" } +opt_connection_limit: + CONNECTION LIMIT opt_equal signed_iconst + { + ret, err := $4.numVal().AsInt32() + if err != nil { + return setErr(sqllex, err) + } + $$.val = ret + } +| /* EMPTY */ + { + $$.val = int32(-1) + } + opt_equal: '=' {} | /* EMPTY */ {} @@ -11392,6 +11409,7 @@ unreserved_keyword: | CONFIGURATION | CONFIGURATIONS | CONFIGURE +| CONNECTION | CONSTRAINTS | CONTROLCHANGEFEED | CONTROLJOB diff --git a/pkg/sql/sem/tree/create.go b/pkg/sql/sem/tree/create.go index 2a9a0179d855..59e6a6ff77d1 100644 --- a/pkg/sql/sem/tree/create.go +++ b/pkg/sql/sem/tree/create.go @@ -35,12 +35,13 @@ import ( // CreateDatabase represents a CREATE DATABASE statement. type CreateDatabase struct { - IfNotExists bool - Name Name - Template string - Encoding string - Collate string - CType string + IfNotExists bool + Name Name + Template string + Encoding string + Collate string + CType string + ConnectionLimit int32 } // Format implements the NodeFormatter interface. @@ -66,6 +67,10 @@ func (node *CreateDatabase) Format(ctx *FmtCtx) { ctx.WriteString(" LC_CTYPE = ") lex.EncodeSQLStringWithFlags(&ctx.Buffer, node.CType, ctx.flags.EncodeFlags()) } + if node.ConnectionLimit != -1 { + ctx.WriteString(" CONNECTION LIMIT = ") + ctx.WriteString(strconv.Itoa(int(node.ConnectionLimit))) + } } // IndexElem represents a column with a direction in a CREATE INDEX statement.