mirror of https://github.com/AMT-Cheif/drift.git
346 lines
8.7 KiB
Dart
346 lines
8.7 KiB
Dart
import 'package:sqlparser/sqlparser.dart';
|
|
import 'package:sqlparser/src/utils/ast_equality.dart';
|
|
import 'package:sqlparser/utils/node_to_text.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
enum _ParseKind { statement, moorFile }
|
|
|
|
void main() {
|
|
final engine = SqlEngine(EngineOptions(useMoorExtensions: true));
|
|
|
|
void testFormat(String input, {_ParseKind kind = _ParseKind.statement}) {
|
|
AstNode parse(String input) {
|
|
late ParseResult result;
|
|
|
|
switch (kind) {
|
|
case _ParseKind.statement:
|
|
result = engine.parse(input);
|
|
break;
|
|
case _ParseKind.moorFile:
|
|
result = engine.parseMoorFile(input);
|
|
break;
|
|
}
|
|
|
|
if (result.errors.isNotEmpty) {
|
|
fail('Error parsing $input: ${result.errors.join('\n')}');
|
|
}
|
|
|
|
return result.rootNode;
|
|
}
|
|
|
|
final originalAst = parse(input);
|
|
final formatted = originalAst.toSql();
|
|
final newAst = parse(formatted);
|
|
|
|
try {
|
|
enforceEqual(originalAst, newAst);
|
|
} catch (e) {
|
|
fail('Not equal after formatting: $input to $formatted: $e');
|
|
}
|
|
}
|
|
|
|
group('create', () {
|
|
group('trigger', () {
|
|
test('before delete', () {
|
|
testFormat('''
|
|
CREATE TRIGGER IF NOT EXISTS my_trigger BEFORE DELETE ON t1 BEGIN
|
|
SELECT * FROM t2;
|
|
END;
|
|
''');
|
|
});
|
|
|
|
test('instead of insert', () {
|
|
testFormat('''
|
|
CREATE TRIGGER IF NOT EXISTS my_trigger INSTEAD OF INSERT ON t1 BEGIN
|
|
SELECT * FROM t2;
|
|
END;
|
|
''');
|
|
});
|
|
|
|
test('after update', () {
|
|
testFormat('''
|
|
CREATE TRIGGER IF NOT EXISTS my_trigger AFTER UPDATE ON t1 BEGIN
|
|
SELECT * FROM t2;
|
|
END;
|
|
''');
|
|
});
|
|
|
|
test('after update of when', () {
|
|
testFormat('''
|
|
CREATE TRIGGER IF NOT EXISTS my_trigger AFTER UPDATE OF c1, c2 ON t1
|
|
WHEN foo = bar
|
|
BEGIN
|
|
SELECT * FROM t2;
|
|
END;
|
|
''');
|
|
});
|
|
});
|
|
|
|
test('view', () {
|
|
testFormat('''
|
|
CREATE VIEW my_view (foo, bar) AS SELECT * FROM t1;
|
|
''');
|
|
|
|
testFormat('''
|
|
CREATE VIEW my_view AS SELECT * FROM t1;
|
|
''');
|
|
});
|
|
|
|
test('table', () {
|
|
testFormat('''
|
|
CREATE TABLE IF NOT EXISTS my_table(
|
|
foo TEXT NOT NULL PRIMARY KEY DEFAULT (3 * 4),
|
|
baz INT CONSTRAINT not_null NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
bar TEXT
|
|
UNIQUE ON CONFLICT IGNORE
|
|
CHECK (3 * 4 = 12)
|
|
DEFAULT 'some string here'
|
|
COLLATE c
|
|
REFERENCES t2 (c) ON DELETE RESTRICT ON UPDATE NO ACTION,
|
|
|
|
PRIMARY KEY (foo, bar) ON CONFLICT ABORT,
|
|
UNIQUE (baz) ON CONFLICT REPLACE,
|
|
CONSTRAINT my_constraint CHECK(baz < 3),
|
|
FOREIGN KEY (foo, baz) REFERENCES t2 ON DELETE SET NULL ON UPDATE CASCADE
|
|
DEFERRABLE INITIALLY IMMEDIATE,
|
|
FOREIGN KEY (bar) REFERENCES t2 (bax) ON DELETE SET DEFAULT NOT DEFERRABLE
|
|
);
|
|
''');
|
|
|
|
testFormat('''
|
|
CREATE TABLE IF NOT EXISTS my_table(
|
|
foo INTEGER NOT NULL PRIMARY KEY ASC
|
|
) WITHOUT ROWID;
|
|
''');
|
|
});
|
|
|
|
test('virtual table', () {
|
|
testFormat('CREATE VIRTUAL TABLE foo USING bar(a, b, c);');
|
|
});
|
|
|
|
test('index', () {
|
|
testFormat('''
|
|
CREATE INDEX my_idx ON t1 (c1, c2, c3);
|
|
''');
|
|
|
|
testFormat('''
|
|
CREATE UNIQUE INDEX my_idx ON t1 (c1, c2, c3) WHERE c1 < c3;
|
|
''');
|
|
});
|
|
});
|
|
|
|
group('escapes identifiers', () {
|
|
test("when they're keywords", () {
|
|
testFormat('SELECT * FROM "create";');
|
|
});
|
|
|
|
test('when they contain whitespace', () {
|
|
testFormat('SELECT * FROM "my fancy table"');
|
|
});
|
|
});
|
|
|
|
group('query statements', () {
|
|
group('select', () {
|
|
test('with common table expressions', () {
|
|
testFormat('''
|
|
WITH RECURSIVE foo (id) AS (VALUES(1) UNION ALL SELECT id + 1 FROM foo)
|
|
SELECT * FROM foo;
|
|
''');
|
|
});
|
|
|
|
test('compound', () {
|
|
testFormat('''
|
|
SELECT * FROM foo
|
|
UNION ALL SELECT * FROM bar
|
|
UNION SELECT * FROM baz
|
|
INTERSECT SELECT * FROM bar2
|
|
EXCEPT SELECT * FROM bar3
|
|
LIMIT 5;
|
|
''');
|
|
});
|
|
|
|
test('values', () {
|
|
testFormat('VALUES (1,2,3), (4,5,6);');
|
|
});
|
|
|
|
test('group by', () {
|
|
testFormat('''
|
|
SELECT * FROM foo
|
|
GROUP BY a, b, c HAVING COUNT(id) > 10
|
|
''');
|
|
});
|
|
|
|
test('with windows', () {
|
|
testFormat('''
|
|
SELECT * FROM foo
|
|
WINDOW my_window AS (
|
|
PARTITION BY bar GROUPS
|
|
BETWEEN 3 PRECEDING AND CURRENT ROW
|
|
EXCLUDE NO OTHERS
|
|
),
|
|
other AS (my_window
|
|
ORDER BY foo DESC, bar ASC NULLS FIRST
|
|
RANGE UNBOUNDED PRECEDING EXCLUDE CURRENT ROW
|
|
),
|
|
yet_another AS (other RANGE CURRENT ROW EXCLUDE GROUP),
|
|
finally AS (other
|
|
RANGE BETWEEN CURRENT ROW AND 4 FOLLOWING
|
|
EXCLUDE TIES
|
|
)
|
|
''');
|
|
});
|
|
|
|
test('joins', () {
|
|
testFormat('''
|
|
SELECT * FROM
|
|
foo AS f,
|
|
bar
|
|
NATURAL INNER JOIN j1 USING (foo)
|
|
LEFT JOIN j2 ON j2.id = bar.c
|
|
LEFT OUTER JOIN j3 ON j3.id = bar.c
|
|
CROSS JOIN j4 ON j4.a = j3.b
|
|
INNER JOIN (SELECT * FROM bar) AS b
|
|
INNER JOIN table_valued_function(foo)
|
|
''');
|
|
});
|
|
|
|
test('limit', () {
|
|
testFormat('SELECT * FROM foo LIMIT 3, 4');
|
|
testFormat('SELECT * FROM foo LIMIT 4 OFFSET 3');
|
|
});
|
|
|
|
test('order by', () {
|
|
testFormat('SELECT foo.* FROM foo ORDER BY foo NULLS FIRST');
|
|
});
|
|
});
|
|
|
|
test('delete', () {
|
|
testFormat(
|
|
'WITH foo (id) AS (SELECT * FROM bar) DELETE FROM bar WHERE x;');
|
|
});
|
|
|
|
group('insert', () {
|
|
test('replace', () {
|
|
testFormat('WITH foo (id) AS (SELECT * FROM bar) '
|
|
'REPLACE INTO foo DEFAULT VALUES');
|
|
});
|
|
|
|
test('insert into select', () {
|
|
testFormat('INSERT INTO foo SELECT * FROM bar');
|
|
});
|
|
|
|
test('upsert - do nothing', () {
|
|
testFormat(
|
|
'INSERT OR REPLACE INTO foo DEFAULT VALUES ON CONFLICT DO NOTHING');
|
|
});
|
|
|
|
test('upsert - update', () {
|
|
testFormat('INSERT INTO foo VALUES (1, 2, 3) '
|
|
'ON CONFLICT DO UPDATE SET a = b, c = d WHERE d < a;');
|
|
});
|
|
});
|
|
});
|
|
|
|
group('expressions', () {
|
|
test('between', () {
|
|
testFormat('SELECT x BETWEEN a AND b');
|
|
testFormat('SELECT x NOT BETWEEN a AND b');
|
|
});
|
|
|
|
test('binary', () {
|
|
testFormat('SELECT x OR y');
|
|
testFormat('SELECT x AND y');
|
|
});
|
|
|
|
test('boolean literals', () {
|
|
testFormat('SELECT TRUE OR FALSE');
|
|
});
|
|
|
|
test('case', () {
|
|
testFormat('SELECT CASE WHEN a THEN b ELSE C END');
|
|
testFormat('SELECT CASE x WHEN a THEN b WHEN c THEN d END');
|
|
});
|
|
|
|
test('cast', () {
|
|
testFormat('SELECT CAST(X AS INTEGER)');
|
|
});
|
|
|
|
test('collate', () {
|
|
testFormat('SELECT x COLLATE y AS xc');
|
|
});
|
|
|
|
test('exists', () {
|
|
testFormat('SELECT x FROM foo WHERE EXISTS (SELECT * FROM bar)');
|
|
});
|
|
|
|
test('function', () {
|
|
testFormat('SELECT my_function(*) FROM foo');
|
|
testFormat('SELECT my_function(DISTINCT a, b) FROM foo');
|
|
testFormat('SELECT my_function(a, b) FROM foo');
|
|
});
|
|
|
|
test('is', () {
|
|
testFormat('SELECT foo IS bar');
|
|
testFormat('SELECT foo IS NOT bar');
|
|
});
|
|
|
|
test('is null', () {
|
|
testFormat('SELECT foo ISNULL');
|
|
testFormat('SELECT foo NOTNULL');
|
|
});
|
|
|
|
test('null literal', () {
|
|
testFormat('SELECT NULL;');
|
|
});
|
|
|
|
test('parentheses', () {
|
|
testFormat('SELECT (3 + 4) * 5');
|
|
});
|
|
|
|
test('reference with table', () {
|
|
testFormat('SELECT foo.bar FROM foo');
|
|
});
|
|
|
|
test('string comparison', () {
|
|
testFormat('SELECT x LIKE y FROM foo');
|
|
});
|
|
|
|
test('time literals', () {
|
|
testFormat('SELECT CURRENT_TIME;');
|
|
testFormat('SELECT CURRENT_DATE;');
|
|
testFormat('SELECT CURRENT_TIMESTAMP;');
|
|
});
|
|
|
|
test('unary expression', () {
|
|
testFormat('SELECT -(+(~3));');
|
|
});
|
|
});
|
|
|
|
group('moor', () {
|
|
test('dart placeholders', () {
|
|
testFormat(r'SELECT $placeholder FROM foo');
|
|
});
|
|
|
|
test('imports', () {
|
|
testFormat('import \'foo.bar\';', kind: _ParseKind.moorFile);
|
|
});
|
|
|
|
test('declared statements', () {
|
|
testFormat('foo (?1 AS INT): SELECT * FROM bar WHERE ? < 10;',
|
|
kind: _ParseKind.moorFile);
|
|
testFormat('foo: SELECT * FROM bar WHERE :id < 10;',
|
|
kind: _ParseKind.moorFile);
|
|
testFormat(r'foo ($pred = FALSE): SELECT * FROM bar WHERE $pred;',
|
|
kind: _ParseKind.moorFile);
|
|
});
|
|
|
|
test('nested star', () {
|
|
testFormat('q: SELECT foo.** FROM foo;', kind: _ParseKind.moorFile);
|
|
});
|
|
});
|
|
|
|
test('does not format invalid statements', () {
|
|
expect(InvalidStatement().toSql, throwsUnsupportedError);
|
|
});
|
|
}
|