diff --git a/src/defines.ts b/src/defines.ts index f26ecda..833316f 100644 --- a/src/defines.ts +++ b/src/defines.ts @@ -109,6 +109,7 @@ export interface IdentifyResult { executionType: ExecutionType; parameters: string[]; tables: string[]; + parameterMacros: Record; } export interface Statement { @@ -124,6 +125,7 @@ export interface Statement { parameters: string[]; tables: string[]; isCte?: boolean; + parameterMacros: Record; } export interface ConcreteStatement extends Statement { diff --git a/src/index.ts b/src/index.ts index f600339..a7c1d91 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,6 +37,7 @@ export function identify(query: string, options: IdentifyOptions = {}): Identify // we want to sort the postgres params: $1 $2 $3, regardless of the order they appear parameters: sort ? statement.parameters.sort() : statement.parameters, tables: statement.tables || [], + parameterMacros: statement.parameterMacros || {}, }; return result; }); diff --git a/src/parser.ts b/src/parser.ts index b185904..3654e31 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -128,9 +128,24 @@ function createInitialStatement(): Statement { end: 0, parameters: [], tables: [], + parameterMacros: {}, }; } +const INLINE_MACRO_RE = /^--\s*%([^%]+)%\s*=\s*(.+)$/; +const BLOCK_MACRO_RE = /^\/\*\s*%([^%]+)%\s*=\s*(.*?)\s*\*\/$/s; + +function parseMacroFromComment(token: Token): [string, string] | null { + if (token.type === 'comment-inline') { + const match = INLINE_MACRO_RE.exec(token.value.trim()); + if (match) return [match[1].trim(), match[2].trim()]; + } else if (token.type === 'comment-block') { + const match = BLOCK_MACRO_RE.exec(token.value.trim()); + if (match) return [match[1].trim(), match[2].trim()]; + } + return null; +} + function nextNonWhitespaceToken(state: State, dialect: Dialect): Token { let token: Token; do { @@ -161,6 +176,7 @@ export function parse( let prevState: State = topLevelState; let statementParser: StatementParser | null = null; + let pendingMacros: Record = {}; const cteState: { isCte: boolean; asSeen: boolean; @@ -189,6 +205,12 @@ export function parse( if (!cteState.isCte && ignoreOutsideBlankTokens.includes(token.type)) { topLevelStatement.tokens.push(token); prevState = tokenState; + if (token.type === 'comment-inline' || token.type === 'comment-block') { + const macro = parseMacroFromComment(token); + if (macro) pendingMacros[macro[0]] = macro[1]; + } else if (token.type === 'semicolon') { + pendingMacros = {}; + } } else if ( !cteState.isCte && token.type === 'keyword' && @@ -211,7 +233,9 @@ export function parse( executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: { ...pendingMacros }, }); + pendingMacros = {}; cteState.isCte = false; cteState.asSeen = false; cteState.statementEnd = false; @@ -253,6 +277,8 @@ export function parse( dialect, identifyTables, }); + statementParser.getStatement().parameterMacros = { ...pendingMacros }; + pendingMacros = {}; if (cteState.isCte) { statementParser.getStatement().start = cteState.state.start; statementParser.getStatement().isCte = true; diff --git a/test/identifier/inner-statements.spec.ts b/test/identifier/inner-statements.spec.ts index cbbc98b..a9d19f7 100644 --- a/test/identifier/inner-statements.spec.ts +++ b/test/identifier/inner-statements.spec.ts @@ -17,6 +17,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -36,6 +37,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -57,6 +59,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -79,6 +82,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; diff --git a/test/identifier/multiple-statement.spec.ts b/test/identifier/multiple-statement.spec.ts index a55b620..b6f2739 100644 --- a/test/identifier/multiple-statement.spec.ts +++ b/test/identifier/multiple-statement.spec.ts @@ -17,6 +17,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, { end: 76, @@ -26,6 +27,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -47,6 +49,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, { start: 74, @@ -56,6 +59,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -80,6 +84,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, { start: 35, @@ -89,6 +94,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -112,6 +118,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, { start: 20, @@ -121,6 +128,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, { start: 50, @@ -130,6 +138,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -171,6 +180,7 @@ describe('identifier', () => { executionType: 'ANON_BLOCK', parameters: [], tables: [], + parameterMacros: {}, 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', @@ -219,6 +229,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, 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', @@ -228,6 +239,7 @@ describe('identifier', () => { executionType: 'ANON_BLOCK', parameters: [], tables: [], + parameterMacros: {}, 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', @@ -261,6 +273,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, { start: 79, @@ -270,6 +283,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, { start: 250, @@ -279,6 +293,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -302,6 +317,7 @@ describe('identifier', () => { executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: {}, }, { start: 54, @@ -311,6 +327,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -335,6 +352,7 @@ describe('identifier', () => { executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: {}, }, { start: 6, @@ -344,6 +362,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -367,6 +386,7 @@ describe('identifier', () => { executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: {}, }, { start: 24, @@ -376,6 +396,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -398,6 +419,7 @@ describe('identifier', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, { start: 19, @@ -407,6 +429,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, { start: 29, @@ -416,6 +439,7 @@ describe('identifier', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -436,6 +460,7 @@ describe('identifier', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, { start: 19 + offset, @@ -445,6 +470,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, { start: 29 + offset, @@ -454,6 +480,7 @@ describe('identifier', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); diff --git a/test/identifier/single-statement.spec.ts b/test/identifier/single-statement.spec.ts index f86c1d8..a30214e 100644 --- a/test/identifier/single-statement.spec.ts +++ b/test/identifier/single-statement.spec.ts @@ -15,6 +15,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -32,6 +33,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -49,6 +51,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -66,6 +69,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -86,6 +90,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -111,6 +116,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -129,6 +135,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -149,6 +156,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -179,6 +187,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -216,6 +225,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -248,6 +258,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -270,6 +281,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -294,6 +306,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -320,6 +333,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -355,6 +369,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -373,6 +388,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -403,6 +419,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -431,6 +448,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], // FIXME: should return mydataset.customers + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -457,6 +475,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -493,6 +512,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -522,6 +542,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -597,6 +618,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -645,6 +667,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -696,6 +719,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -721,6 +745,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -740,6 +765,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -759,6 +785,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -793,6 +820,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -812,6 +840,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -829,6 +858,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -848,6 +878,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -869,6 +900,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -891,6 +923,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -916,6 +949,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -933,6 +967,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -950,6 +985,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -967,6 +1003,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -984,6 +1021,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -1001,6 +1039,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -1017,6 +1056,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1034,6 +1074,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1051,6 +1092,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1070,6 +1112,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; expect(actual).to.eql(expected); @@ -1086,6 +1129,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1113,6 +1157,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1151,6 +1196,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1173,6 +1219,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1195,6 +1242,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1215,6 +1263,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1236,6 +1285,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1259,6 +1309,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1284,6 +1335,7 @@ describe('identifier', () => { executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1307,6 +1359,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1329,6 +1382,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], // FIXME: should return 'table'? + parameterMacros: {}, }, ]; @@ -1368,6 +1422,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1397,6 +1452,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1418,6 +1474,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: ['$1', '$2'], tables: [], + parameterMacros: {}, }, ]; @@ -1438,6 +1495,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: ['$1', '$2', '$3', '$4'], tables: [], + parameterMacros: {}, }, ]; @@ -1458,6 +1516,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [':one', ':two'], tables: [], + parameterMacros: {}, }, ]; @@ -1478,6 +1537,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: [':one', ':two', ':three'], tables: [], + parameterMacros: {}, }, ]; @@ -1498,6 +1558,7 @@ describe('identifier', () => { executionType: 'LISTING', parameters: ['?', '?', '?'], tables: [], + parameterMacros: {}, }, ]; @@ -1518,6 +1579,7 @@ describe('identifier', () => { executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1543,6 +1605,7 @@ describe('identifier', () => { executionType: 'MODIFICATION', parameters: [], tables: [], + parameterMacros: {}, }, ]; @@ -1561,6 +1624,7 @@ describe('identifier', () => { executionType: 'UNKNOWN', parameters: [], tables: [], + parameterMacros: {}, }, ]; diff --git a/test/index.spec.ts b/test/index.spec.ts index 557cd86..12be15a 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -19,6 +19,7 @@ describe('identify', () => { executionType: 'LISTING', parameters: ['$1', '$2'], tables: [], + parameterMacros: {}, }, ]); }); @@ -42,6 +43,7 @@ describe('identify', () => { executionType: 'LISTING', parameters: ['?', '$1', ':fizzz', ':"buzz buzz"', '{fooo}'], tables: [], + parameterMacros: {}, }, ]); }); @@ -62,6 +64,7 @@ describe('identify', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -83,6 +86,7 @@ describe('identify', () => { executionType: 'LISTING', parameters: ['$1'], tables: [], + parameterMacros: {}, }, ]); }); @@ -99,6 +103,7 @@ describe('identify', () => { executionType: 'LISTING', parameters: [], tables: ['foo', 'bar'], + parameterMacros: {}, }, ]); }); @@ -177,6 +182,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -191,6 +197,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -205,6 +212,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -219,6 +227,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -233,6 +242,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -247,6 +257,7 @@ describe('Transaction statements', () => { executionType: 'ANON_BLOCK', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -267,6 +278,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -279,6 +291,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -293,6 +306,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -305,6 +319,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -317,6 +332,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -329,6 +345,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -343,6 +360,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); @@ -357,6 +375,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -369,6 +388,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -381,6 +401,7 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); @@ -395,7 +416,99 @@ describe('Transaction statements', () => { executionType: 'TRANSACTION', parameters: [], tables: [], + parameterMacros: {}, }, ]); }); + + describe('parameter macros', () => { + it('should detect a single inline macro before a statement', () => { + const query = '-- %id% = 7\nSELECT * FROM table WHERE id = %id%;'; + expect(identify(query, { strict: false })).to.eql([ + { + start: 12, + end: 47, + text: 'SELECT * FROM table WHERE id = %id%;', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: [], + parameterMacros: { id: '7' }, + }, + ]); + }); + + it('should detect multiple inline macros before a statement', () => { + const query = '-- %id% = 7\n-- %name% = Alice\nSELECT * FROM t WHERE id = %id% AND name = %name%;'; + expect(identify(query, { strict: false })).to.eql([ + { + start: 30, + end: 79, + text: 'SELECT * FROM t WHERE id = %id% AND name = %name%;', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: [], + parameterMacros: { id: '7', name: 'Alice' }, + }, + ]); + }); + + it('should detect a block comment macro before a statement', () => { + const query = '/* %id% = 42 */\nSELECT 1;'; + expect(identify(query, { strict: false })).to.eql([ + { + start: 16, + end: 24, + text: 'SELECT 1;', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: [], + parameterMacros: { id: '42' }, + }, + ]); + }); + + it('macros should not bleed into the next statement', () => { + const query = '-- %id% = 7\nSELECT 1;\nSELECT 2;'; + expect(identify(query, { strict: false })).to.eql([ + { + start: 12, + end: 20, + text: 'SELECT 1;', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: [], + parameterMacros: { id: '7' }, + }, + { + start: 22, + end: 30, + text: 'SELECT 2;', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: [], + parameterMacros: {}, + }, + ]); + }); + + it('should return empty parameterMacros when no macros declared', () => { + expect(identify('SELECT 1;')).to.eql([ + { + start: 0, + end: 8, + text: 'SELECT 1;', + type: 'SELECT', + executionType: 'LISTING', + parameters: [], + tables: [], + parameterMacros: {}, + }, + ]); + }); + }); }); diff --git a/test/parser/multiple-statements.spec.ts b/test/parser/multiple-statements.spec.ts index af638db..1d1e6b6 100644 --- a/test/parser/multiple-statements.spec.ts +++ b/test/parser/multiple-statements.spec.ts @@ -25,6 +25,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, { start: 56, @@ -33,6 +34,7 @@ describe('parser', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -96,6 +98,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, { start: 74, @@ -104,6 +107,7 @@ describe('parser', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ diff --git a/test/parser/single-statements.spec.ts b/test/parser/single-statements.spec.ts index 4fa5b0b..2b49d63 100644 --- a/test/parser/single-statements.spec.ts +++ b/test/parser/single-statements.spec.ts @@ -24,6 +24,7 @@ describe('parser', () => { end: 14, parameters: [], tables: [], + parameterMacros: {}, type: 'UNKNOWN', executionType: 'UNKNOWN', }, @@ -45,6 +46,7 @@ describe('parser', () => { end: 19, parameters: [], tables: [], + parameterMacros: {}, type: 'UNKNOWN', executionType: 'UNKNOWN', }, @@ -76,6 +78,7 @@ describe('parser', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -114,6 +117,7 @@ describe('parser', () => { executionType: 'LISTING', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -153,6 +157,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -218,6 +223,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -283,6 +289,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -340,6 +347,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -403,6 +411,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -460,6 +469,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -522,6 +532,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -567,6 +578,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -612,6 +624,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [ @@ -657,6 +670,7 @@ describe('parser', () => { endStatement: ';', parameters: [], tables: [], + parameterMacros: {}, }, ], tokens: [