Skip to content

Commit dd3d1aa

Browse files
committed
SQL: add database and schema kinds
Signed-off-by: Masatake YAMATO <[email protected]>
1 parent 6383f88 commit dd3d1aa

File tree

7 files changed

+151
-19
lines changed

7 files changed

+151
-19
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--sort=no
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
lusiadas input.sql /^CREATE DATABASE lusiadas;$/;" b
2+
sales input.sql /^CREATE DATABASE sales OWNER salesapp TABLESPACE salesspace;$/;" b
3+
music input.sql /^CREATE DATABASE music$/;" b
4+
music2 input.sql /^CREATE DATABASE music2$/;" b
5+
test input.sql /^create database 'test' || replace(current_date, '-', '');$/;" b
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- Taken from https://www.postgresql.org/docs/current/sql-createdatabase.html
2+
CREATE DATABASE lusiadas;
3+
CREATE DATABASE sales OWNER salesapp TABLESPACE salesspace;
4+
CREATE DATABASE music
5+
LOCALE 'sv_SE.utf8'
6+
TEMPLATE template0;
7+
CREATE DATABASE music2
8+
LOCALE 'sv_SE.iso885915'
9+
ENCODING LATIN9
10+
TEMPLATE template0;
11+
12+
-- Taken from http://www.hplsql.org/create-database
13+
create database 'test' || replace(current_date, '-', '');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--sort=no
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
myschema input.sql /^CREATE SCHEMA myschema;$/;" S
2+
joe input.sql /^CREATE SCHEMA AUTHORIZATION joe;$/;" S
3+
test input.sql /^CREATE SCHEMA IF NOT EXISTS test AUTHORIZATION joe;$/;" S
4+
hollywood input.sql /^CREATE SCHEMA hollywood$/;" S
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- https://www.postgresql.org/docs/current/sql-createschema.html
2+
CREATE SCHEMA myschema;
3+
CREATE SCHEMA AUTHORIZATION joe;
4+
CREATE SCHEMA IF NOT EXISTS test AUTHORIZATION joe;
5+
CREATE SCHEMA hollywood
6+
CREATE TABLE films (title text, release date, awards text[])
7+
CREATE VIEW winners AS
8+
SELECT title, release FROM films WHERE awards IS NOT NULL;
9+
10+
-- TODO: films and winners should be tagged.

parsers/sql.c

Lines changed: 117 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ enum eKeywordId {
7878
KEYWORD_constraint,
7979
KEYWORD_create,
8080
KEYWORD_cursor,
81+
KEYWORD_database,
8182
KEYWORD_datatype,
8283
KEYWORD_declare,
8384
KEYWORD_do,
@@ -130,6 +131,7 @@ enum eKeywordId {
130131
KEYWORD_result,
131132
KEYWORD_return,
132133
KEYWORD_returns,
134+
KEYWORD_schema,
133135
KEYWORD_select,
134136
KEYWORD_service,
135137
KEYWORD_subtype,
@@ -217,9 +219,11 @@ typedef enum {
217219
SQLTAG_BLOCK_LABEL,
218220
SQLTAG_PACKAGE,
219221
SQLTAG_SERVICE,
222+
SQLTAG_SCHEMA,
220223
SQLTAG_TRIGGER,
221224
SQLTAG_PUBLICATION,
222225
SQLTAG_VIEW,
226+
SQLTAG_DATABASE,
223227
SQLTAG_CURSOR,
224228
SQLTAG_PROTOTYPE,
225229
SQLTAG_EVENT,
@@ -245,9 +249,11 @@ static kindDefinition SqlKinds [] = {
245249
{ true, 'L', "label", "block label" },
246250
{ true, 'P', "package", "packages" },
247251
{ true, 'R', "service", "services" },
252+
{ true, 'S', "schema", "schemas" },
248253
{ true, 'T', "trigger", "triggers" },
249254
{ true, 'U', "publication", "publications" },
250255
{ true, 'V', "view", "views" },
256+
{ true, 'b', "database", "database" },
251257
{ true, 'c', "cursor", "cursors" },
252258
{ false, 'd', "prototype", "prototypes" },
253259
{ true, 'e', "event", "events" },
@@ -278,6 +284,7 @@ static const keywordTable SqlKeywordTable [] = {
278284
{ "constraint", KEYWORD_constraint },
279285
{ "create", KEYWORD_create },
280286
{ "cursor", KEYWORD_cursor },
287+
{ "database", KEYWORD_database },
281288
{ "datatype", KEYWORD_datatype },
282289
{ "declare", KEYWORD_declare },
283290
{ "do", KEYWORD_do },
@@ -329,6 +336,7 @@ static const keywordTable SqlKeywordTable [] = {
329336
{ "result", KEYWORD_result },
330337
{ "return", KEYWORD_return },
331338
{ "returns", KEYWORD_returns },
339+
{ "schema", KEYWORD_schema },
332340
{ "select", KEYWORD_select },
333341
{ "service", KEYWORD_service },
334342
{ "subtype", KEYWORD_subtype },
@@ -399,6 +407,8 @@ static bool SqlReservedWordPredicatorForIsOrAs (tokenInfo *const token);
399407
static struct SqlReservedWord SqlReservedWord [SQLKEYWORD_COUNT] = {
400408
/*
401409
* RESERVED_BIT: MYSQL & POSTGRESQL&SQL2016&SQL2011&SQL92 & ORACLE11g&PLSQL & SQLANYWERE
410+
*
411+
* { 0 } means we have not inspect whether the keyword is reserved or not.
402412
*/
403413
[KEYWORD_at] = {0 & 0&1&1&1 & 0&1 & 0},
404414
[KEYWORD_begin] = {0 & 0&1&1&1 & 0&1 & 1},
@@ -410,6 +420,7 @@ static struct SqlReservedWord SqlReservedWord [SQLKEYWORD_COUNT] = {
410420
[KEYWORD_constraint] = {1 & 1&1&1&1 & 0&1 & 1},
411421
[KEYWORD_create] = {1 & 1&1&1&1 & 1&1 & 1},
412422
[KEYWORD_cursor] = {1 & 0&1&1&1 & 0&1 & 1},
423+
[KEYWORD_database] = { 0 },
413424
[KEYWORD_datatype] = {0 & 0&0&0&0 & 0&0 & 0},
414425
[KEYWORD_declare] = {1 & 0&1&1&1 & 0&1 & 1},
415426
[KEYWORD_do] = {0 & 1&0&0&0 & 0&1 & 1},
@@ -462,6 +473,7 @@ static struct SqlReservedWord SqlReservedWord [SQLKEYWORD_COUNT] = {
462473
[KEYWORD_result] = {0 & 0&1&1&0 & 0&0 & 0},
463474
[KEYWORD_return] = {1 & 0&1&1&0 & 0&1 & 1},
464475
[KEYWORD_returns] = {0 & 0&0&0&0 & 0&0 & 0},
476+
[KEYWORD_schema] = {0 & 0&0&0&0 & 0&0 & 0},
465477
[KEYWORD_select] = {1 & 1&1&1&1 & 1&1 & 1},
466478
[KEYWORD_service] = {0 & 0&0&0&0 & 0&0 & 0},
467479
[KEYWORD_subtype] = {0 & 0&0&0&0 & 0&1 & 0},
@@ -2169,6 +2181,44 @@ static void parseColumnsAndAliases (tokenInfo *const token)
21692181
deleteToken (lastId);
21702182
}
21712183

2184+
/* Skip "IF NOT EXISTS"
2185+
* https://dev.mysql.com/doc/refman/8.0/en/create-table.html
2186+
* https://www.postgresql.org/docs/current/sql-createtable.html
2187+
* https://sqlite.org/lang_createtable.html
2188+
*/
2189+
static bool parseIdAfterIfNotExists(tokenInfo *const name,
2190+
tokenInfo *const token,
2191+
bool authorization_following)
2192+
{
2193+
if (isKeyword (name, KEYWORD_if)
2194+
&& (isType (token, TOKEN_IDENTIFIER)
2195+
&& vStringLength (token->string) == 3
2196+
&& strcasecmp ("not", vStringValue (token->string)) == 0))
2197+
{
2198+
readToken (token);
2199+
if (isType (token, TOKEN_IDENTIFIER)
2200+
&& vStringLength (token->string) == 6
2201+
&& strcasecmp ("exists", vStringValue (token->string)) == 0)
2202+
{
2203+
readIdentifier (name);
2204+
if (authorization_following
2205+
&& isType (name, TOKEN_IDENTIFIER)
2206+
&& vStringLength (name->string) == 13
2207+
&& strcasecmp("authorization", vStringValue(name->string)) == 0)
2208+
{
2209+
/*
2210+
* PostgreSQL:
2211+
* - CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_specification
2212+
*/
2213+
readIdentifier (name);
2214+
}
2215+
readToken (token);
2216+
return true;
2217+
}
2218+
}
2219+
return false;
2220+
}
2221+
21722222
static void parseTable (tokenInfo *const token)
21732223
{
21742224
tokenInfo *const name = newToken ();
@@ -2207,25 +2257,7 @@ static void parseTable (tokenInfo *const token)
22072257
readIdentifier (name);
22082258
readToken (token);
22092259

2210-
/* Skip "IF NOT EXISTS"
2211-
* https://dev.mysql.com/doc/refman/8.0/en/create-table.html
2212-
* https://www.postgresql.org/docs/current/sql-createtable.html
2213-
* https://sqlite.org/lang_createtable.html
2214-
*/
2215-
if (isKeyword (name, KEYWORD_if)
2216-
&& (isType (token, TOKEN_IDENTIFIER)
2217-
&& vStringLength (token->string) == 3
2218-
&& strcasecmp ("not", vStringValue (token->string)) == 0))
2219-
{
2220-
readToken (token);
2221-
if (isType (token, TOKEN_IDENTIFIER)
2222-
&& vStringLength (token->string) == 6
2223-
&& strcasecmp ("exists", vStringValue (token->string)) == 0)
2224-
{
2225-
readIdentifier (name);
2226-
readToken (token);
2227-
}
2228-
}
2260+
parseIdAfterIfNotExists(name, token, false);
22292261

22302262
if (isType (token, TOKEN_PERIOD))
22312263
{
@@ -2902,6 +2934,70 @@ static void parseCCFLAGS (tokenInfo *const token)
29022934

29032935
}
29042936

2937+
static void parseDatabase (tokenInfo *const token, enum eKeywordId keyword)
2938+
{
2939+
tokenInfo * name;
2940+
2941+
/*
2942+
* In MySQL and HPL/SQL, "CREATE DATABASE" and "CREATE SCHEMA"
2943+
* are the same. However, In PostgreSQL, they are different.
2944+
* Too support PostgreSQL, we prepare different kinds for them.
2945+
*
2946+
* MySQL
2947+
* A. CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name ...;
2948+
*
2949+
* PostgreSQL
2950+
*
2951+
* B. CREATE DATABASE name ...;
2952+
*
2953+
* C. CREATE SCHEMA schema_name [ AUTHORIZATION role_specification ] [ schema_element [ ... ] ]
2954+
* D. CREATE SCHEMA AUTHORIZATION role_specification [ schema_element [ ... ] ]
2955+
* E. CREATE SCHEMA IF NOT EXISTS schema_name [ AUTHORIZATION role_specification ]
2956+
* F. CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_specification
2957+
*
2958+
* HPL/SQL
2959+
* G. CREATE DATABASE | SCHEMA [IF NOT EXISTS] dbname_expr...;
2960+
*/
2961+
readIdentifier (token);
2962+
if (keyword == KEYWORD_schema
2963+
&& isType (token, TOKEN_IDENTIFIER)
2964+
&& vStringLength (token->string) == 13
2965+
&& strcasecmp("authorization", vStringValue(token->string)) == 0)
2966+
{
2967+
/* D. */
2968+
readIdentifier (token);
2969+
makeSqlTag (token, SQLTAG_SCHEMA);
2970+
findCmdTerm (token, false);
2971+
return;
2972+
}
2973+
2974+
name = newToken ();
2975+
copyToken (name, token);
2976+
readIdentifier (token);
2977+
parseIdAfterIfNotExists (name, token, true);
2978+
2979+
makeSqlTag (name,
2980+
keyword == KEYWORD_database
2981+
? SQLTAG_DATABASE: SQLTAG_SCHEMA);
2982+
deleteToken (name);
2983+
2984+
/* TODO:
2985+
*
2986+
* In PostgreSQL, CREATE FOO can follow to CREATE SCHEMA like:
2987+
*
2988+
* -- https://www.postgresql.org/docs/current/sql-createschema.html
2989+
*
2990+
* CREATE SCHEMA hollywood
2991+
* CREATE TABLE films (title text, release date, awards text[])
2992+
* CREATE VIEW winners AS
2993+
* SELECT title, release FROM films WHERE awards IS NOT NULL;
2994+
*
2995+
* In above example, "hollywood.films" and "hollywood.winners" should be
2996+
* tagged.
2997+
*/
2998+
findCmdTerm (token, true);
2999+
}
3000+
29053001
static void parseKeywords (tokenInfo *const token)
29063002
{
29073003
switch (token->keyword)
@@ -2913,6 +3009,7 @@ static void parseKeywords (tokenInfo *const token)
29133009
break;
29143010
case KEYWORD_comment: parseComment (token); break;
29153011
case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
3012+
case KEYWORD_database: parseDatabase (token, KEYWORD_database); break;
29163013
case KEYWORD_datatype: parseDomain (token); break;
29173014
case KEYWORD_declare: parseBlock (token, false); break;
29183015
case KEYWORD_domain: parseDomain (token); break;
@@ -2936,6 +3033,7 @@ static void parseKeywords (tokenInfo *const token)
29363033
case KEYWORD_package: parsePackage (token); break;
29373034
case KEYWORD_procedure: parseSubProgram (token); break;
29383035
case KEYWORD_publication: parsePublication (token); break;
3036+
case KEYWORD_schema: parseDatabase (token, KEYWORD_schema); break;
29393037
case KEYWORD_service: parseService (token); break;
29403038
case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
29413039
case KEYWORD_synonym: parseSynonym (token); break;

0 commit comments

Comments
 (0)