From 67a04ea0cfda09e2a14829dd83a69d5f9295041d Mon Sep 17 00:00:00 2001 From: Will Montgomery Date: Sun, 8 Jan 2023 13:47:15 -0500 Subject: [PATCH 1/8] feat(parser): add SHOW statements for mysql to parser --- README.md | 7 +- src/defines.ts | 8 +- src/parser.ts | 49 ++++++++++- src/tokenizer.ts | 6 ++ test/identifier/single-statement.spec.ts | 106 +++++++++++++++++++++++ 5 files changed, 173 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0114ba0..02ad15c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,12 @@ This way you have sure is a valid query before trying to identify the types. * CREATE_FUNCTION * CREATE_INDEX * CREATE_PROCEDURE +* SHOW_DATABASES +* SHOW_KEYS (Mysql dialects only) +* SHOW_INDEX (Mysql dialects only) +* SHOW_TABLE (Mysql dialects only) (Refers to SHOW TABLE STATUS) +* SHOW_TABLES (Mysql dialects only) +* SHOW_COLUMNS (Mysql dialects only) * DROP_DATABASE * DROP_SCHEMA * DROP_TABLE @@ -67,7 +73,6 @@ Execution types allow to know what is the query behavior * `LISTING:` is when the query list the data * `MODIFICATION:` is when the query modificate the database somehow (structure or data) -* `INFORMATION:` is show some data information such as a profile data * `ANON_BLOCK: ` is for an anonymous block query which may contain multiple statements of unknown type (BigQuery and Oracle dialects only) * `UNKNOWN`: (only available if strict mode is disabled) diff --git a/src/defines.ts b/src/defines.ts index 2b45648..90aa5f4 100644 --- a/src/defines.ts +++ b/src/defines.ts @@ -7,7 +7,7 @@ export const DIALECTS = [ 'bigquery', 'generic', ] as const; -export type Dialect = typeof DIALECTS[number]; +export type Dialect = (typeof DIALECTS)[number]; export type StatementType = | 'INSERT' | 'UPDATE' @@ -22,6 +22,12 @@ export type StatementType = | 'CREATE_FUNCTION' | 'CREATE_INDEX' | 'CREATE_PROCEDURE' + | 'SHOW_DATABASES' + | 'SHOW_KEYS' + | 'SHOW_INDEX' + | 'SHOW_TABLE' + | 'SHOW_TABLES' + | 'SHOW_COLUMNS' | 'DROP_DATABASE' | 'DROP_SCHEMA' | 'DROP_TABLE' diff --git a/src/parser.ts b/src/parser.ts index 14dffa5..4654406 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -20,7 +20,6 @@ interface StatementParser { * Execution types allow to know what is the query behavior * - LISTING: is when the query list the data * - MODIFICATION: is when the query modificate the database somehow (structure or data) - * - INFORMATION: is show some data information such as a profile data * - UNKNOWN */ export const EXECUTION_TYPES: Record = { @@ -37,6 +36,12 @@ export const EXECUTION_TYPES: Record = { CREATE_FUNCTION: 'MODIFICATION', CREATE_INDEX: 'MODIFICATION', CREATE_PROCEDURE: 'MODIFICATION', + SHOW_DATABASES: 'LISTING', + SHOW_KEYS: 'LISTING', + SHOW_INDEX: 'LISTING', + SHOW_TABLE: 'LISTING', // for SHOW TABLE STATUS + SHOW_TABLES: 'LISTING', + SHOW_COLUMNS: 'LISTING', DROP_DATABASE: 'MODIFICATION', DROP_SCHEMA: 'MODIFICATION', DROP_TABLE: 'MODIFICATION', @@ -265,6 +270,8 @@ function createStatementParserByToken( return createSelectStatementParser(options); case 'CREATE': return createCreateStatementParser(options); + case 'SHOW': + return createShowStatementParser(options); case 'DROP': return createDropStatementParser(options); case 'ALTER': @@ -580,6 +587,46 @@ function createTruncateStatementParser(options: ParseOptions) { return stateMachineStatementParser(statement, steps, options); } +function createShowStatementParser(options: ParseOptions) { + const statement = createInitialStatement(); + + const steps: Step[] = [ + { + preCanGoToNext: () => false, + validation: { + acceptTokens: [{ type: 'keyword', value: 'SHOW' }], + }, + add: (token) => { + if (statement.start < 0) { + statement.start = token.start; + } + }, + postCanGoToNext: () => true, + }, + // Database/Table/Columns/... + { + preCanGoToNext: () => false, + validation: { + requireBefore: ['whitespace'], + acceptTokens: [ + { type: 'keyword', value: 'DATABASES' }, + { type: 'keyword', value: 'KEYS' }, + { type: 'keyword', value: 'INDEX' }, + { type: 'keyword', value: 'COLUMNS' }, + { type: 'keyword', value: 'TABLES' }, + { type: 'keyword', value: 'TABLE' }, + ], + }, + add: (token) => { + statement.type = `SHOW_${token.value.toUpperCase().replace(' ', '_')}` as StatementType; + }, + postCanGoToNext: () => true, + }, + ]; + + return stateMachineStatementParser(statement, steps, options); +} + function createUnknownStatementParser(options: ParseOptions) { const statement = createInitialStatement(); diff --git a/src/tokenizer.ts b/src/tokenizer.ts index cf5fbff..890728e 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -34,6 +34,12 @@ const KEYWORDS = [ 'WHILE', 'FOR', 'PROCEDURE', + 'SHOW', + 'DATABASES', + 'KEYS', + 'TABLES', + 'COLUMNS', + 'STATUS', ]; const INDIVIDUALS: Record = { diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index b652937..b3f3d5e 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -522,6 +522,112 @@ describe('identifier', () => { }); }); + describe('identify SHOW statements', () => { + (['mysql'] as Dialect[]).forEach((dialect) => { + it(`identify "SHOW KEYS" statements for ${dialect}`, () => { + const sqlStatement = 'SHOW KEYS;'; + const actual = identify(sqlStatement); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: 'SHOW_KEYS', + executionType: 'LISTING', + parameters: [], + }, + ]; + + expect(actual).to.eql(expected); + }); + + it(`identify "SHOW DATABASES" statements for ${dialect}`, () => { + const sqlStatement = 'SHOW DATABASES;'; + const actual = identify(sqlStatement); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: 'SHOW_DATABASES', + executionType: 'LISTING', + parameters: [], + }, + ]; + + expect(actual).to.eql(expected); + }); + + it(`identify "SHOW_INDEX" statements for ${dialect}`, () => { + const sqlStatement = 'SHOW INDEX;'; + const actual = identify(sqlStatement); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: 'SHOW_INDEX', + executionType: 'LISTING', + parameters: [], + }, + ]; + + expect(actual).to.eql(expected); + }); + + it(`identify "SHOW_TABLE_STATUS" statements for ${dialect}`, () => { + const sqlStatement = 'SHOW TABLE STATUS;'; + const actual = identify(sqlStatement); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: 'SHOW_TABLE', + executionType: 'LISTING', + parameters: [], + }, + ]; + + expect(actual).to.eql(expected); + }); + + it(`identify "SHOW_TABLES" statements for ${dialect}`, () => { + const sqlStatement = 'SHOW TABLES;'; + const actual = identify(sqlStatement); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: 'SHOW_TABLES', + executionType: 'LISTING', + parameters: [], + }, + ]; + + expect(actual).to.eql(expected); + }); + + it(`identify "SHOW_COLUMNS" statements for ${dialect}`, () => { + const sqlStatement = 'SHOW COLUMNS;'; + const actual = identify(sqlStatement); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: 'SHOW_COLUMNS', + executionType: 'LISTING', + parameters: [], + }, + ]; + + expect(actual).to.eql(expected); + }); + }); + }); + describe('identify "CREATE FUNCTION" statements', () => { it('should identify postgres "CREATE FUNCTION" statement with LANGUAGE at end', () => { const sql = `CREATE FUNCTION quarterly_summary_func(start_date date DEFAULT CURRENT_TIMESTAMP) From 8c91e721857bf4ecb2cc2602af42c36b9bfd28e4 Mon Sep 17 00:00:00 2001 From: Will Montgomery Date: Sun, 8 Jan 2023 13:48:25 -0500 Subject: [PATCH 2/8] docs(readme): forgot to inclue SHOW DATABASES to the lsit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02ad15c..b011909 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ This way you have sure is a valid query before trying to identify the types. * CREATE_FUNCTION * CREATE_INDEX * CREATE_PROCEDURE -* SHOW_DATABASES +* SHOW_DATABASES (Mysql dialects only) * SHOW_KEYS (Mysql dialects only) * SHOW_INDEX (Mysql dialects only) * SHOW_TABLE (Mysql dialects only) (Refers to SHOW TABLE STATUS) From 869e43601335c6e1a6a61db423469123ac5bc6e1 Mon Sep 17 00:00:00 2001 From: Will Montgomery Date: Sun, 15 Jan 2023 13:28:39 -0500 Subject: [PATCH 3/8] feat: change the "show" parser to INFORMATION and add INFORATION to the interface --- README.md | 1 + src/defines.ts | 2 +- src/parser.ts | 12 ++++++------ test/identifier/single-statement.spec.ts | 12 ++++++------ 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b011909..3d66d15 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Execution types allow to know what is the query behavior * `LISTING:` is when the query list the data * `MODIFICATION:` is when the query modificate the database somehow (structure or data) +* `INFORMATION:` is show some data information such as a profile data * `ANON_BLOCK: ` is for an anonymous block query which may contain multiple statements of unknown type (BigQuery and Oracle dialects only) * `UNKNOWN`: (only available if strict mode is disabled) diff --git a/src/defines.ts b/src/defines.ts index 90aa5f4..f26cf19 100644 --- a/src/defines.ts +++ b/src/defines.ts @@ -47,7 +47,7 @@ export type StatementType = | 'ANON_BLOCK' | 'UNKNOWN'; -export type ExecutionType = 'LISTING' | 'MODIFICATION' | 'ANON_BLOCK' | 'UNKNOWN'; +export type ExecutionType = 'LISTING' | 'MODIFICATION' | 'INFORMATION' | 'ANON_BLOCK' | 'UNKNOWN'; export interface IdentifyOptions { strict?: boolean; diff --git a/src/parser.ts b/src/parser.ts index 4654406..bc23f9d 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -36,12 +36,12 @@ export const EXECUTION_TYPES: Record = { CREATE_FUNCTION: 'MODIFICATION', CREATE_INDEX: 'MODIFICATION', CREATE_PROCEDURE: 'MODIFICATION', - SHOW_DATABASES: 'LISTING', - SHOW_KEYS: 'LISTING', - SHOW_INDEX: 'LISTING', - SHOW_TABLE: 'LISTING', // for SHOW TABLE STATUS - SHOW_TABLES: 'LISTING', - SHOW_COLUMNS: 'LISTING', + SHOW_DATABASES: 'INFORMATION', + SHOW_KEYS: 'INFORMATION', + SHOW_INDEX: 'INFORMATION', + SHOW_TABLE: 'INFORMATION', // for SHOW TABLE STATUS + SHOW_TABLES: 'INFORMATION', + SHOW_COLUMNS: 'INFORMATION', DROP_DATABASE: 'MODIFICATION', DROP_SCHEMA: 'MODIFICATION', DROP_TABLE: 'MODIFICATION', diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index b3f3d5e..b92829a 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -533,7 +533,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: 'SHOW_KEYS', - executionType: 'LISTING', + executionType: 'INFORMATION', parameters: [], }, ]; @@ -550,7 +550,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: 'SHOW_DATABASES', - executionType: 'LISTING', + executionType: 'INFORMATION', parameters: [], }, ]; @@ -567,7 +567,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: 'SHOW_INDEX', - executionType: 'LISTING', + executionType: 'INFORMATION', parameters: [], }, ]; @@ -584,7 +584,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: 'SHOW_TABLE', - executionType: 'LISTING', + executionType: 'INFORMATION', parameters: [], }, ]; @@ -601,7 +601,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: 'SHOW_TABLES', - executionType: 'LISTING', + executionType: 'INFORMATION', parameters: [], }, ]; @@ -618,7 +618,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: 'SHOW_COLUMNS', - executionType: 'LISTING', + executionType: 'INFORMATION', parameters: [], }, ]; From 9256dff944d4d40de017391b0502364dc0f10946 Mon Sep 17 00:00:00 2001 From: Will Montgomery Date: Sat, 21 Jan 2023 11:07:53 -0500 Subject: [PATCH 4/8] feat: ensure select only works for mysql or generic --- src/parser.ts | 6 +++- test/identifier/single-statement.spec.ts | 45 ++++++++++++++++++++---- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index bc23f9d..b2bb57c 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -271,7 +271,11 @@ function createStatementParserByToken( case 'CREATE': return createCreateStatementParser(options); case 'SHOW': - return createShowStatementParser(options); + console.log(options.dialect); + if (['mysql', 'generic'].includes(options.dialect)) { + return createShowStatementParser(options); + } + break; case 'DROP': return createDropStatementParser(options); case 'ALTER': diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index b92829a..2cd4a63 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -523,10 +523,16 @@ describe('identifier', () => { }); describe('identify SHOW statements', () => { - (['mysql'] as Dialect[]).forEach((dialect) => { + (['mysql', 'generic', 'mssql'] as Dialect[]).forEach((dialect) => { it(`identify "SHOW KEYS" statements for ${dialect}`, () => { const sqlStatement = 'SHOW KEYS;'; - const actual = identify(sqlStatement); + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + + const actual = identify(sqlStatement, { dialect }); const expected = [ { start: 0, @@ -543,7 +549,12 @@ describe('identifier', () => { it(`identify "SHOW DATABASES" statements for ${dialect}`, () => { const sqlStatement = 'SHOW DATABASES;'; - const actual = identify(sqlStatement); + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + const actual = identify(sqlStatement, { dialect }); const expected = [ { start: 0, @@ -560,7 +571,12 @@ describe('identifier', () => { it(`identify "SHOW_INDEX" statements for ${dialect}`, () => { const sqlStatement = 'SHOW INDEX;'; - const actual = identify(sqlStatement); + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + const actual = identify(sqlStatement, { dialect }); const expected = [ { start: 0, @@ -577,7 +593,12 @@ describe('identifier', () => { it(`identify "SHOW_TABLE_STATUS" statements for ${dialect}`, () => { const sqlStatement = 'SHOW TABLE STATUS;'; - const actual = identify(sqlStatement); + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + const actual = identify(sqlStatement, { dialect }); const expected = [ { start: 0, @@ -594,7 +615,12 @@ describe('identifier', () => { it(`identify "SHOW_TABLES" statements for ${dialect}`, () => { const sqlStatement = 'SHOW TABLES;'; - const actual = identify(sqlStatement); + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + const actual = identify(sqlStatement, { dialect }); const expected = [ { start: 0, @@ -611,7 +637,12 @@ describe('identifier', () => { it(`identify "SHOW_COLUMNS" statements for ${dialect}`, () => { const sqlStatement = 'SHOW COLUMNS;'; - const actual = identify(sqlStatement); + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + const actual = identify(sqlStatement, { dialect }); const expected = [ { start: 0, From 446903359938c7394436d593a8040ed07060d2d0 Mon Sep 17 00:00:00 2001 From: Will Montgomery Date: Sat, 21 Jan 2023 11:19:12 -0500 Subject: [PATCH 5/8] style: remove a console.log and add back a documentation line --- src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.ts b/src/parser.ts index b2bb57c..037d39a 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -20,6 +20,7 @@ interface StatementParser { * Execution types allow to know what is the query behavior * - LISTING: is when the query list the data * - MODIFICATION: is when the query modificate the database somehow (structure or data) + * - INFORMATION: is show some data information such as a profile data * - UNKNOWN */ export const EXECUTION_TYPES: Record = { @@ -271,7 +272,6 @@ function createStatementParserByToken( case 'CREATE': return createCreateStatementParser(options); case 'SHOW': - console.log(options.dialect); if (['mysql', 'generic'].includes(options.dialect)) { return createShowStatementParser(options); } From 57d8516881922d9131082ef597a4201089408a3b Mon Sep 17 00:00:00 2001 From: Will Montgomery Date: Mon, 23 Jan 2023 21:59:44 -0500 Subject: [PATCH 6/8] feat: add the show statements for mysql and generic had to add Profile as a keyword as part of one of the tests as it is a keyword now --- README.md | 39 ++++- src/defines.ts | 27 ++++ src/parser.ts | 55 +++++++ src/tokenizer.ts | 23 +++ test/identifier/single-statement.spec.ts | 185 +++++++---------------- test/parser/single-statements.spec.ts | 20 ++- 6 files changed, 209 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 3d66d15..d2fd110 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,6 @@ This way you have sure is a valid query before trying to identify the types. * CREATE_FUNCTION * CREATE_INDEX * CREATE_PROCEDURE -* SHOW_DATABASES (Mysql dialects only) -* SHOW_KEYS (Mysql dialects only) -* SHOW_INDEX (Mysql dialects only) -* SHOW_TABLE (Mysql dialects only) (Refers to SHOW TABLE STATUS) -* SHOW_TABLES (Mysql dialects only) -* SHOW_COLUMNS (Mysql dialects only) * DROP_DATABASE * DROP_SCHEMA * DROP_TABLE @@ -66,6 +60,39 @@ This way you have sure is a valid query before trying to identify the types. * ALTER_PROCEDURE * ANON_BLOCK (BigQuery and Oracle dialects only) * UNKNOWN (only available if strict mode is disabled) +* (For the following, please refer to the [MySQL Docs about SHOW Statements](https://dev.mysql.com/doc/refman/8.0/en/show.html)) +* SHOW_BINARY (MySQL and generic dialects only) +* SHOW_BINLOG (MySQL and generic dialects only) +* SHOW_CHARACTER (MySQL and generic dialects only) +* SHOW_COLLATION (MySQL and generic dialects only) +* SHOW_COLUMNS (MySQL and generic dialects only) +* SHOW_CREATE (MySQL and generic dialects only) +* SHOW_DATABASES (MySQL and generic dialects only) +* SHOW_ENGINE (MySQL and generic dialects only) +* SHOW_ENGINES (MySQL and generic dialects only) +* SHOW_ERRORS (MySQL and generic dialects only) +* SHOW_EVENTS (MySQL and generic dialects only) +* SHOW_FUNCTION (MySQL and generic dialects only) +* SHOW_GRANTS (MySQL and generic dialects only) +* SHOW_INDEX (MySQL and generic dialects only) +* SHOW_MASTER (MySQL and generic dialects only) +* SHOW_OPEN (MySQL and generic dialects only) +* SHOW_PLUGINS (MySQL and generic dialects only) +* SHOW_PRIVILEGES (MySQL and generic dialects only) +* SHOW_PROCEDURE (MySQL and generic dialects only) +* SHOW_PROCESSLIST (MySQL and generic dialects only) +* SHOW_PROFILE (MySQL and generic dialects only) +* SHOW_PROFILES (MySQL and generic dialects only) +* SHOW_RELAYLOG (MySQL and generic dialects only) +* SHOW_REPLICAS (MySQL and generic dialects only) +* SHOW_SLAVE (MySQL and generic dialects only) +* SHOW_REPLICA (MySQL and generic dialects only) +* SHOW_STATUS (MySQL and generic dialects only) +* SHOW_TABLE (MySQL and generic dialects only) +* SHOW_TABLES (MySQL and generic dialects only) +* SHOW_TRIGGERS (MySQL and generic dialects only) +* SHOW_VARIABLES (MySQL and generic dialects only) +* SHOW_WARNINGS (MySQL and generic dialects only) ## Execution types diff --git a/src/defines.ts b/src/defines.ts index f26cf19..66e4dd3 100644 --- a/src/defines.ts +++ b/src/defines.ts @@ -22,6 +22,33 @@ export type StatementType = | 'CREATE_FUNCTION' | 'CREATE_INDEX' | 'CREATE_PROCEDURE' + | 'SHOW_BINARY' + | 'SHOW_BINLOG' + | 'SHOW_CHARACTER' + | 'SHOW_COLLATION' + | 'SHOW_CREATE' + | 'SHOW_ENGINE' + | 'SHOW_ENGINES' + | 'SHOW_ERRORS' + | 'SHOW_EVENTS' + | 'SHOW_FUNCTION' + | 'SHOW_GRANTS' + | 'SHOW_MASTER' + | 'SHOW_OPEN' + | 'SHOW_PLUGINS' + | 'SHOW_PRIVILEGES' + | 'SHOW_PROCEDURE' + | 'SHOW_PROCESSLIST' + | 'SHOW_PROFILE' + | 'SHOW_PROFILES' + | 'SHOW_RELAYLOG' + | 'SHOW_REPLICAS' + | 'SHOW_SLAVE' + | 'SHOW_REPLICA' + | 'SHOW_STATUS' + | 'SHOW_TRIGGERS' + | 'SHOW_VARIABLES' + | 'SHOW_WARNINGS' | 'SHOW_DATABASES' | 'SHOW_KEYS' | 'SHOW_INDEX' diff --git a/src/parser.ts b/src/parser.ts index 037d39a..b79f411 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -37,6 +37,33 @@ export const EXECUTION_TYPES: Record = { CREATE_FUNCTION: 'MODIFICATION', CREATE_INDEX: 'MODIFICATION', CREATE_PROCEDURE: 'MODIFICATION', + SHOW_BINARY: 'INFORMATION', + SHOW_BINLOG: 'INFORMATION', + SHOW_CHARACTER: 'INFORMATION', + SHOW_COLLATION: 'INFORMATION', + SHOW_CREATE: 'INFORMATION', + SHOW_ENGINE: 'INFORMATION', + SHOW_ENGINES: 'INFORMATION', + SHOW_ERRORS: 'INFORMATION', + SHOW_EVENTS: 'INFORMATION', + SHOW_FUNCTION: 'INFORMATION', + SHOW_GRANTS: 'INFORMATION', + SHOW_MASTER: 'INFORMATION', + SHOW_OPEN: 'INFORMATION', + SHOW_PLUGINS: 'INFORMATION', + SHOW_PRIVILEGES: 'INFORMATION', + SHOW_PROCEDURE: 'INFORMATION', + SHOW_PROCESSLIST: 'INFORMATION', + SHOW_PROFILE: 'INFORMATION', + SHOW_PROFILES: 'INFORMATION', + SHOW_RELAYLOG: 'INFORMATION', + SHOW_REPLICAS: 'INFORMATION', + SHOW_SLAVE: 'INFORMATION', + SHOW_REPLICA: 'INFORMATION', + SHOW_STATUS: 'INFORMATION', + SHOW_TRIGGERS: 'INFORMATION', + SHOW_VARIABLES: 'INFORMATION', + SHOW_WARNINGS: 'INFORMATION', SHOW_DATABASES: 'INFORMATION', SHOW_KEYS: 'INFORMATION', SHOW_INDEX: 'INFORMATION', @@ -614,11 +641,39 @@ function createShowStatementParser(options: ParseOptions) { requireBefore: ['whitespace'], acceptTokens: [ { type: 'keyword', value: 'DATABASES' }, + { type: 'keyword', value: 'DATABASE' }, { type: 'keyword', value: 'KEYS' }, { type: 'keyword', value: 'INDEX' }, { type: 'keyword', value: 'COLUMNS' }, { type: 'keyword', value: 'TABLES' }, { type: 'keyword', value: 'TABLE' }, + { type: 'keyword', value: 'BINARY' }, + { type: 'keyword', value: 'BINLOG' }, + { type: 'keyword', value: 'CHARACTER' }, + { type: 'keyword', value: 'COLLATION' }, + { type: 'keyword', value: 'CREATE' }, + { type: 'keyword', value: 'ENGINE' }, + { type: 'keyword', value: 'ENGINES' }, + { type: 'keyword', value: 'ERRORS' }, + { type: 'keyword', value: 'EVENTS' }, + { type: 'keyword', value: 'FUNCTION' }, + { type: 'keyword', value: 'GRANTS' }, + { type: 'keyword', value: 'MASTER' }, + { type: 'keyword', value: 'OPEN' }, + { type: 'keyword', value: 'PLUGINS' }, + { type: 'keyword', value: 'PRIVILEGES' }, + { type: 'keyword', value: 'PROCEDURE' }, + { type: 'keyword', value: 'PROCESSLIST' }, + { type: 'keyword', value: 'PROFILE' }, + { type: 'keyword', value: 'PROFILES' }, + { type: 'keyword', value: 'RELAYLOG' }, + { type: 'keyword', value: 'REPLICAS' }, + { type: 'keyword', value: 'REPLICA' }, + { type: 'keyword', value: 'SLAVE' }, + { type: 'keyword', value: 'STATUS' }, + { type: 'keyword', value: 'TRIGGERS' }, + { type: 'keyword', value: 'VARIABLES' }, + { type: 'keyword', value: 'WARNINGS' }, ], }, add: (token) => { diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 890728e..bc3b10d 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -40,6 +40,29 @@ const KEYWORDS = [ 'TABLES', 'COLUMNS', 'STATUS', + 'BINARY', + 'BINLOG', + 'CHARACTER', + 'COLLATION', + 'ENGINE', + 'ENGINES', + 'ERRORS', + 'EVENTS', + 'GRANTS', + 'MASTER', + 'OPEN', + 'PLUGINS', + 'PRIVILEGES', + 'PROCESSLIST', + 'PROFILE', + 'PROFILES', + 'RELAYLOG', + 'REPLICAS', + 'SLAVE', + 'REPLICA', + 'TRIGGERS', + 'VARIABLES', + 'WARNINGS', ]; const INDIVIDUALS: Record = { diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index 2cd4a63..165d82a 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -524,137 +524,62 @@ describe('identifier', () => { describe('identify SHOW statements', () => { (['mysql', 'generic', 'mssql'] as Dialect[]).forEach((dialect) => { - it(`identify "SHOW KEYS" statements for ${dialect}`, () => { - const sqlStatement = 'SHOW KEYS;'; - const testFunction = () => identify(sqlStatement, { dialect }); - if (dialect === 'mssql') { - expect(testFunction).to.throw('Invalid statement parser "SHOW"'); - return; - } - - const actual = identify(sqlStatement, { dialect }); - const expected = [ - { - start: 0, - end: sqlStatement.length - 1, - text: sqlStatement, - type: 'SHOW_KEYS', - executionType: 'INFORMATION', - parameters: [], - }, - ]; - - expect(actual).to.eql(expected); - }); - - it(`identify "SHOW DATABASES" statements for ${dialect}`, () => { - const sqlStatement = 'SHOW DATABASES;'; - const testFunction = () => identify(sqlStatement, { dialect }); - if (dialect === 'mssql') { - expect(testFunction).to.throw('Invalid statement parser "SHOW"'); - return; - } - const actual = identify(sqlStatement, { dialect }); - const expected = [ - { - start: 0, - end: sqlStatement.length - 1, - text: sqlStatement, - type: 'SHOW_DATABASES', - executionType: 'INFORMATION', - parameters: [], - }, - ]; - - expect(actual).to.eql(expected); - }); - - it(`identify "SHOW_INDEX" statements for ${dialect}`, () => { - const sqlStatement = 'SHOW INDEX;'; - const testFunction = () => identify(sqlStatement, { dialect }); - if (dialect === 'mssql') { - expect(testFunction).to.throw('Invalid statement parser "SHOW"'); - return; - } - const actual = identify(sqlStatement, { dialect }); - const expected = [ - { - start: 0, - end: sqlStatement.length - 1, - text: sqlStatement, - type: 'SHOW_INDEX', - executionType: 'INFORMATION', - parameters: [], - }, - ]; - - expect(actual).to.eql(expected); - }); - - it(`identify "SHOW_TABLE_STATUS" statements for ${dialect}`, () => { - const sqlStatement = 'SHOW TABLE STATUS;'; - const testFunction = () => identify(sqlStatement, { dialect }); - if (dialect === 'mssql') { - expect(testFunction).to.throw('Invalid statement parser "SHOW"'); - return; - } - const actual = identify(sqlStatement, { dialect }); - const expected = [ - { - start: 0, - end: sqlStatement.length - 1, - text: sqlStatement, - type: 'SHOW_TABLE', - executionType: 'INFORMATION', - parameters: [], - }, - ]; - - expect(actual).to.eql(expected); - }); - - it(`identify "SHOW_TABLES" statements for ${dialect}`, () => { - const sqlStatement = 'SHOW TABLES;'; - const testFunction = () => identify(sqlStatement, { dialect }); - if (dialect === 'mssql') { - expect(testFunction).to.throw('Invalid statement parser "SHOW"'); - return; - } - const actual = identify(sqlStatement, { dialect }); - const expected = [ - { - start: 0, - end: sqlStatement.length - 1, - text: sqlStatement, - type: 'SHOW_TABLES', - executionType: 'INFORMATION', - parameters: [], - }, - ]; - - expect(actual).to.eql(expected); - }); - - it(`identify "SHOW_COLUMNS" statements for ${dialect}`, () => { - const sqlStatement = 'SHOW COLUMNS;'; - const testFunction = () => identify(sqlStatement, { dialect }); - if (dialect === 'mssql') { - expect(testFunction).to.throw('Invalid statement parser "SHOW"'); - return; - } - const actual = identify(sqlStatement, { dialect }); - const expected = [ - { - start: 0, - end: sqlStatement.length - 1, - text: sqlStatement, - type: 'SHOW_COLUMNS', - executionType: 'INFORMATION', - parameters: [], - }, - ]; + [ + ['BINARY', "SHOW BINARY LOGS 'blerns';"], + ['BINLOG', "SHOW BINLOG EVENTS 'blerns';"], + ['CHARACTER', "SHOW CHARACTER SET 'blerns';"], + ['COLLATION', "SHOW COLLATION 'blerns';"], + ['COLUMNS', "SHOW COLUMNS 'blerns';"], + ['CREATE', "SHOW CREATE DATABASE 'blerns';"], + ['DATABASES', "SHOW DATABASES 'blerns';"], + ['ENGINE', "SHOW ENGINE 'blerns';"], + ['ENGINES', "SHOW ENGINES 'blerns';"], + ['ERRORS', "SHOW ERRORS 'blerns';"], + ['EVENTS', "SHOW EVENTS 'blerns';"], + ['FUNCTION', "SHOW FUNCTION CODE 'blerns';"], + ['GRANTS', "SHOW GRANTS 'blerns';"], + ['INDEX', "SHOW INDEX 'blerns';"], + ['MASTER', "SHOW MASTER STATUS 'blerns';"], + ['OPEN', "SHOW OPEN TABLES 'blerns';"], + ['PLUGINS', "SHOW PLUGINS 'blerns';"], + ['PRIVILEGES', "SHOW PRIVILEGES 'blerns';"], + ['PROCEDURE', "SHOW PROCEDURE CODE 'blerns';"], + ['PROCESSLIST', "SHOW PROCESSLIST 'blerns';"], + ['PROFILE', "SHOW PROFILE 'blerns';"], + ['PROFILES', "SHOW PROFILES 'blerns';"], + ['RELAYLOG', "SHOW RELAYLOG EVENTS 'blerns';"], + ['REPLICAS', "SHOW REPLICAS 'blerns';"], + ['SLAVE', 'SHOW SLAVE HOSTS;'], + ['REPLICA', "SHOW REPLICA STATUS 'blerns';"], + ['STATUS', "SHOW STATUS 'blerns';"], + ['TABLE', "SHOW TABLE STATUS 'blerns';"], + ['TABLES', "SHOW TABLES 'blerns';"], + ['TRIGGERS', "SHOW TRIGGERS 'blerns';"], + ['VARIABLES', "SHOW VARIABLES 'blerns';"], + ['WARNINGS', "SHOW WARNINGS 'blerns';"], + ].forEach(([type, sql]) => { + it(`identify "SHOW ${type}" statements for ${dialect}`, () => { + const sqlStatement = sql; + const testFunction = () => identify(sqlStatement, { dialect }); + if (dialect === 'mssql') { + expect(testFunction).to.throw('Invalid statement parser "SHOW"'); + return; + } + + const actual = identify(sqlStatement, { dialect }); + const expected = [ + { + start: 0, + end: sqlStatement.length - 1, + text: sqlStatement, + type: `SHOW_${type}`, + executionType: 'INFORMATION', + parameters: [], + }, + ]; - expect(actual).to.eql(expected); + expect(actual).to.eql(expected); + }); }); }); }); diff --git a/test/parser/single-statements.spec.ts b/test/parser/single-statements.spec.ts index df23ac6..9e3f9e4 100644 --- a/test/parser/single-statements.spec.ts +++ b/test/parser/single-statements.spec.ts @@ -226,9 +226,15 @@ describe('parser', () => { end: 14, }, { - type: 'unknown', - value: ' Profile', + type: 'whitespace', + value: ' ', start: 15, + end: 15, + }, + { + type: 'keyword', + value: 'Profile', + start: 16, end: 22, }, { @@ -338,9 +344,15 @@ describe('parser', () => { end: 12, }, { - type: 'unknown', - value: ' Profile', + type: 'whitespace', + value: ' ', start: 13, + end: 13, + }, + { + type: 'keyword', + value: 'Profile', + start: 14, end: 20, }, { From a12e3e0ada6c970aaed8fb1c6e7603489561f5e3 Mon Sep 17 00:00:00 2001 From: Matthew Peveler Date: Mon, 6 Feb 2023 00:23:50 -0500 Subject: [PATCH 7/8] move back to listing --- src/parser.ts | 66 ++++++++++++------------ test/identifier/single-statement.spec.ts | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index b79f411..48ea882 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -37,39 +37,39 @@ export const EXECUTION_TYPES: Record = { CREATE_FUNCTION: 'MODIFICATION', CREATE_INDEX: 'MODIFICATION', CREATE_PROCEDURE: 'MODIFICATION', - SHOW_BINARY: 'INFORMATION', - SHOW_BINLOG: 'INFORMATION', - SHOW_CHARACTER: 'INFORMATION', - SHOW_COLLATION: 'INFORMATION', - SHOW_CREATE: 'INFORMATION', - SHOW_ENGINE: 'INFORMATION', - SHOW_ENGINES: 'INFORMATION', - SHOW_ERRORS: 'INFORMATION', - SHOW_EVENTS: 'INFORMATION', - SHOW_FUNCTION: 'INFORMATION', - SHOW_GRANTS: 'INFORMATION', - SHOW_MASTER: 'INFORMATION', - SHOW_OPEN: 'INFORMATION', - SHOW_PLUGINS: 'INFORMATION', - SHOW_PRIVILEGES: 'INFORMATION', - SHOW_PROCEDURE: 'INFORMATION', - SHOW_PROCESSLIST: 'INFORMATION', - SHOW_PROFILE: 'INFORMATION', - SHOW_PROFILES: 'INFORMATION', - SHOW_RELAYLOG: 'INFORMATION', - SHOW_REPLICAS: 'INFORMATION', - SHOW_SLAVE: 'INFORMATION', - SHOW_REPLICA: 'INFORMATION', - SHOW_STATUS: 'INFORMATION', - SHOW_TRIGGERS: 'INFORMATION', - SHOW_VARIABLES: 'INFORMATION', - SHOW_WARNINGS: 'INFORMATION', - SHOW_DATABASES: 'INFORMATION', - SHOW_KEYS: 'INFORMATION', - SHOW_INDEX: 'INFORMATION', - SHOW_TABLE: 'INFORMATION', // for SHOW TABLE STATUS - SHOW_TABLES: 'INFORMATION', - SHOW_COLUMNS: 'INFORMATION', + SHOW_BINARY: 'LISTING', + SHOW_BINLOG: 'LISTING', + SHOW_CHARACTER: 'LISTING', + SHOW_COLLATION: 'LISTING', + SHOW_CREATE: 'LISTING', + SHOW_ENGINE: 'LISTING', + SHOW_ENGINES: 'LISTING', + SHOW_ERRORS: 'LISTING', + SHOW_EVENTS: 'LISTING', + SHOW_FUNCTION: 'LISTING', + SHOW_GRANTS: 'LISTING', + SHOW_MASTER: 'LISTING', + SHOW_OPEN: 'LISTING', + SHOW_PLUGINS: 'LISTING', + SHOW_PRIVILEGES: 'LISTING', + SHOW_PROCEDURE: 'LISTING', + SHOW_PROCESSLIST: 'LISTING', + SHOW_PROFILE: 'LISTING', + SHOW_PROFILES: 'LISTING', + SHOW_RELAYLOG: 'LISTING', + SHOW_REPLICAS: 'LISTING', + SHOW_SLAVE: 'LISTING', + SHOW_REPLICA: 'LISTING', + SHOW_STATUS: 'LISTING', + SHOW_TRIGGERS: 'LISTING', + SHOW_VARIABLES: 'LISTING', + SHOW_WARNINGS: 'LISTING', + SHOW_DATABASES: 'LISTING', + SHOW_KEYS: 'LISTING', + SHOW_INDEX: 'LISTING', + SHOW_TABLE: 'LISTING', // for SHOW TABLE STATUS + SHOW_TABLES: 'LISTING', + SHOW_COLUMNS: 'LISTING', DROP_DATABASE: 'MODIFICATION', DROP_SCHEMA: 'MODIFICATION', DROP_TABLE: 'MODIFICATION', diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index 165d82a..71f7fe9 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -573,7 +573,7 @@ describe('identifier', () => { end: sqlStatement.length - 1, text: sqlStatement, type: `SHOW_${type}`, - executionType: 'INFORMATION', + executionType: 'LISTING', parameters: [], }, ]; From bf3c59656f217b8ac17fe130c8895f67067501d3 Mon Sep 17 00:00:00 2001 From: Matthew Peveler Date: Mon, 6 Feb 2023 00:25:28 -0500 Subject: [PATCH 8/8] update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2fd110..6fd1486 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ This way you have sure is a valid query before trying to identify the types. ## Current Available Types +For the show statements, please refer to the [MySQL Docs about SHOW Statements](https://dev.mysql.com/doc/refman/8.0/en/show.html). + * INSERT * UPDATE * DELETE @@ -59,8 +61,6 @@ This way you have sure is a valid query before trying to identify the types. * ALTER_INDEX * ALTER_PROCEDURE * ANON_BLOCK (BigQuery and Oracle dialects only) -* UNKNOWN (only available if strict mode is disabled) -* (For the following, please refer to the [MySQL Docs about SHOW Statements](https://dev.mysql.com/doc/refman/8.0/en/show.html)) * SHOW_BINARY (MySQL and generic dialects only) * SHOW_BINLOG (MySQL and generic dialects only) * SHOW_CHARACTER (MySQL and generic dialects only) @@ -93,6 +93,7 @@ This way you have sure is a valid query before trying to identify the types. * SHOW_TRIGGERS (MySQL and generic dialects only) * SHOW_VARIABLES (MySQL and generic dialects only) * SHOW_WARNINGS (MySQL and generic dialects only) +* UNKNOWN (only available if strict mode is disabled) ## Execution types