diff --git a/package.json b/package.json index 913acf7..11a3348 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-query-identifier", - "version": "2.6.0", + "version": "2.7.0", "description": "A SQL query identifier", "license": "MIT", "main": "lib/index.js", @@ -34,12 +34,11 @@ "prettier": "^2.3.2", "terser-webpack-plugin": "^5.1.1", "ts-loader": "^8.0.17", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "typescript": "^4.1.5", "webpack": "^5.11.1", "webpack-cli": "^4.3.1" }, - "dependencies": {}, "engines": { "node": ">= 10.13" } diff --git a/src/defines.ts b/src/defines.ts index c797224..cfea14b 100644 --- a/src/defines.ts +++ b/src/defines.ts @@ -79,6 +79,7 @@ export type ExecutionType = 'LISTING' | 'MODIFICATION' | 'INFORMATION' | 'ANON_B export interface IdentifyOptions { strict?: boolean; dialect?: Dialect; + identifyTables?: boolean; enableCrossDBParameters?: boolean; } @@ -89,6 +90,7 @@ export interface IdentifyResult { type: StatementType; executionType: ExecutionType; parameters: string[]; + tables: string[]; } export interface Statement { @@ -102,6 +104,8 @@ export interface Statement { algorithm?: number; sqlSecurity?: number; parameters: string[]; + tables: string[]; + isCte?: boolean; } export interface ConcreteStatement extends Statement { @@ -125,6 +129,7 @@ export interface Token { | 'semicolon' | 'keyword' | 'parameter' + | 'table' | 'unknown'; value: string; start: number; diff --git a/src/index.ts b/src/index.ts index e5763c9..512aaa1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,16 +16,18 @@ export type { export function identify(query: string, options: IdentifyOptions = {}): IdentifyResult[] { const isStrict = typeof options.strict === 'undefined' ? true : options.strict === true; const dialect = typeof options.dialect === 'undefined' ? 'generic' : options.dialect; - const enableCrossDBParameters = - typeof options.enableCrossDBParameters === 'undefined' - ? false - : options.enableCrossDBParameters; if (!DIALECTS.includes(dialect)) { throw new Error(`Unknown dialect. Allowed values: ${DIALECTS.join(', ')}`); } - const result = parse(query, isStrict, dialect, enableCrossDBParameters); + const result = parse( + query, + isStrict, + dialect, + options.identifyTables, + options.enableCrossDBParameters, + ); return result.body.map((statement) => { const result: IdentifyResult = { @@ -36,6 +38,7 @@ export function identify(query: string, options: IdentifyOptions = {}): Identify executionType: statement.executionType, // we want to sort the postgres params: $1 $2 $3, regardless of the order they appear parameters: dialect === 'psql' ? statement.parameters.sort() : statement.parameters, + tables: statement.tables || [], }; return result; }); diff --git a/src/parser.ts b/src/parser.ts index e9fa8aa..e2c54d8 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -98,6 +98,10 @@ const statementsWithEnds = [ 'UNKNOWN', ]; +// keywords that come directly before a table name. +// v1 - keeping it very simple. +const PRE_TABLE_KEYWORDS = /^from$|^join$|^into$/i; + const blockOpeners: Record = { generic: ['BEGIN', 'CASE'], psql: ['BEGIN', 'CASE', 'LOOP', 'IF'], @@ -111,6 +115,7 @@ const blockOpeners: Record = { interface ParseOptions { isStrict: boolean; dialect: Dialect; + identifyTables: boolean; } function createInitialStatement(): Statement { @@ -118,6 +123,7 @@ function createInitialStatement(): Statement { start: -1, end: 0, parameters: [], + tables: [], }; } @@ -141,6 +147,7 @@ export function parse( input: string, isStrict = true, dialect: Dialect = 'generic', + identifyTables = false, enableCrossDBParameters = false, ): ParseResult { const topLevelState = initState({ input }); @@ -180,7 +187,6 @@ export function parse( if (!cteState.isCte && ignoreOutsideBlankTokens.includes(token.type)) { topLevelStatement.tokens.push(token); prevState = tokenState; - continue; } else if ( !cteState.isCte && token.type === 'keyword' && @@ -190,7 +196,7 @@ export function parse( topLevelStatement.tokens.push(token); cteState.state = tokenState; prevState = tokenState; - continue; + // If we're scanning in a CTE, handle someone putting a semicolon anywhere (after 'with', // after semicolon, etc.) along it to "early terminate". } else if (cteState.isCte && token.type === 'semicolon') { @@ -202,12 +208,12 @@ export function parse( type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }); cteState.isCte = false; cteState.asSeen = false; cteState.statementEnd = false; cteState.parens = 0; - continue; } else if (cteState.isCte && !cteState.statementEnd) { if (cteState.asSeen) { if (token.value === '(') { @@ -224,14 +230,13 @@ export function parse( topLevelStatement.tokens.push(token); prevState = tokenState; - continue; } else if (cteState.isCte && cteState.statementEnd && token.value === ',') { cteState.asSeen = false; cteState.statementEnd = false; topLevelStatement.tokens.push(token); prevState = tokenState; - continue; + // Ignore blank tokens after the end of the CTE till start of statement } else if ( cteState.isCte && @@ -240,28 +245,32 @@ export function parse( ) { topLevelStatement.tokens.push(token); prevState = tokenState; - continue; + } else { + statementParser = createStatementParserByToken(token, nextToken, { + isStrict, + dialect, + identifyTables, + }); + if (cteState.isCte) { + statementParser.getStatement().start = cteState.state.start; + statementParser.getStatement().isCte = true; + cteState.isCte = false; + cteState.asSeen = false; + cteState.statementEnd = false; + } } + } else { + statementParser.addToken(token, nextToken); + topLevelStatement.tokens.push(token); + prevState = tokenState; - statementParser = createStatementParserByToken(token, nextToken, { isStrict, dialect }); - if (cteState.isCte) { - statementParser.getStatement().start = cteState.state.start; - cteState.isCte = false; - cteState.asSeen = false; - cteState.statementEnd = false; + const statement = statementParser.getStatement(); + if (statement.endStatement) { + statement.end = token.end; + topLevelStatement.body.push(statement as ConcreteStatement); + statementParser = null; } } - - statementParser.addToken(token, nextToken); - topLevelStatement.tokens.push(token); - prevState = tokenState; - - const statement = statementParser.getStatement(); - if (statement.endStatement) { - statement.end = token.end; - topLevelStatement.body.push(statement as ConcreteStatement); - statementParser = null; - } } // last statement without ending key @@ -717,7 +726,7 @@ function createUnknownStatementParser(options: ParseOptions) { function stateMachineStatementParser( statement: Statement, steps: Step[], - { isStrict, dialect }: ParseOptions, + { isStrict, dialect, identifyTables }: ParseOptions, ): StatementParser { let currentStepIndex = 0; let prevToken: Token | undefined; @@ -817,6 +826,18 @@ function stateMachineStatementParser( } } + if ( + identifyTables && + PRE_TABLE_KEYWORDS.exec(token.value) && + !statement.isCte && + statement.type?.match(/SELECT|INSERT/) + ) { + const tableValue = nextToken.value; + if (!statement.tables.includes(tableValue)) { + statement.tables.push(tableValue); + } + } + if ( token.type === 'parameter' && (token.value === '?' || !statement.parameters.includes(token.value)) diff --git a/test/identifier/inner-statements.spec.ts b/test/identifier/inner-statements.spec.ts index e7ca454..cbbc98b 100644 --- a/test/identifier/inner-statements.spec.ts +++ b/test/identifier/inner-statements.spec.ts @@ -16,6 +16,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -34,6 +35,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -54,6 +56,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -75,6 +78,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; diff --git a/test/identifier/multiple-statement.spec.ts b/test/identifier/multiple-statement.spec.ts index fdb6fb2..4e2c003 100644 --- a/test/identifier/multiple-statement.spec.ts +++ b/test/identifier/multiple-statement.spec.ts @@ -16,6 +16,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, { end: 76, @@ -24,6 +25,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -44,6 +46,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, { start: 74, @@ -52,6 +55,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -75,6 +79,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, { start: 35, @@ -83,6 +88,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -105,6 +111,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, { start: 20, @@ -113,6 +120,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, { start: 50, @@ -121,6 +129,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -161,6 +170,7 @@ describe('identifier', () => { end: 1043, executionType: 'ANON_BLOCK', parameters: [], + tables: [], start: 11, text: 'DECLARE\n PK_NAME VARCHAR(200);\n\n BEGIN\n EXECUTE IMMEDIATE (\'CREATE SEQUENCE "untitled_table8_seq"\');\n\n SELECT\n cols.column_name INTO PK_NAME\n FROM\n all_constraints cons,\n all_cons_columns cols\n WHERE\n cons.constraint_type = \'P\'\n AND cons.constraint_name = cols.constraint_name\n AND cons.owner = cols.owner\n AND cols.table_name = \'untitled_table8\';\n\n execute immediate (\n \'create or replace trigger "untitled_table8_autoinc_trg" BEFORE INSERT on "untitled_table8" for each row declare checking number := 1; begin if (:new."\' || PK_NAME || \'" is null) then while checking >= 1 loop select "untitled_table8_seq".nextval into :new."\' || PK_NAME || \'" from dual; select count("\' || PK_NAME || \'") into checking from "untitled_table8" where "\' || PK_NAME || \'" = :new."\' || PK_NAME || \'"; end loop; end if; end;\'\n );\n\n END;', type: 'ANON_BLOCK', @@ -208,6 +218,7 @@ describe('identifier', () => { end: 167, executionType: 'MODIFICATION', parameters: [], + tables: [], start: 11, text: 'create table\n "untitled_table8" (\n "id" integer not null primary key,\n "created_at" varchar(255) not null\n );', type: 'CREATE_TABLE', @@ -216,6 +227,7 @@ describe('identifier', () => { end: 1212, executionType: 'ANON_BLOCK', parameters: [], + tables: [], start: 180, text: 'DECLARE\n PK_NAME VARCHAR(200);\n\n BEGIN\n EXECUTE IMMEDIATE (\'CREATE SEQUENCE "untitled_table8_seq"\');\n\n SELECT\n cols.column_name INTO PK_NAME\n FROM\n all_constraints cons,\n all_cons_columns cols\n WHERE\n cons.constraint_type = \'P\'\n AND cons.constraint_name = cols.constraint_name\n AND cons.owner = cols.owner\n AND cols.table_name = \'untitled_table8\';\n\n execute immediate (\n \'create or replace trigger "untitled_table8_autoinc_trg" BEFORE INSERT on "untitled_table8" for each row declare checking number := 1; begin if (:new."\' || PK_NAME || \'" is null) then while checking >= 1 loop select "untitled_table8_seq".nextval into :new."\' || PK_NAME || \'" from dual; select count("\' || PK_NAME || \'") into checking from "untitled_table8" where "\' || PK_NAME || \'" = :new."\' || PK_NAME || \'"; end loop; end if; end;\'\n );\n\n END;', type: 'ANON_BLOCK', @@ -248,6 +260,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, { start: 79, @@ -256,6 +269,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, { start: 250, @@ -264,6 +278,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -286,6 +301,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, { start: 54, @@ -294,6 +310,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -317,6 +334,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, { start: 6, @@ -325,6 +343,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -347,6 +366,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, { start: 24, @@ -355,6 +375,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -376,6 +397,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, { start: 19, @@ -384,6 +406,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, { start: 29, @@ -392,6 +415,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -411,6 +435,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, { start: 19 + offset, @@ -419,6 +444,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, { start: 29 + offset, @@ -427,6 +453,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index 30ec5e0..f86c1d8 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -14,6 +14,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -30,6 +31,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -46,6 +48,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -62,6 +65,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -81,6 +85,7 @@ describe('identifier', () => { type: `CREATE_${type}`, executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -105,6 +110,7 @@ describe('identifier', () => { type: 'CREATE_TABLE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -122,6 +128,7 @@ describe('identifier', () => { type: 'CREATE_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -141,6 +148,7 @@ describe('identifier', () => { type: 'CREATE_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -170,6 +178,7 @@ describe('identifier', () => { type: 'CREATE_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -206,6 +215,7 @@ describe('identifier', () => { type: 'CREATE_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -237,6 +247,7 @@ describe('identifier', () => { type: 'CREATE_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -258,6 +269,7 @@ describe('identifier', () => { type: 'CREATE_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -281,6 +293,7 @@ describe('identifier', () => { type: 'CREATE_TRIGGER', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -306,6 +319,7 @@ describe('identifier', () => { type: 'CREATE_TRIGGER', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -340,6 +354,7 @@ describe('identifier', () => { type: 'CREATE_TRIGGER', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -357,6 +372,7 @@ describe('identifier', () => { type: 'CREATE_TRIGGER', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -386,6 +402,7 @@ describe('identifier', () => { type: 'CREATE_PROCEDURE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -413,6 +430,7 @@ describe('identifier', () => { type: 'CREATE_PROCEDURE', executionType: 'MODIFICATION', parameters: [], + tables: [], // FIXME: should return mydataset.customers }, ]; expect(actual).to.eql(expected); @@ -438,6 +456,7 @@ describe('identifier', () => { type: 'CREATE_PROCEDURE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -473,6 +492,7 @@ describe('identifier', () => { type: 'DROP_PROCEDURE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -501,6 +521,7 @@ describe('identifier', () => { type: 'ALTER_PROCEDURE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -575,6 +596,7 @@ describe('identifier', () => { type: `SHOW_${type}`, executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -622,6 +644,7 @@ describe('identifier', () => { type: 'CREATE_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -672,6 +695,7 @@ describe('identifier', () => { type: 'CREATE_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -696,6 +720,7 @@ describe('identifier', () => { type: 'CREATE_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -714,6 +739,7 @@ describe('identifier', () => { type: 'CREATE_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -732,6 +758,7 @@ describe('identifier', () => { type: 'CREATE_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -765,6 +792,7 @@ describe('identifier', () => { type: 'CREATE_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -783,6 +811,7 @@ describe('identifier', () => { type: 'CREATE_INDEX', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -799,6 +828,7 @@ describe('identifier', () => { type: 'CREATE_INDEX', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -817,6 +847,7 @@ describe('identifier', () => { type: 'CREATE_INDEX', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -837,6 +868,7 @@ describe('identifier', () => { type: 'CREATE_INDEX', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -858,6 +890,7 @@ describe('identifier', () => { type: `DROP_${type}`, executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -882,6 +915,7 @@ describe('identifier', () => { type: 'DROP_TABLE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -898,6 +932,7 @@ describe('identifier', () => { type: 'DROP_VIEW', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -914,6 +949,7 @@ describe('identifier', () => { type: 'DROP_DATABASE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -930,6 +966,7 @@ describe('identifier', () => { type: 'DROP_TRIGGER', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -946,6 +983,7 @@ describe('identifier', () => { type: 'DROP_FUNCTION', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -962,6 +1000,7 @@ describe('identifier', () => { type: 'DROP_INDEX', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -977,6 +1016,7 @@ describe('identifier', () => { type: 'TRUNCATE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -993,6 +1033,7 @@ describe('identifier', () => { type: 'INSERT', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -1009,6 +1050,7 @@ describe('identifier', () => { type: 'UPDATE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -1027,6 +1069,7 @@ describe('identifier', () => { type: 'UPDATE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; expect(actual).to.eql(expected); @@ -1042,6 +1085,7 @@ describe('identifier', () => { type: 'DELETE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -1068,6 +1112,7 @@ describe('identifier', () => { type: `ALTER_${type}`, executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -1105,6 +1150,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1126,6 +1172,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1147,6 +1194,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1166,6 +1214,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1186,6 +1235,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1208,6 +1258,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1232,6 +1283,7 @@ describe('identifier', () => { type: 'CREATE_LOGFILE', executionType: 'UNKNOWN', parameters: [], + tables: [], }, ]; @@ -1254,6 +1306,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1275,6 +1328,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], // FIXME: should return 'table'? }, ]; @@ -1313,6 +1367,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1341,6 +1396,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ]; @@ -1361,6 +1417,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: ['$1', '$2'], + tables: [], }, ]; @@ -1380,6 +1437,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: ['$1', '$2', '$3', '$4'], + tables: [], }, ]; @@ -1399,6 +1457,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [':one', ':two'], + tables: [], }, ]; @@ -1418,6 +1477,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: [':one', ':two', ':three'], + tables: [], }, ]; @@ -1437,6 +1497,7 @@ describe('identifier', () => { type: 'SELECT', executionType: 'LISTING', parameters: ['?', '?', '?'], + tables: [], }, ]; @@ -1456,6 +1517,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, ]; @@ -1480,6 +1542,7 @@ describe('identifier', () => { type: 'CREATE_PROCEDURE', executionType: 'MODIFICATION', parameters: [], + tables: [], }, ]; @@ -1497,6 +1560,7 @@ describe('identifier', () => { type: 'UNKNOWN', executionType: 'UNKNOWN', parameters: [], + tables: [], }, ]; diff --git a/test/index.spec.ts b/test/index.spec.ts index 2e4ba1e..d7cd5e9 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -17,6 +17,23 @@ describe('identify', () => { type: 'SELECT', executionType: 'LISTING', parameters: ['$1', '$2'], + tables: [], + }, + ]); + }); + + it('should identify tables in simple for basic cases', () => { + expect( + identify('SELECT * FROM foo JOIN bar ON foo.id = bar.id', { identifyTables: true }), + ).to.eql([ + { + start: 0, + end: 44, + text: 'SELECT * FROM foo JOIN bar ON foo.id = bar.id', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: ['foo', 'bar'], }, ]); }); diff --git a/test/parser/multiple-statements.spec.ts b/test/parser/multiple-statements.spec.ts index dee8793..af638db 100644 --- a/test/parser/multiple-statements.spec.ts +++ b/test/parser/multiple-statements.spec.ts @@ -24,6 +24,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, { start: 56, @@ -31,6 +32,7 @@ describe('parser', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ], tokens: [ @@ -93,6 +95,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, { start: 74, @@ -100,6 +103,7 @@ describe('parser', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ], tokens: [ diff --git a/test/parser/single-statements.spec.ts b/test/parser/single-statements.spec.ts index 8aba295..73caba8 100644 --- a/test/parser/single-statements.spec.ts +++ b/test/parser/single-statements.spec.ts @@ -5,7 +5,7 @@ import { parse } from '../../src/parser'; import { Token } from '../../src/defines'; describe('parser', () => { - describe('given is a not reconized statement', () => { + describe('given is a not recognized statement', () => { it('should throw an error including the unknown statement', () => { expect(() => parse('LIST * FROM Persons')).to.throw('Invalid statement parser "LIST"'); }); @@ -23,6 +23,7 @@ describe('parser', () => { start: 0, end: 14, parameters: [], + tables: [], type: 'UNKNOWN', executionType: 'UNKNOWN', }, @@ -43,6 +44,7 @@ describe('parser', () => { start: 0, end: 19, parameters: [], + tables: [], type: 'UNKNOWN', executionType: 'UNKNOWN', }, @@ -73,6 +75,7 @@ describe('parser', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ], tokens: [ @@ -110,6 +113,7 @@ describe('parser', () => { type: 'SELECT', executionType: 'LISTING', parameters: [], + tables: [], }, ], tokens: [ @@ -148,6 +152,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -212,6 +217,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -276,6 +282,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -332,6 +339,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -394,6 +402,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -450,6 +459,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -511,6 +521,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -555,6 +566,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -599,6 +611,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -643,6 +656,7 @@ describe('parser', () => { executionType: 'MODIFICATION', endStatement: ';', parameters: [], + tables: [], }, ], tokens: [ @@ -885,6 +899,7 @@ describe('parser', () => { 'select x from a where x = ? and y = :foo and z = $1', true, 'generic', + undefined, true, ); actual.tokens = aggregateUnknownTokens(actual.tokens);