Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,18 @@ function stateMachineStatementParser(
statement.parameters.push(token.value);
}

// `SELECT ... INTO target` creates and populates a new table, so the
// statement modifies the database even though its type is SELECT. Only
// dialects where `SELECT INTO` builds a table are flagged here; Oracle
// and MySQL use `SELECT INTO` to assign variables, so they are excluded.
if (
statement.type === 'SELECT' &&
token.value.toUpperCase() === 'INTO' &&
['generic', 'mssql', 'psql'].includes(dialect)
) {
statement.executionType = 'MODIFICATION';
}

if (statement.type && statement.start >= 0) {
// statement has already been identified
// just wait until end of the statement
Expand Down
53 changes: 53 additions & 0 deletions test/identifier/single-statement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,59 @@ describe('identifier', () => {
expect(actual).to.eql(expected);
});

describe('identify "SELECT ... INTO" statements', () => {
// `SELECT ... INTO` creates and populates a new table, so it modifies the
// database. See https://github.com/coresql/sql-query-identifier/issues/81
it('should identify "SELECT ... INTO" as a modification', () => {
const sql = 'SELECT * INTO public."MyTable1" FROM public."MyTable2"';
const actual = identify(sql);
const expected = [
{
start: 0,
end: sql.length - 1,
text: sql,
type: 'SELECT',
executionType: 'MODIFICATION',
parameters: [],
tables: [],
columns: [],
},
];

expect(actual).to.eql(expected);
});

it('should identify "SELECT ... INTO" as a modification in mssql', () => {
const sql = 'SELECT id, name INTO [dbo].[copy] FROM [dbo].[users]';
const actual = identify(sql, { dialect: 'mssql' });

expect(actual[0].type).to.eql('SELECT');
expect(actual[0].executionType).to.eql('MODIFICATION');
});

it('should identify "SELECT ... INTO" as a modification in psql', () => {
const sql = 'SELECT * INTO new_table FROM old_table';
const actual = identify(sql, { dialect: 'psql' });

expect(actual[0].type).to.eql('SELECT');
expect(actual[0].executionType).to.eql('MODIFICATION');
});

it('should still identify a plain "SELECT" as a listing', () => {
const actual = identify('SELECT * FROM Persons');

expect(actual[0].executionType).to.eql('LISTING');
});
Comment on lines +117 to +121
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this test as it's adequately covered by a bunch of other tests, if not explicitly so.


it('should not flag oracle "SELECT INTO", which assigns variables', () => {
const sql = 'SELECT name INTO v_name FROM employees WHERE id = 1';
const actual = identify(sql, { dialect: 'oracle' });

expect(actual[0].type).to.eql('SELECT');
expect(actual[0].executionType).to.eql('LISTING');
});
});

['DATABASE', 'SCHEMA'].forEach((type) => {
describe(`identify "CREATE ${type}" statements`, () => {
const sql = `CREATE ${type} Profile;`;
Expand Down
Loading