Always escape column names

This avoids the performance impact of using a regex in `escapeIfNeeded`.
Closes #2071.
This commit is contained in:
Simon Binder 2022-09-22 16:55:36 +02:00
parent be61af5111
commit 28c90b6ffb
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
33 changed files with 307 additions and 290 deletions

View File

@ -1,3 +1,8 @@
## 2.2.0-dev
- Always escape column names, avoiding the costs or using a regular expression
to check whether they need to be escaped.
## 2.1.0 ## 2.1.0
- Improve stack traces when using `watchSingle()` with a stream emitting a non- - Improve stack traces when using `watchSingle()` with a stream emitting a non-

View File

@ -386,7 +386,7 @@ class $TodoItemsTable extends TodoItems
'category_id', aliasedName, false, 'category_id', aliasedName, false,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: true, requiredDuringInsert: true,
defaultConstraints: 'REFERENCES todo_categories (id)'); defaultConstraints: 'REFERENCES "todo_categories" ("id")');
final VerificationMeta _generatedTextMeta = final VerificationMeta _generatedTextMeta =
const VerificationMeta('generatedText'); const VerificationMeta('generatedText');
@override @override

View File

@ -1,4 +1,6 @@
/// Provides utilities around sql keywords, like optional escaping etc. /// Provides utilities around sql keywords, like optional escaping etc.
@Deprecated('Drift is no longer using this library and will remove it in the '
'next breaking release')
library drift.sqlite_keywords; library drift.sqlite_keywords;
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';

View File

@ -38,8 +38,12 @@ abstract class Column<T extends Object> extends Expression<T> {
/// needed. /// needed.
String get name; String get name;
/// [name], but escaped if it's an sql keyword. /// [name], but wrapped in double quotes to escape it as a a same identifier.
String get escapedName => escapeIfNeeded(name); ///
/// In the past, this getter only used to add double-quotes when that is
/// really needed (for instance because [name] is also a reserved keyword).
/// For performance reasons, we unconditionally escape names now.
String get escapedName => '"$name"';
} }
/// A column that stores int values. /// A column that stores int values.

View File

@ -1,5 +1,4 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift/sqlite_keywords.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:meta/meta_meta.dart'; import 'package:meta/meta_meta.dart';

View File

@ -77,4 +77,8 @@ class GenerationContext {
/// Shortcut to add a single space to the buffer because it's used very often. /// Shortcut to add a single space to the buffer because it's used very often.
void writeWhitespace() => buffer.write(' '); void writeWhitespace() => buffer.write(' ');
/// Turns [columnName] into a safe SQL identifier by wrapping it in double
/// quotes.
String identifier(String columnName) => '"$columnName"';
} }

View File

@ -227,15 +227,16 @@ class Migrator {
expr.writeInto(context); expr.writeInto(context);
first = false; first = false;
} }
context.buffer.write(' FROM ${escapeIfNeeded(tableName)};'); context.buffer.write(' FROM ${context.identifier(tableName)};');
await _issueCustomQuery(context.sql, context.introducedVariables); await _issueCustomQuery(context.sql, context.introducedVariables);
// Step 6: Drop the old table // Step 6: Drop the old table
await _issueCustomQuery('DROP TABLE ${escapeIfNeeded(tableName)}'); await _issueCustomQuery('DROP TABLE ${context.identifier(tableName)}');
// Step 7: Rename the new table to the old name // Step 7: Rename the new table to the old name
await _issueCustomQuery('ALTER TABLE ${escapeIfNeeded(temporaryName)} ' await _issueCustomQuery(
'RENAME TO ${escapeIfNeeded(tableName)}'); 'ALTER TABLE ${context.identifier(temporaryName)} '
'RENAME TO ${context.identifier(tableName)}');
// Step 8: Re-create associated indexes, triggers and views // Step 8: Re-create associated indexes, triggers and views
for (final stmt in createAffected) { for (final stmt in createAffected) {
@ -253,7 +254,7 @@ class Migrator {
void _writeCreateTable(TableInfo table, GenerationContext context) { void _writeCreateTable(TableInfo table, GenerationContext context) {
context.buffer.write('CREATE TABLE IF NOT EXISTS ' context.buffer.write('CREATE TABLE IF NOT EXISTS '
'${escapeIfNeeded(table.aliasedName, context.dialect)} ('); '${context.identifier(table.aliasedName)} (');
var hasAutoIncrement = false; var hasAutoIncrement = false;
for (var i = 0; i < table.$columns.length; i++) { for (var i = 0; i < table.$columns.length; i++) {
@ -281,7 +282,7 @@ class Migrator {
for (var i = 0; i < pkList.length; i++) { for (var i = 0; i < pkList.length; i++) {
final column = pkList[i]; final column = pkList[i];
context.buffer.write(escapeIfNeeded(column.$name)); context.buffer.write(column.escapedName);
if (i != pkList.length - 1) context.buffer.write(', '); if (i != pkList.length - 1) context.buffer.write(', ');
} }
@ -295,7 +296,7 @@ class Migrator {
for (var i = 0; i < uqList.length; i++) { for (var i = 0; i < uqList.length; i++) {
final column = uqList[i]; final column = uqList[i];
context.buffer.write(escapeIfNeeded(column.name)); context.buffer.write(column.escapedName);
if (i != uqList.length - 1) context.buffer.write(', '); if (i != uqList.length - 1) context.buffer.write(', ');
} }
@ -327,7 +328,7 @@ class Migrator {
void _writeCreateVirtual(VirtualTableInfo table, GenerationContext context) { void _writeCreateVirtual(VirtualTableInfo table, GenerationContext context) {
context.buffer context.buffer
..write('CREATE VIRTUAL TABLE IF NOT EXISTS ') ..write('CREATE VIRTUAL TABLE IF NOT EXISTS ')
..write(escapeIfNeeded(table.aliasedName)) ..write(context.identifier(table.aliasedName))
..write(' USING ') ..write(' USING ')
..write(table.moduleAndArgs) ..write(table.moduleAndArgs)
..write(';'); ..write(';');
@ -353,8 +354,8 @@ class Migrator {
final columnNames = view.$columns.map((e) => e.escapedName).join(', '); final columnNames = view.$columns.map((e) => e.escapedName).join(', ');
context.generatingForView = view.entityName; context.generatingForView = view.entityName;
context.buffer.write( context.buffer.write('CREATE VIEW IF NOT EXISTS '
'CREATE VIEW IF NOT EXISTS ${view.entityName} ($columnNames) AS '); '${context.identifier(view.entityName)} ($columnNames) AS ');
view.query!.writeInto(context); view.query!.writeInto(context);
await _issueCustomQuery(context.sql, const []); await _issueCustomQuery(context.sql, const []);
} }
@ -362,7 +363,8 @@ class Migrator {
/// Drops a table, trigger or index. /// Drops a table, trigger or index.
Future<void> drop(DatabaseSchemaEntity entity) async { Future<void> drop(DatabaseSchemaEntity entity) async {
final escapedName = escapeIfNeeded(entity.entityName); final context = _createContext();
final escapedName = context.identifier(entity.entityName);
String kind; String kind;
@ -385,15 +387,17 @@ class Migrator {
/// Deletes the table with the given name. Note that this function does not /// Deletes the table with the given name. Note that this function does not
/// escape the [name] parameter. /// escape the [name] parameter.
Future<void> deleteTable(String name) async { Future<void> deleteTable(String name) async {
return _issueCustomQuery('DROP TABLE IF EXISTS $name;'); final context = _createContext();
return _issueCustomQuery(
'DROP TABLE IF EXISTS ${context.identifier(name)};');
} }
/// Adds the given column to the specified table. /// Adds the given column to the specified table.
Future<void> addColumn(TableInfo table, GeneratedColumn column) async { Future<void> addColumn(TableInfo table, GeneratedColumn column) async {
final context = _createContext(); final context = _createContext();
context.buffer context.buffer.write(
.write('ALTER TABLE ${escapeIfNeeded(table.aliasedName)} ADD COLUMN '); 'ALTER TABLE ${context.identifier(table.aliasedName)} ADD COLUMN ');
column.writeColumnDefinition(context); column.writeColumnDefinition(context);
context.buffer.write(';'); context.buffer.write(';');
@ -421,8 +425,8 @@ class Migrator {
TableInfo table, String oldName, GeneratedColumn column) async { TableInfo table, String oldName, GeneratedColumn column) async {
final context = _createContext(); final context = _createContext();
context.buffer context.buffer
..write('ALTER TABLE ${escapeIfNeeded(table.aliasedName)} ') ..write('ALTER TABLE ${context.identifier(table.aliasedName)} ')
..write('RENAME COLUMN ${escapeIfNeeded(oldName)} ') ..write('RENAME COLUMN ${context.identifier(oldName)} ')
..write('TO ${column.escapedName};'); ..write('TO ${column.escapedName};');
return _issueCustomQuery(context.sql); return _issueCustomQuery(context.sql);
@ -436,8 +440,8 @@ class Migrator {
/// databases. /// databases.
Future<void> renameTable(TableInfo table, String oldName) async { Future<void> renameTable(TableInfo table, String oldName) async {
final context = _createContext(); final context = _createContext();
context.buffer.write('ALTER TABLE ${escapeIfNeeded(oldName)} ' context.buffer.write('ALTER TABLE ${context.identifier(oldName)} '
'RENAME TO ${escapeIfNeeded(table.actualTableName)};'); 'RENAME TO ${context.identifier(table.actualTableName)};');
return _issueCustomQuery(context.sql); return _issueCustomQuery(context.sql);
} }

View File

@ -5,7 +5,6 @@ import 'dart:async';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift/sqlite_keywords.dart';
import 'package:drift/src/runtime/executor/stream_queries.dart'; import 'package:drift/src/runtime/executor/stream_queries.dart';
import 'package:drift/src/utils/single_transformer.dart'; import 'package:drift/src/utils/single_transformer.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';

View File

@ -175,7 +175,7 @@ class GeneratedColumn<T extends Object> extends Column<T> {
} else { } else {
if (context.hasMultipleTables) { if (context.hasMultipleTables) {
context.buffer context.buffer
..write(tableName) ..write(context.identifier(tableName))
..write('.'); ..write('.');
} }
context.buffer.write(ignoreEscape ? $name : escapedName); context.buffer.write(ignoreEscape ? $name : escapedName);

View File

@ -140,9 +140,9 @@ extension NameWithAlias on ResultSetImplementation<dynamic, dynamic> {
/// for a table called users that has been aliased as "u". /// for a table called users that has been aliased as "u".
String get tableWithAlias { String get tableWithAlias {
if (aliasedName == entityName) { if (aliasedName == entityName) {
return entityName; return '"$entityName"';
} else { } else {
return '$entityName $aliasedName'; return '"$entityName" "$aliasedName"';
} }
} }
} }

View File

@ -156,7 +156,7 @@ class InsertStatement<T extends Table, D> {
..write(_insertKeywords[ ..write(_insertKeywords[
ctx.dialect == SqlDialect.postgres ? InsertMode.insert : mode]) ctx.dialect == SqlDialect.postgres ? InsertMode.insert : mode])
..write(' INTO ') ..write(' INTO ')
..write(table.aliasedName) ..write(ctx.identifier(table.aliasedName))
..write(' '); ..write(' ');
if (map.isEmpty) { if (map.isEmpty) {
@ -210,7 +210,7 @@ class InsertStatement<T extends Table, D> {
first = true; first = true;
for (final update in updateSet.entries) { for (final update in updateSet.entries) {
final column = escapeIfNeeded(update.key); final column = ctx.identifier(update.key);
if (!first) ctx.buffer.write(', '); if (!first) ctx.buffer.write(', ');
ctx.buffer.write('$column = '); ctx.buffer.write('$column = ');
@ -260,7 +260,7 @@ class InsertStatement<T extends Table, D> {
/// Writes column names and values from the [map]. /// Writes column names and values from the [map].
@internal @internal
void writeInsertable(GenerationContext ctx, Map<String, Expression> map) { void writeInsertable(GenerationContext ctx, Map<String, Expression> map) {
final columns = map.keys.map(escapeIfNeeded); final columns = map.keys.map(ctx.identifier);
ctx.buffer ctx.buffer
..write('(') ..write('(')

View File

@ -24,7 +24,7 @@ class UpdateStatement<T extends Table, D> extends Query<T, D>
} }
ctx.buffer ctx.buffer
..write(escapeIfNeeded(columnName)) ..write(ctx.identifier(columnName))
..write(' = '); ..write(' = ');
variable.writeInto(ctx); variable.writeInto(ctx);

View File

@ -52,12 +52,12 @@ void main() {
transaction.runBatched( transaction.runBatched(
BatchedStatements( BatchedStatements(
[ [
'INSERT INTO todos (content) VALUES (?)', 'INSERT INTO "todos" ("content") VALUES (?)',
'UPDATE users SET name = ?;', 'UPDATE "users" SET "name" = ?;',
'UPDATE users SET name = ? WHERE name = ?;', 'UPDATE "users" SET "name" = ? WHERE "name" = ?;',
'UPDATE categories SET "desc" = ?, priority = 0 WHERE id = ?;', 'UPDATE "categories" SET "desc" = ?, "priority" = 0 WHERE "id" = ?;',
'DELETE FROM categories WHERE 1;', 'DELETE FROM "categories" WHERE 1;',
'DELETE FROM todos WHERE id = ?;', 'DELETE FROM "todos" WHERE "id" = ?;',
'some custom statement', 'some custom statement',
], ],
[ [
@ -90,8 +90,8 @@ void main() {
verify(executor.transactions.runBatched(BatchedStatements( verify(executor.transactions.runBatched(BatchedStatements(
[ [
('INSERT INTO categories ("desc") VALUES (?) ' ('INSERT INTO "categories" ("desc") VALUES (?) '
'ON CONFLICT(id) DO UPDATE SET id = ?') 'ON CONFLICT("id") DO UPDATE SET "id" = ?')
], ],
[ [
ArgumentsForBatchedStatement(0, ['description', 42]) ArgumentsForBatchedStatement(0, ['description', 42])
@ -111,8 +111,8 @@ void main() {
verify(executor.transactions.runBatched(BatchedStatements( verify(executor.transactions.runBatched(BatchedStatements(
[ [
('INSERT INTO categories ("desc") VALUES (?) ' ('INSERT INTO "categories" ("desc") VALUES (?) '
'ON CONFLICT(id) DO UPDATE SET "desc" = ?') 'ON CONFLICT("id") DO UPDATE SET "desc" = ?')
], ],
[ [
ArgumentsForBatchedStatement(0, ['first', 'first']), ArgumentsForBatchedStatement(0, ['first', 'first']),
@ -145,9 +145,9 @@ void main() {
verify(executor.transactions.runBatched(BatchedStatements( verify(executor.transactions.runBatched(BatchedStatements(
[ [
('INSERT INTO categories ("desc") VALUES (?) ON CONFLICT(id) ' ('INSERT INTO "categories" ("desc") VALUES (?) ON CONFLICT("id") '
'DO UPDATE SET "desc" = categories."desc", ' 'DO UPDATE SET "desc" = "categories"."desc", '
'priority = excluded.priority WHERE categories.id >= excluded.id') '"priority" = "excluded"."priority" WHERE "categories"."id" >= "excluded"."id"')
], ],
[ [
ArgumentsForBatchedStatement(0, ['first']), ArgumentsForBatchedStatement(0, ['first']),
@ -179,7 +179,7 @@ void main() {
}); });
verify(executor.transactions.runBatched(BatchedStatements( verify(executor.transactions.runBatched(BatchedStatements(
['INSERT INTO categories ("desc") VALUES (?)'], ['INSERT INTO "categories" ("desc") VALUES (?)'],
[ [
ArgumentsForBatchedStatement(0, ['first']), ArgumentsForBatchedStatement(0, ['first']),
ArgumentsForBatchedStatement(0, ['second']), ArgumentsForBatchedStatement(0, ['second']),

View File

@ -13,7 +13,8 @@ void main() {
expect( expect(
existsExpression, existsExpression,
generates('EXISTS (SELECT * FROM users WHERE users.is_awesome = ?)', [1]), generates(
'EXISTS (SELECT * FROM "users" WHERE "users"."is_awesome" = ?)', [1]),
); );
}); });
@ -23,7 +24,7 @@ void main() {
expect( expect(
notExistsExpression, notExistsExpression,
generates('NOT EXISTS (SELECT * FROM users)'), generates('NOT EXISTS (SELECT * FROM "users")'),
); );
}); });
} }

View File

@ -27,8 +27,7 @@ void main() {
}); });
test('generates parentheses for OR in AND', () { test('generates parentheses for OR in AND', () {
final c = final c = CustomExpression<String>('c', precedence: Precedence.primary);
GeneratedColumn<String>('c', 't', false, type: DriftSqlType.string);
final expr = final expr =
(c.equals('A') | c.equals('B')) & (c.equals('C') | c.equals('')); (c.equals('A') | c.equals('B')) & (c.equals('C') | c.equals(''));
expect( expect(
@ -54,7 +53,7 @@ void main() {
expect( expect(
subqueryExpression<String>( subqueryExpression<String>(
db.selectOnly(db.users)..addColumns([db.users.name])), db.selectOnly(db.users)..addColumns([db.users.name])),
generates('(SELECT users.name AS "users.name" FROM users)')); generates('(SELECT "users"."name" AS "users.name" FROM "users")'));
}); });
test('does not allow subqueries with more than one column', () { test('does not allow subqueries with more than one column', () {
@ -77,8 +76,8 @@ void main() {
innerJoin(db.categories, db.categories.id.equalsExp(db.users.id), innerJoin(db.categories, db.categories.id.equalsExp(db.users.id),
useColumns: false) useColumns: false)
])), ])),
generates('(SELECT users.name AS "users.name" FROM users ' generates('(SELECT "users"."name" AS "users.name" FROM "users" '
'INNER JOIN categories ON categories.id = users.id)'), 'INNER JOIN "categories" ON "categories"."id" = "users"."id")'),
); );
}); });
@ -94,7 +93,7 @@ void main() {
}); });
test('generates a rowid expression', () { test('generates a rowid expression', () {
expect(TodoDb().categories.rowId, generates('_rowid_')); expect(TodoDb().categories.rowId, generates('"_rowid_"'));
}); });
test('generates an aliased rowid expression when needed', () async { test('generates an aliased rowid expression when needed', () async {
@ -108,7 +107,7 @@ void main() {
await query.get(); await query.get();
verify(executor verify(executor
.runSelect(argThat(contains('ON categories._rowid_ = ?')), [3])); .runSelect(argThat(contains('ON "categories"."_rowid_" = ?')), [3]));
}); });
}); });

View File

@ -28,8 +28,10 @@ void main() {
final isInExpression = innerExpression final isInExpression = innerExpression
.isInQuery(db.selectOnly(db.users)..addColumns([db.users.name])); .isInQuery(db.selectOnly(db.users)..addColumns([db.users.name]));
expect(isInExpression, expect(
generates('name IN (SELECT users.name AS "users.name" FROM users)')); isInExpression,
generates(
'name IN (SELECT "users"."name" AS "users.name" FROM "users")'));
final ctx = stubContext(); final ctx = stubContext();
isInExpression.writeInto(ctx); isInExpression.writeInto(ctx);
@ -43,7 +45,7 @@ void main() {
expect( expect(
isInExpression, isInExpression,
generates( generates(
'name NOT IN (SELECT users.name AS "users.name" FROM users)')); 'name NOT IN (SELECT "users"."name" AS "users.name" FROM "users")'));
}); });
}); });
} }

View File

@ -24,7 +24,7 @@ void main() {
test('without any constraints', () async { test('without any constraints', () async {
await db.delete(db.users).go(); await db.delete(db.users).go();
verify(executor.runDelete('DELETE FROM users;', argThat(isEmpty))); verify(executor.runDelete('DELETE FROM "users";', argThat(isEmpty)));
}); });
test('for complex components', () async { test('for complex components', () async {
@ -33,7 +33,8 @@ void main() {
.go(); .go();
verify(executor.runDelete( verify(executor.runDelete(
'DELETE FROM users WHERE NOT is_awesome OR id < ?;', const [100])); 'DELETE FROM "users" WHERE NOT "is_awesome" OR "id" < ?;',
const [100]));
}); });
test('to delete an entity via a dataclasss', () async { test('to delete an entity via a dataclasss', () async {
@ -42,7 +43,7 @@ void main() {
.delete(const SharedTodo(todo: 3, user: 2)); .delete(const SharedTodo(todo: 3, user: 2));
verify(executor.runDelete( verify(executor.runDelete(
'DELETE FROM shared_todos WHERE todo = ? AND user = ?;', 'DELETE FROM "shared_todos" WHERE "todo" = ? AND "user" = ?;',
const [3, 2], const [3, 2],
)); ));
}); });
@ -59,8 +60,8 @@ void main() {
.delete(db.todosTable) .delete(db.todosTable)
.deleteReturning(const TodosTableCompanion(id: Value(10))); .deleteReturning(const TodosTableCompanion(id: Value(10)));
verify(executor verify(executor.runSelect(
.runSelect('DELETE FROM todos WHERE id = ? RETURNING *;', [10])); 'DELETE FROM "todos" WHERE "id" = ? RETURNING *;', [10]));
verify(streamQueries.handleTableUpdates( verify(streamQueries.handleTableUpdates(
{TableUpdate.onTable(db.todosTable, kind: UpdateKind.delete)})); {TableUpdate.onTable(db.todosTable, kind: UpdateKind.delete)}));
expect(returnedValue, const TodoEntry(id: 10, content: 'Content')); expect(returnedValue, const TodoEntry(id: 10, content: 'Content'));
@ -70,7 +71,7 @@ void main() {
final rows = await db.delete(db.users).goAndReturn(); final rows = await db.delete(db.users).goAndReturn();
expect(rows, isEmpty); expect(rows, isEmpty);
verify(executor.runSelect('DELETE FROM users RETURNING *;', [])); verify(executor.runSelect('DELETE FROM "users" RETURNING *;', []));
verifyNever(streamQueries.handleTableUpdates(any)); verifyNever(streamQueries.handleTableUpdates(any));
}); });
}); });
@ -107,19 +108,21 @@ void main() {
test('delete()', () async { test('delete()', () async {
await db.users.delete().go(); await db.users.delete().go();
verify(executor.runDelete('DELETE FROM users;', const [])); verify(executor.runDelete('DELETE FROM "users";', const []));
}); });
test('deleteOne()', () async { test('deleteOne()', () async {
await db.users.deleteOne(const UsersCompanion(id: Value(3))); await db.users.deleteOne(const UsersCompanion(id: Value(3)));
verify(executor.runDelete('DELETE FROM users WHERE id = ?;', const [3])); verify(
executor.runDelete('DELETE FROM "users" WHERE "id" = ?;', const [3]));
}); });
test('deleteWhere', () async { test('deleteWhere', () async {
await db.users.deleteWhere((tbl) => tbl.id.isSmallerThanValue(3)); await db.users.deleteWhere((tbl) => tbl.id.isSmallerThanValue(3));
verify(executor.runDelete('DELETE FROM users WHERE id < ?;', const [3])); verify(
executor.runDelete('DELETE FROM "users" WHERE "id" < ?;', const [3]));
}); });
}); });
} }

View File

@ -24,7 +24,7 @@ void main() {
title: Value.absent(), title: Value.absent(),
)); ));
verify(executor.runInsert('INSERT INTO todos (content) VALUES (?)', verify(executor.runInsert('INSERT INTO "todos" ("content") VALUES (?)',
['Implement insert statements'])); ['Implement insert statements']));
}); });
@ -35,8 +35,8 @@ void main() {
.toInsertable()); .toInsertable());
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO table_without_p_k ' 'INSERT INTO "table_without_p_k" '
'(not_really_an_id, some_float, web_safe_int, custom) ' '("not_really_an_id", "some_float", "web_safe_int", "custom") '
'VALUES (?, ?, ?, ?)', 'VALUES (?, ?, ?, ?)',
[42, 3.1415, isNull, anything])); [42, 3.1415, isNull, anything]));
}); });
@ -47,8 +47,8 @@ void main() {
.toInsertable()); .toInsertable());
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO table_without_p_k ' 'INSERT INTO "table_without_p_k" '
'(not_really_an_id, some_float, web_safe_int, custom) ' '("not_really_an_id", "some_float", "web_safe_int", "custom") '
'VALUES (?, ?, ?, ?)', 'VALUES (?, ?, ?, ?)',
[42, 0.0, BigInt.one, anything])); [42, 0.0, BigInt.one, anything]));
}); });
@ -62,14 +62,15 @@ void main() {
mode: InsertMode.insertOrReplace); mode: InsertMode.insertOrReplace);
verify(executor.runInsert( verify(executor.runInsert(
'INSERT OR REPLACE INTO todos (id, content) VALUES (?, ?)', 'INSERT OR REPLACE INTO "todos" ("id", "content") VALUES (?, ?)',
[113, 'Done'])); [113, 'Done']));
}); });
test('generates DEFAULT VALUES statement when otherwise empty', () async { test('generates DEFAULT VALUES statement when otherwise empty', () async {
await db.into(db.pureDefaults).insert(const PureDefaultsCompanion()); await db.into(db.pureDefaults).insert(const PureDefaultsCompanion());
verify(executor.runInsert('INSERT INTO pure_defaults DEFAULT VALUES', [])); verify(
executor.runInsert('INSERT INTO "pure_defaults" DEFAULT VALUES', []));
}); });
test('notifies stream queries on inserts', () async { test('notifies stream queries on inserts', () async {
@ -156,7 +157,7 @@ void main() {
TodosTableCompanion.insert(content: 'content', title: Value(null))); TodosTableCompanion.insert(content: 'content', title: Value(null)));
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (title, content) VALUES (?, ?)', 'INSERT INTO "todos" ("title", "content") VALUES (?, ?)',
[null, 'content'])); [null, 'content']));
}); });
}); });
@ -181,7 +182,7 @@ void main() {
r'[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12}'); r'[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12}');
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO table_without_p_k (not_really_an_id, some_float, custom) ' 'INSERT INTO "table_without_p_k" ("not_really_an_id", "some_float", "custom") '
'VALUES (?, ?, ?)', 'VALUES (?, ?, ?)',
[3, 3.14, matches(uuidRegex)], [3, 3.14, matches(uuidRegex)],
)); ));
@ -191,8 +192,8 @@ void main() {
await db.into(db.pureDefaults).insert( await db.into(db.pureDefaults).insert(
PureDefaultsCompanion.insert(txt: Value(MyCustomObject('foo')))); PureDefaultsCompanion.insert(txt: Value(MyCustomObject('foo'))));
verify(executor verify(executor.runInsert(
.runInsert('INSERT INTO pure_defaults ("insert") VALUES (?)', ['foo'])); 'INSERT INTO "pure_defaults" ("insert") VALUES (?)', ['foo']));
}); });
test('can insert custom companions', () async { test('can insert custom companions', () async {
@ -204,7 +205,7 @@ void main() {
verify( verify(
executor.runInsert( executor.runInsert(
'INSERT INTO users (name, is_awesome, profile_picture, creation_time) ' 'INSERT INTO "users" ("name", "is_awesome", "profile_picture", "creation_time") '
'VALUES (?, 1, _custom_, ' 'VALUES (?, 1, _custom_, '
"CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER))", "CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER))",
['User name'], ['User name'],
@ -222,8 +223,8 @@ void main() {
); );
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(id) DO UPDATE SET content = ? || content', 'ON CONFLICT("id") DO UPDATE SET "content" = ? || "content"',
argThat(equals(['my content', 'important: '])), argThat(equals(['my content', 'important: '])),
)); ));
}); });
@ -238,9 +239,9 @@ void main() {
); );
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(id) DO UPDATE SET content = ? || content ' 'ON CONFLICT("id") DO UPDATE SET "content" = ? || "content" '
'WHERE category = ?', 'WHERE "category" = ?',
argThat(equals(['my content', 'important: ', 1])), argThat(equals(['my content', 'important: ', 1])),
)); ));
}); });
@ -268,9 +269,9 @@ void main() {
])); ]));
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(id) DO UPDATE SET content = ? || content ' 'ON CONFLICT("id") DO UPDATE SET "content" = ? || "content" '
'ON CONFLICT(content) DO UPDATE SET content = ? || content', 'ON CONFLICT("content") DO UPDATE SET "content" = ? || "content"',
argThat(equals(['my content', 'important: ', 'second: '])), argThat(equals(['my content', 'important: ', 'second: '])),
)); ));
}, },
@ -296,11 +297,11 @@ void main() {
])); ]));
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(id) DO UPDATE SET content = ? || content ' 'ON CONFLICT("id") DO UPDATE SET "content" = ? || "content" '
'WHERE category = ? ' 'WHERE "category" = ? '
'ON CONFLICT(content) DO UPDATE SET content = ? || content ' 'ON CONFLICT("content") DO UPDATE SET "content" = ? || "content" '
'WHERE category = ?', 'WHERE "category" = ?',
argThat(equals(['my content', 'important: ', 1, 'second: ', 1])), argThat(equals(['my content', 'important: ', 1, 'second: ', 1])),
)); ));
}, },
@ -316,8 +317,8 @@ void main() {
); );
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(content) DO UPDATE SET content = ?', 'ON CONFLICT("content") DO UPDATE SET "content" = ?',
argThat(equals(['my content', 'changed'])), argThat(equals(['my content', 'changed'])),
)); ));
}); });
@ -332,9 +333,9 @@ void main() {
); );
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(content) DO UPDATE SET content = ? ' 'ON CONFLICT("content") DO UPDATE SET "content" = ? '
'WHERE content = title', 'WHERE "content" = "title"',
argThat(equals(['my content', 'changed'])), argThat(equals(['my content', 'changed'])),
)); ));
}); });
@ -346,8 +347,8 @@ void main() {
TodosTableCompanion.insert(content: 'content', id: const Value(3))); TodosTableCompanion.insert(content: 'content', id: const Value(3)));
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (id, content) VALUES (?, ?) ' 'INSERT INTO "todos" ("id", "content") VALUES (?, ?) '
'ON CONFLICT(id) DO UPDATE SET id = ?, content = ?', 'ON CONFLICT("id") DO UPDATE SET "id" = ?, "content" = ?',
[3, 'content', 3, 'content'], [3, 'content', 3, 'content'],
)); ));
expect(id, 3); expect(id, 3);
@ -364,9 +365,9 @@ void main() {
); );
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(id) DO UPDATE ' 'ON CONFLICT("id") DO UPDATE '
'SET content = todos.content || excluded.content', 'SET "content" = "todos"."content" || "excluded"."content"',
['content'], ['content'],
)); ));
}); });
@ -382,10 +383,10 @@ void main() {
); );
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO todos (content) VALUES (?) ' 'INSERT INTO "todos" ("content") VALUES (?) '
'ON CONFLICT(id) DO UPDATE ' 'ON CONFLICT("id") DO UPDATE '
'SET content = todos.content || excluded.content ' 'SET "content" = "todos"."content" || "excluded"."content" '
'WHERE todos.title = excluded.title', 'WHERE "todos"."title" = "excluded"."title"',
['content'], ['content'],
)); ));
}); });
@ -397,7 +398,7 @@ void main() {
)); ));
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO categories ("desc", priority) VALUES (?, ?)', 'INSERT INTO "categories" ("desc", "priority") VALUES (?, ?)',
['description', 1], ['description', 1],
)); ));
}); });
@ -420,7 +421,7 @@ void main() {
)); ));
verify(executor.runSelect( verify(executor.runSelect(
'INSERT INTO categories ("desc", priority) VALUES (?, ?) RETURNING *', 'INSERT INTO "categories" ("desc", "priority") VALUES (?, ?) RETURNING *',
['description', 1], ['description', 1],
)); ));
}); });
@ -432,7 +433,7 @@ void main() {
.insert(CategoriesCompanion.insert(description: 'description')); .insert(CategoriesCompanion.insert(description: 'description'));
verify(executor.runInsert( verify(executor.runInsert(
'INSERT INTO categories ("desc") VALUES (?)', ['description'])); 'INSERT INTO "categories" ("desc") VALUES (?)', ['description']));
}); });
test('insertOne', () async { test('insertOne', () async {
@ -441,7 +442,7 @@ void main() {
mode: InsertMode.insertOrReplace); mode: InsertMode.insertOrReplace);
verify(executor.runInsert( verify(executor.runInsert(
'INSERT OR REPLACE INTO categories ("desc") VALUES (?)', 'INSERT OR REPLACE INTO "categories" ("desc") VALUES (?)',
['description'])); ['description']));
}); });
@ -470,7 +471,7 @@ void main() {
); );
verify(executor.runSelect( verify(executor.runSelect(
'INSERT INTO categories ("desc") VALUES (?) RETURNING *', 'INSERT INTO "categories" ("desc") VALUES (?) RETURNING *',
['description'], ['description'],
)); ));
}); });

View File

@ -23,12 +23,12 @@ void main() {
]).get(); ]).get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT t.id AS "t.id", t.title AS "t.title", ' 'SELECT "t"."id" AS "t.id", "t"."title" AS "t.title", '
't.content AS "t.content", t.target_date AS "t.target_date", ' '"t"."content" AS "t.content", "t"."target_date" AS "t.target_date", '
't.category AS "t.category", c.id AS "c.id", c."desc" AS "c.desc", ' '"t"."category" AS "t.category", "c"."id" AS "c.id", "c"."desc" AS "c.desc", '
'c.priority AS "c.priority", ' '"c"."priority" AS "c.priority", '
'c.description_in_upper_case AS "c.description_in_upper_case" ' '"c"."description_in_upper_case" AS "c.description_in_upper_case" '
'FROM todos t LEFT OUTER JOIN categories c ON c.id = t.category;', 'FROM "todos" "t" LEFT OUTER JOIN "categories" "c" ON "c"."id" = "t"."category";',
argThat(isEmpty))); argThat(isEmpty)));
}); });
@ -126,7 +126,7 @@ void main() {
[innerJoin(categories, categories.id.equalsExp(todos.category))]).get(); [innerJoin(categories, categories.id.equalsExp(todos.category))]).get();
verify(executor.runSelect( verify(executor.runSelect(
argThat(contains('WHERE t.id < ? ORDER BY t.title ASC')), [3])); argThat(contains('WHERE "t"."id" < ? ORDER BY "t"."title" ASC')), [3]));
}); });
test('limit clause is kept', () async { test('limit clause is kept', () async {
@ -189,8 +189,8 @@ void main() {
await query.get(); await query.get();
verify(executor verify(executor.runSelect(
.runSelect(argThat(contains('WHERE t.id < ? AND c.id >= ?')), [5, 10])); argThat(contains('WHERE "t"."id" < ? AND "c"."id" >= ?')), [5, 10]));
}); });
test('supports custom columns and results', () async { test('supports custom columns and results', () async {
@ -214,10 +214,10 @@ void main() {
final result = await query.getSingle(); final result = await query.getSingle();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT c.id AS "c.id", c."desc" AS "c.desc", ' 'SELECT "c"."id" AS "c.id", "c"."desc" AS "c.desc", '
'c.priority AS "c.priority", c.description_in_upper_case AS ' '"c"."priority" AS "c.priority", "c"."description_in_upper_case" AS '
'"c.description_in_upper_case", LENGTH(c."desc") AS "c4" ' '"c.description_in_upper_case", LENGTH("c"."desc") AS "c4" '
'FROM categories c;', 'FROM "categories" "c";',
[], [],
)); ));
@ -263,11 +263,11 @@ void main() {
final result = await query.getSingle(); final result = await query.getSingle();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT c.id AS "c.id", c."desc" AS "c.desc", c.priority AS "c.priority"' 'SELECT "c"."id" AS "c.id", "c"."desc" AS "c.desc", "c"."priority" AS "c.priority"'
', c.description_in_upper_case AS "c.description_in_upper_case", ' ', "c"."description_in_upper_case" AS "c.description_in_upper_case", '
'LENGTH(c."desc") AS "c4" ' 'LENGTH("c"."desc") AS "c4" '
'FROM categories c ' 'FROM "categories" "c" '
'INNER JOIN todos t ON c.id = t.category;', 'INNER JOIN "todos" "t" ON "c"."id" = "t"."category";',
[], [],
)); ));
@ -319,12 +319,12 @@ void main() {
final result = await query.getSingle(); final result = await query.getSingle();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT c.id AS "c.id", c."desc" AS "c.desc", ' 'SELECT "c"."id" AS "c.id", "c"."desc" AS "c.desc", '
'c.priority AS "c.priority", ' '"c"."priority" AS "c.priority", '
'c.description_in_upper_case AS "c.description_in_upper_case", ' '"c"."description_in_upper_case" AS "c.description_in_upper_case", '
'COUNT(t.id) AS "c4" ' 'COUNT("t"."id") AS "c4" '
'FROM categories c INNER JOIN todos t ON t.category = c.id ' 'FROM "categories" "c" INNER JOIN "todos" "t" ON "t"."category" = "c"."id" '
'GROUP BY c.id HAVING COUNT(t.id) >= ?;', 'GROUP BY "c"."id" HAVING COUNT("t"."id") >= ?;',
[10])); [10]));
expect(result.readTableOrNull(todos), isNull); expect(result.readTableOrNull(todos), isNull);
@ -356,8 +356,8 @@ void main() {
final row = await query.getSingle(); final row = await query.getSingle();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT AVG(LENGTH(todos.content)) AS "c0", ' 'SELECT AVG(LENGTH("todos"."content")) AS "c0", '
'MAX(LENGTH(todos.content)) AS "c1" FROM todos;', 'MAX(LENGTH("todos"."content")) AS "c1" FROM "todos";',
[])); []));
expect(row.read(avgLength), 3.0); expect(row.read(avgLength), 3.0);
@ -392,9 +392,9 @@ void main() {
final result = await query.getSingle(); final result = await query.getSingle();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT categories.id AS "categories.id", COUNT(todos.id) AS "c1" ' 'SELECT "categories"."id" AS "categories.id", COUNT("todos"."id") AS "c1" '
'FROM categories INNER JOIN todos ON todos.category = categories.id ' 'FROM "categories" INNER JOIN "todos" ON "todos"."category" = "categories"."id" '
'GROUP BY categories.id;', 'GROUP BY "categories"."id";',
[])); []));
expect(result.read(categories.id), equals(2)); expect(result.read(categories.id), equals(2));
@ -428,9 +428,9 @@ void main() {
final result = await query.getSingle(); final result = await query.getSingle();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT categories.id AS "categories.id", COUNT(todos.id) AS "c1" ' 'SELECT "categories"."id" AS "categories.id", COUNT("todos"."id") AS "c1" '
'FROM categories INNER JOIN todos ON todos.category = categories.id ' 'FROM "categories" INNER JOIN "todos" ON "todos"."category" = "categories"."id" '
'GROUP BY categories.id;', 'GROUP BY "categories"."id";',
[])); []));
expect(result.read(categories.id), equals(2)); expect(result.read(categories.id), equals(2));

View File

@ -19,7 +19,7 @@ void main() {
query.orderBy([(tbl) => OrderingTerm(expression: tbl.name)]); query.orderBy([(tbl) => OrderingTerm(expression: tbl.name)]);
await query.get(); await query.get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC;', 'SELECT * FROM "users" ORDER BY "name" ASC;',
argThat(isEmpty), argThat(isEmpty),
)); ));
}); });
@ -34,7 +34,7 @@ void main() {
]); ]);
await query.get(); await query.get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC NULLS LAST;', 'SELECT * FROM "users" ORDER BY "name" ASC NULLS LAST;',
argThat(isEmpty), argThat(isEmpty),
)); ));
}); });
@ -49,7 +49,7 @@ void main() {
]); ]);
await query.get(); await query.get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC NULLS FIRST;', 'SELECT * FROM "users" ORDER BY "name" ASC NULLS FIRST;',
argThat(isEmpty), argThat(isEmpty),
)); ));
}); });
@ -71,7 +71,7 @@ void main() {
]); ]);
await query.get(); await query.get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC NULLS FIRST, creation_time ASC, profile_picture ASC NULLS LAST;', 'SELECT * FROM "users" ORDER BY "name" ASC NULLS FIRST, "creation_time" ASC, "profile_picture" ASC NULLS LAST;',
argThat(isEmpty), argThat(isEmpty),
)); ));
}); });
@ -79,10 +79,10 @@ void main() {
test('works with helper factories', () { test('works with helper factories', () {
final table = db.users; final table = db.users;
expect(OrderingTerm.asc(table.id), generates('id ASC')); expect(OrderingTerm.asc(table.id), generates('"id" ASC'));
expect(OrderingTerm.asc(table.id, nulls: NullsOrder.last), expect(OrderingTerm.asc(table.id, nulls: NullsOrder.last),
generates('id ASC NULLS LAST')); generates('"id" ASC NULLS LAST'));
expect(OrderingTerm.desc(table.id, nulls: NullsOrder.first), expect(OrderingTerm.desc(table.id, nulls: NullsOrder.first),
generates('id DESC NULLS FIRST')); generates('"id" DESC NULLS FIRST'));
}); });
} }

View File

@ -20,40 +20,40 @@ void main() {
// should create todos, categories, users and shared_todos table // should create todos, categories, users and shared_todos table
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE TABLE IF NOT EXISTS todos ' 'CREATE TABLE IF NOT EXISTS "todos" '
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, title TEXT NULL, ' '("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "title" TEXT NULL, '
'content TEXT NOT NULL, target_date INTEGER NULL UNIQUE, ' '"content" TEXT NOT NULL, "target_date" INTEGER NULL UNIQUE, '
'category INTEGER NULL REFERENCES categories (id), ' '"category" INTEGER NULL REFERENCES "categories" ("id"), '
'UNIQUE (title, category), UNIQUE (title, target_date));', 'UNIQUE ("title", "category"), UNIQUE ("title", "target_date"));',
[])); []));
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE TABLE IF NOT EXISTS categories ' 'CREATE TABLE IF NOT EXISTS "categories" '
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' '("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
'"desc" TEXT NOT NULL UNIQUE, ' '"desc" TEXT NOT NULL UNIQUE, '
'priority INTEGER NOT NULL DEFAULT 0, ' '"priority" INTEGER NOT NULL DEFAULT 0, '
'description_in_upper_case TEXT NOT NULL GENERATED ALWAYS AS ' '"description_in_upper_case" TEXT NOT NULL GENERATED ALWAYS AS '
'(UPPER("desc")) VIRTUAL' '(UPPER("desc")) VIRTUAL'
');', ');',
[])); []));
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE TABLE IF NOT EXISTS users (' 'CREATE TABLE IF NOT EXISTS "users" ('
'id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' '"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
'name TEXT NOT NULL UNIQUE, ' '"name" TEXT NOT NULL UNIQUE, '
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome IN (0, 1)), ' '"is_awesome" INTEGER NOT NULL DEFAULT 1 CHECK ("is_awesome" IN (0, 1)), '
'profile_picture BLOB NOT NULL, ' '"profile_picture" BLOB NOT NULL, '
'creation_time INTEGER NOT NULL ' '"creation_time" INTEGER NOT NULL '
"DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)) " "DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)) "
'CHECK(creation_time > -631152000)' 'CHECK("creation_time" > -631152000)'
');', ');',
[])); []));
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE TABLE IF NOT EXISTS shared_todos (' 'CREATE TABLE IF NOT EXISTS "shared_todos" ('
'todo INTEGER NOT NULL, ' '"todo" INTEGER NOT NULL, '
'user INTEGER NOT NULL, ' '"user" INTEGER NOT NULL, '
'PRIMARY KEY (todo, user), ' 'PRIMARY KEY ("todo", "user"), '
'FOREIGN KEY (todo) REFERENCES todos(id), ' 'FOREIGN KEY (todo) REFERENCES todos(id), '
'FOREIGN KEY (user) REFERENCES users(id)' 'FOREIGN KEY (user) REFERENCES users(id)'
');', ');',
@ -61,33 +61,33 @@ void main() {
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE TABLE IF NOT EXISTS ' 'CREATE TABLE IF NOT EXISTS '
'table_without_p_k (' '"table_without_p_k" ('
'not_really_an_id INTEGER NOT NULL, ' '"not_really_an_id" INTEGER NOT NULL, '
'some_float REAL NOT NULL, ' '"some_float" REAL NOT NULL, '
'web_safe_int INTEGER NULL, ' '"web_safe_int" INTEGER NULL, '
'custom TEXT NOT NULL' '"custom" TEXT NOT NULL'
');', ');',
[])); []));
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE VIEW IF NOT EXISTS category_todo_count_view ' 'CREATE VIEW IF NOT EXISTS "category_todo_count_view" '
'(description, item_count) AS SELECT ' '("description", "item_count") AS SELECT '
't1."desc" || \'!\' AS "description", ' '"t1"."desc" || \'!\' AS "description", '
'COUNT(t0.id) AS "item_count" ' 'COUNT("t0"."id") AS "item_count" '
'FROM categories t1 ' 'FROM "categories" "t1" '
'INNER JOIN todos t0 ' 'INNER JOIN "todos" "t0" '
'ON t0.category = t1.id ' 'ON "t0"."category" = "t1"."id" '
'GROUP BY t1.id', 'GROUP BY "t1"."id"',
[])); []));
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE VIEW IF NOT EXISTS todo_with_category_view ' 'CREATE VIEW IF NOT EXISTS "todo_with_category_view" '
'(title, "desc") AS SELECT ' '("title", "desc") AS SELECT '
't0.title AS "title", ' '"t0"."title" AS "title", '
't1."desc" AS "desc" ' '"t1"."desc" AS "desc" '
'FROM todos t0 ' 'FROM "todos" "t0" '
'INNER JOIN categories t1 ' 'INNER JOIN "categories" "t1" '
'ON t1.id = t0.category', 'ON "t1"."id" = "t0"."category"',
[])); []));
}); });
@ -95,14 +95,14 @@ void main() {
await db.createMigrator().createTable(db.users); await db.createMigrator().createTable(db.users);
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE TABLE IF NOT EXISTS users ' 'CREATE TABLE IF NOT EXISTS "users" '
'(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' '("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
'name TEXT NOT NULL UNIQUE, ' '"name" TEXT NOT NULL UNIQUE, '
'is_awesome INTEGER NOT NULL DEFAULT 1 CHECK (is_awesome IN (0, 1)), ' '"is_awesome" INTEGER NOT NULL DEFAULT 1 CHECK ("is_awesome" IN (0, 1)), '
'profile_picture BLOB NOT NULL, ' '"profile_picture" BLOB NOT NULL, '
'creation_time INTEGER NOT NULL ' '"creation_time" INTEGER NOT NULL '
"DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)) " "DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)) "
'CHECK(creation_time > -631152000)' 'CHECK("creation_time" > -631152000)'
');', ');',
[])); []));
}); });
@ -111,21 +111,21 @@ void main() {
await db.createMigrator().create(db.categoryTodoCountView); await db.createMigrator().create(db.categoryTodoCountView);
verify(mockExecutor.runCustom( verify(mockExecutor.runCustom(
'CREATE VIEW IF NOT EXISTS category_todo_count_view ' 'CREATE VIEW IF NOT EXISTS "category_todo_count_view" '
'(description, item_count) AS SELECT ' '("description", "item_count") AS SELECT '
't1."desc" || \'!\' AS "description", ' '"t1"."desc" || \'!\' AS "description", '
'COUNT(t0.id) AS "item_count" ' 'COUNT("t0"."id") AS "item_count" '
'FROM categories t1 ' 'FROM "categories" "t1" '
'INNER JOIN todos t0 ' 'INNER JOIN "todos" "t0" '
'ON t0.category = t1.id ' 'ON "t0"."category" = "t1"."id" '
'GROUP BY t1.id', 'GROUP BY "t1"."id"',
[])); []));
}); });
test('drops tables', () async { test('drops tables', () async {
await db.createMigrator().deleteTable('users'); await db.createMigrator().deleteTable('users');
verify(mockExecutor.runCustom('DROP TABLE IF EXISTS users;')); verify(mockExecutor.runCustom('DROP TABLE IF EXISTS "users";'));
}); });
test('drops indices', () async { test('drops indices', () async {
@ -137,15 +137,15 @@ void main() {
test('drops triggers', () async { test('drops triggers', () async {
await db.createMigrator().drop(Trigger('foo', 'my_trigger')); await db.createMigrator().drop(Trigger('foo', 'my_trigger'));
verify(mockExecutor.runCustom('DROP TRIGGER IF EXISTS my_trigger;')); verify(mockExecutor.runCustom('DROP TRIGGER IF EXISTS "my_trigger";'));
}); });
test('adds columns', () async { test('adds columns', () async {
await db.createMigrator().addColumn(db.users, db.users.isAwesome); await db.createMigrator().addColumn(db.users, db.users.isAwesome);
verify(mockExecutor.runCustom('ALTER TABLE users ADD COLUMN ' verify(mockExecutor.runCustom('ALTER TABLE "users" ADD COLUMN '
'is_awesome INTEGER NOT NULL DEFAULT 1 ' '"is_awesome" INTEGER NOT NULL DEFAULT 1 '
'CHECK (is_awesome IN (0, 1));')); 'CHECK ("is_awesome" IN (0, 1));'));
}); });
test('renames columns', () async { test('renames columns', () async {
@ -154,7 +154,7 @@ void main() {
.renameColumn(db.users, 'my name', db.users.name); .renameColumn(db.users, 'my name', db.users.name);
verify(mockExecutor verify(mockExecutor
.runCustom('ALTER TABLE users RENAME COLUMN "my name" TO name;')); .runCustom('ALTER TABLE "users" RENAME COLUMN "my name" TO "name";'));
}); });
}); });
@ -207,7 +207,7 @@ void main() {
// This should not attempt to generate a parameter (`?`) // This should not attempt to generate a parameter (`?`)
// https://github.com/simolus3/drift/discussions/1936 // https://github.com/simolus3/drift/discussions/1936
verify(executor.runCustom(argThat(contains('CHECK(foo < 3)')), [])); verify(executor.runCustom(argThat(contains('CHECK("foo" < 3)')), []));
}); });
} }

View File

@ -34,26 +34,26 @@ void main() {
test('for simple statements', () async { test('for simple statements', () async {
await db.select(db.users, distinct: true).get(); await db.select(db.users, distinct: true).get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT DISTINCT * FROM users;', argThat(isEmpty))); 'SELECT DISTINCT * FROM "users";', argThat(isEmpty)));
}); });
test('with limit statements', () async { test('with limit statements', () async {
await (db.select(db.users)..limit(10, offset: 0)).get(); await (db.select(db.users)..limit(10, offset: 0)).get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users LIMIT 10 OFFSET 0;', argThat(isEmpty))); 'SELECT * FROM "users" LIMIT 10 OFFSET 0;', argThat(isEmpty)));
}); });
test('with simple limits', () async { test('with simple limits', () async {
await (db.select(db.users)..limit(10)).get(); await (db.select(db.users)..limit(10)).get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users LIMIT 10;', argThat(isEmpty))); 'SELECT * FROM "users" LIMIT 10;', argThat(isEmpty)));
}); });
test('with like expressions', () async { test('with like expressions', () async {
await (db.select(db.users)..where((u) => u.name.like('Dash%'))).get(); await (db.select(db.users)..where((u) => u.name.like('Dash%'))).get();
verify(executor verify(executor
.runSelect('SELECT * FROM users WHERE name LIKE ?;', ['Dash%'])); .runSelect('SELECT * FROM "users" WHERE "name" LIKE ?;', ['Dash%']));
}); });
test('with order-by clauses', () async { test('with order-by clauses', () async {
@ -65,8 +65,8 @@ void main() {
.get(); .get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users ORDER BY ' 'SELECT * FROM "users" ORDER BY '
'is_awesome DESC, id ASC;', '"is_awesome" DESC, "id" ASC;',
argThat(isEmpty))); argThat(isEmpty)));
}); });
@ -75,7 +75,7 @@ void main() {
.get(); .get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users ORDER BY random() ASC;', argThat(isEmpty))); 'SELECT * FROM "users" ORDER BY random() ASC;', argThat(isEmpty)));
}); });
test('with complex predicates', () async { test('with complex predicates', () async {
@ -85,7 +85,7 @@ void main() {
.get(); .get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users WHERE NOT (name = ?) AND id > ?;', 'SELECT * FROM "users" WHERE NOT ("name" = ?) AND "id" > ?;',
['Dash', 12])); ['Dash', 12]));
}); });
@ -93,7 +93,7 @@ void main() {
await (db.select(db.users)..where((u) => u.isAwesome)).get(); await (db.select(db.users)..where((u) => u.isAwesome)).get();
verify(executor.runSelect( verify(executor.runSelect(
'SELECT * FROM users WHERE is_awesome;', argThat(isEmpty))); 'SELECT * FROM "users" WHERE "is_awesome";', argThat(isEmpty)));
}); });
test('with aliased tables', () async { test('with aliased tables', () async {
@ -102,13 +102,14 @@ void main() {
..where((u) => u.id.isSmallerThan(const Constant(5)))) ..where((u) => u.id.isSmallerThan(const Constant(5))))
.get(); .get();
verify(executor.runSelect('SELECT * FROM users u WHERE id < 5;', [])); verify(
executor.runSelect('SELECT * FROM "users" "u" WHERE "id" < 5;', []));
}); });
}); });
group('SELECT results are parsed', () { group('SELECT results are parsed', () {
test('when all fields are non-null', () { test('when all fields are non-null', () {
when(executor.runSelect('SELECT * FROM todos;', any)) when(executor.runSelect('SELECT * FROM "todos";', any))
.thenAnswer((_) => Future.value([_dataOfTodoEntry])); .thenAnswer((_) => Future.value([_dataOfTodoEntry]));
expect(db.select(db.todosTable).get(), completion([_todoEntry])); expect(db.select(db.todosTable).get(), completion([_todoEntry]));
@ -130,7 +131,7 @@ void main() {
category: null, category: null,
); );
when(executor.runSelect('SELECT * FROM todos;', any)) when(executor.runSelect('SELECT * FROM "todos";', any))
.thenAnswer((_) => Future.value(data)); .thenAnswer((_) => Future.value(data));
expect(db.select(db.todosTable).get(), completion([resolved])); expect(db.select(db.todosTable).get(), completion([resolved]));
@ -139,13 +140,13 @@ void main() {
group('queries for a single row', () { group('queries for a single row', () {
test('get once', () { test('get once', () {
when(executor.runSelect('SELECT * FROM todos;', any)) when(executor.runSelect('SELECT * FROM "todos";', any))
.thenAnswer((_) => Future.value([_dataOfTodoEntry])); .thenAnswer((_) => Future.value([_dataOfTodoEntry]));
expect(db.select(db.todosTable).getSingle(), completion(_todoEntry)); expect(db.select(db.todosTable).getSingle(), completion(_todoEntry));
}); });
test('get once without rows', () { test('get once without rows', () {
when(executor.runSelect('SELECT * FROM todos;', any)) when(executor.runSelect('SELECT * FROM "todos";', any))
.thenAnswer((_) => Future.value([])); .thenAnswer((_) => Future.value([]));
expect(db.select(db.todosTable).getSingle(), throwsA(anything)); expect(db.select(db.todosTable).getSingle(), throwsA(anything));
@ -160,7 +161,7 @@ void main() {
]; ];
var currentRow = 0; var currentRow = 0;
when(executor.runSelect('SELECT * FROM todos;', any)).thenAnswer((_) { when(executor.runSelect('SELECT * FROM "todos";', any)).thenAnswer((_) {
return Future.value(resultRows[currentRow++]); return Future.value(resultRows[currentRow++]);
}); });

View File

@ -28,7 +28,8 @@ void main() {
)); ));
verify(executor.runUpdate( verify(executor.runUpdate(
'UPDATE todos SET title = ?, category = ?;', ['Updated title', 3])); 'UPDATE "todos" SET "title" = ?, "category" = ?;',
['Updated title', 3]));
}); });
test('with a WHERE clause', () async { test('with a WHERE clause', () async {
@ -37,7 +38,8 @@ void main() {
.write(const TodosTableCompanion(title: Value('Changed title'))); .write(const TodosTableCompanion(title: Value('Changed title')));
verify(executor.runUpdate( verify(executor.runUpdate(
'UPDATE todos SET title = ? WHERE id < ?;', ['Changed title', 50])); 'UPDATE "todos" SET "title" = ? WHERE "id" < ?;',
['Changed title', 50]));
}); });
test('with escaped column names', () async { test('with escaped column names', () async {
@ -46,7 +48,7 @@ void main() {
.write(PureDefaultsCompanion(txt: Value(MyCustomObject('foo')))); .write(PureDefaultsCompanion(txt: Value(MyCustomObject('foo'))));
verify(executor verify(executor
.runUpdate('UPDATE pure_defaults SET "insert" = ?;', ['foo'])); .runUpdate('UPDATE "pure_defaults" SET "insert" = ?;', ['foo']));
}); });
}); });
@ -60,8 +62,8 @@ void main() {
)); ));
verify(executor.runUpdate( verify(executor.runUpdate(
'UPDATE todos SET title = ?, content = ?, ' 'UPDATE "todos" SET "title" = ?, "content" = ?, '
'target_date = ?, category = ? WHERE id = ?;', '"target_date" = ?, "category" = ? WHERE "id" = ?;',
['Title', 'Updated content', null, null, 3])); ['Title', 'Updated content', null, null, 3]));
}); });
@ -75,9 +77,9 @@ void main() {
); );
verify(executor.runUpdate( verify(executor.runUpdate(
'UPDATE users SET name = ?, profile_picture = ?, is_awesome = 1, ' 'UPDATE "users" SET "name" = ?, "profile_picture" = ?, "is_awesome" = 1, '
'creation_time = CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)' '"creation_time" = CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'
' WHERE id = ?;', ' WHERE "id" = ?;',
['Hummingbird', Uint8List(0), 3])); ['Hummingbird', Uint8List(0), 3]));
}); });
}); });
@ -121,22 +123,22 @@ void main() {
)); ));
verify(executor.runUpdate( verify(executor.runUpdate(
'UPDATE todos SET content = content, target_date = target_date + ? ' 'UPDATE "todos" SET "content" = "content", "target_date" = "target_date" + ? '
'WHERE id = ?;', 'WHERE "id" = ?;',
argThat(equals([86400, 4])), argThat(equals([86400, 4])),
)); ));
}); });
group('custom updates', () { group('custom updates', () {
test('execute the correct sql', () async { test('execute the correct sql', () async {
await db.customUpdate('DELETE FROM users'); await db.customUpdate('DELETE FROM "users"');
verify(executor.runUpdate('DELETE FROM users', [])); verify(executor.runUpdate('DELETE FROM "users"', []));
}); });
test('map the variables correctly', () async { test('map the variables correctly', () async {
await db.customUpdate( await db.customUpdate(
'DELETE FROM users WHERE name = ? AND birthdate < ?', 'DELETE FROM "users" WHERE "name" = ? AND "birthdate" < ?',
variables: [ variables: [
Variable.withString('Name'), Variable.withString('Name'),
Variable.withDateTime( Variable.withDateTime(
@ -144,7 +146,7 @@ void main() {
]); ]);
verify(executor.runUpdate( verify(executor.runUpdate(
'DELETE FROM users WHERE name = ? AND birthdate < ?', 'DELETE FROM "users" WHERE "name" = ? AND "birthdate" < ?',
['Name', 1551297563])); ['Name', 1551297563]));
}); });
@ -166,7 +168,7 @@ void main() {
test('update()', () async { test('update()', () async {
await db.users.update().write(const UsersCompanion(id: Value(3))); await db.users.update().write(const UsersCompanion(id: Value(3)));
verify(executor.runUpdate('UPDATE users SET id = ?;', [3])); verify(executor.runUpdate('UPDATE "users" SET "id" = ?;', [3]));
}); });
test('replace', () async { test('replace', () async {
@ -174,7 +176,7 @@ void main() {
id: Value(3), description: Value('new name'))); id: Value(3), description: Value('new name')));
verify(executor.runUpdate( verify(executor.runUpdate(
'UPDATE categories SET "desc" = ?, priority = 0 WHERE id = ?;', 'UPDATE "categories" SET "desc" = ?, "priority" = 0 WHERE "id" = ?;',
['new name', 3])); ['new name', 3]));
}); });
}); });
@ -195,8 +197,8 @@ void main() {
.update() .update()
.writeReturning(const CategoriesCompanion(description: Value('test'))); .writeReturning(const CategoriesCompanion(description: Value('test')));
verify(executor verify(executor.runSelect(
.runSelect('UPDATE categories SET "desc" = ? RETURNING *;', ['test'])); 'UPDATE "categories" SET "desc" = ? RETURNING *;', ['test']));
verify(streamQueries.handleTableUpdates( verify(streamQueries.handleTableUpdates(
{TableUpdate.onTable(db.categories, kind: UpdateKind.update)})); {TableUpdate.onTable(db.categories, kind: UpdateKind.update)}));

View File

@ -15,8 +15,8 @@ void main() {
nonNull.writeColumnDefinition(nonNullQuery); nonNull.writeColumnDefinition(nonNullQuery);
nullable.writeColumnDefinition(nullableQuery); nullable.writeColumnDefinition(nullableQuery);
expect(nullableQuery.sql, equals('name INTEGER NULL')); expect(nullableQuery.sql, equals('"name" INTEGER NULL'));
expect(nonNullQuery.sql, equals('name INTEGER NOT NULL')); expect(nonNullQuery.sql, equals('"name" INTEGER NOT NULL'));
}); });
group('mapping datetime values', () { group('mapping datetime values', () {

View File

@ -16,8 +16,8 @@ void main() {
final context = GenerationContext.fromDb(TodoDb()); final context = GenerationContext.fromDb(TodoDb());
column.writeColumnDefinition(context); column.writeColumnDefinition(context);
expect( expect(context.sql,
context.sql, equals('foo INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT')); equals('"foo" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT'));
}); });
test('int column writes PRIMARY KEY constraint', () { test('int column writes PRIMARY KEY constraint', () {
@ -27,6 +27,6 @@ void main() {
final context = GenerationContext.fromDb(TodoDb()); final context = GenerationContext.fromDb(TodoDb());
column.writeColumnDefinition(context); column.writeColumnDefinition(context);
expect(context.sql, equals('foo INTEGER NOT NULL PRIMARY KEY')); expect(context.sql, equals('"foo" INTEGER NOT NULL PRIMARY KEY'));
}); });
} }

View File

@ -495,7 +495,7 @@ class $TodosTableTable extends TodosTable
'category', aliasedName, true, 'category', aliasedName, true,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: 'REFERENCES categories (id)'); defaultConstraints: 'REFERENCES "categories" ("id")');
@override @override
List<GeneratedColumn> get $columns => List<GeneratedColumn> get $columns =>
[id, title, content, targetDate, category]; [id, title, content, targetDate, category];
@ -777,7 +777,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
'is_awesome', aliasedName, false, 'is_awesome', aliasedName, false,
type: DriftSqlType.bool, type: DriftSqlType.bool,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: 'CHECK (is_awesome IN (0, 1))', defaultConstraints: 'CHECK ("is_awesome" IN (0, 1))',
defaultValue: const Constant(true)); defaultValue: const Constant(true));
final VerificationMeta _profilePictureMeta = final VerificationMeta _profilePictureMeta =
const VerificationMeta('profilePicture'); const VerificationMeta('profilePicture');

View File

@ -7,32 +7,32 @@ import '../generated/custom_tables.dart';
import '../test_utils/test_utils.dart'; import '../test_utils/test_utils.dart';
const _createNoIds = const _createNoIds =
'CREATE TABLE IF NOT EXISTS no_ids (payload BLOB NOT NULL PRIMARY KEY) ' 'CREATE TABLE IF NOT EXISTS "no_ids" ("payload" BLOB NOT NULL PRIMARY KEY) '
'WITHOUT ROWID;'; 'WITHOUT ROWID;';
const _createWithDefaults = 'CREATE TABLE IF NOT EXISTS with_defaults (' const _createWithDefaults = 'CREATE TABLE IF NOT EXISTS "with_defaults" ('
"a TEXT DEFAULT 'something', b INTEGER UNIQUE);"; "\"a\" TEXT DEFAULT 'something', \"b\" INTEGER UNIQUE);";
const _createWithConstraints = 'CREATE TABLE IF NOT EXISTS with_constraints (' const _createWithConstraints = 'CREATE TABLE IF NOT EXISTS "with_constraints" ('
'a TEXT, b INTEGER NOT NULL, c REAL, ' '"a" TEXT, "b" INTEGER NOT NULL, "c" REAL, '
'FOREIGN KEY (a, b) REFERENCES with_defaults (a, b)' 'FOREIGN KEY (a, b) REFERENCES with_defaults (a, b)'
');'; ');';
const _createConfig = 'CREATE TABLE IF NOT EXISTS config (' const _createConfig = 'CREATE TABLE IF NOT EXISTS "config" ('
'config_key TEXT not null primary key, ' '"config_key" TEXT not null primary key, '
'config_value TEXT, ' '"config_value" TEXT, '
'sync_state INTEGER, ' '"sync_state" INTEGER, '
'sync_state_implicit INTEGER) STRICT;'; '"sync_state_implicit" INTEGER) STRICT;';
const _createMyTable = 'CREATE TABLE IF NOT EXISTS mytable (' const _createMyTable = 'CREATE TABLE IF NOT EXISTS "mytable" ('
'someid INTEGER NOT NULL, ' '"someid" INTEGER NOT NULL, '
'sometext TEXT, ' '"sometext" TEXT, '
'is_inserting INTEGER, ' '"is_inserting" INTEGER, '
'somedate TEXT, ' '"somedate" TEXT, '
'PRIMARY KEY (someid DESC)' 'PRIMARY KEY (someid DESC)'
');'; ');';
const _createEmail = 'CREATE VIRTUAL TABLE IF NOT EXISTS email USING ' const _createEmail = 'CREATE VIRTUAL TABLE IF NOT EXISTS "email" USING '
'fts5(sender, title, body);'; 'fts5(sender, title, body);';
const _createMyTrigger = const _createMyTrigger =
@ -98,7 +98,7 @@ void main() {
// regression test for #112: https://github.com/simolus3/drift/issues/112 // regression test for #112: https://github.com/simolus3/drift/issues/112
await db.into(db.mytable).insert(const MytableCompanion()); await db.into(db.mytable).insert(const MytableCompanion());
verify(mock.runInsert('INSERT INTO mytable DEFAULT VALUES', [])); verify(mock.runInsert('INSERT INTO "mytable" DEFAULT VALUES', []));
}); });
test('runs queries with arrays and Dart templates', () async { test('runs queries with arrays and Dart templates', () async {
@ -108,7 +108,7 @@ void main() {
verify(mock.runSelect( verify(mock.runSelect(
'SELECT * FROM config WHERE config_key IN (?1, ?2) ' 'SELECT * FROM config WHERE config_key IN (?1, ?2) '
'ORDER BY config_key ASC', 'ORDER BY "config_key" ASC',
['a', 'b'], ['a', 'b'],
)); ));
}); });
@ -122,8 +122,8 @@ void main() {
.readDynamic(predicate: (config) => config.configKey.equals('key')) .readDynamic(predicate: (config) => config.configKey.equals('key'))
.getSingle(); .getSingle();
verify( verify(mock
mock.runSelect('SELECT * FROM config WHERE config_key = ?1', ['key'])); .runSelect('SELECT * FROM config WHERE "config_key" = ?1', ['key']));
expect(parsed, const Config(configKey: 'key', configValue: 'value')); expect(parsed, const Config(configKey: 'key', configValue: 'value'));
}); });
@ -136,7 +136,7 @@ void main() {
test('columns use table names in queries with multiple tables', () async { test('columns use table names in queries with multiple tables', () async {
await db.multiple(predicate: (d, c) => d.a.equals('foo')).get(); await db.multiple(predicate: (d, c) => d.a.equals('foo')).get();
verify(mock.runSelect(argThat(contains('d.a = ?1')), any)); verify(mock.runSelect(argThat(contains('"d"."a" = ?1')), any));
}); });
test('order by-params are ignored by default', () async { test('order by-params are ignored by default', () async {
@ -241,7 +241,7 @@ void main() {
..where((tbl) => tbl.syncState.equalsValue(SyncType.synchronized))) ..where((tbl) => tbl.syncState.equalsValue(SyncType.synchronized)))
.getSingleOrNull(); .getSingleOrNull();
verify(mock.runSelect('SELECT * FROM config WHERE sync_state = ?;', verify(mock.runSelect('SELECT * FROM "config" WHERE "sync_state" = ?;',
[ConfigTable.$converter0.toSql(SyncType.synchronized)])); [ConfigTable.$converter0.toSql(SyncType.synchronized)]));
}); });
} }

View File

@ -53,7 +53,7 @@ void main() {
.map((row) => row.read<String>('sql')) .map((row) => row.read<String>('sql'))
.getSingle(); .getSingle();
expect(createStmt, contains('category INT')); expect(createStmt, contains('"category" INT'));
final item = await db.select(db.todosTable).getSingle(); final item = await db.select(db.todosTable).getSingle();
expect(item.category, 12); expect(item.category, 12);
@ -102,7 +102,7 @@ void main() {
expect( expect(
createStmt, createStmt,
allOf(contains('category INT'), isNot(contains('category_old'))), allOf(contains('"category" INT'), isNot(contains('category_old'))),
); );
final item = await db.select(db.todosTable).getSingle(); final item = await db.select(db.todosTable).getSingle();

View File

@ -1,7 +1,6 @@
import 'package:charcode/ascii.dart'; import 'package:charcode/ascii.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart' show SqlDialect; import 'package:drift/drift.dart' show SqlDialect;
import 'package:drift/sqlite_keywords.dart';
import 'package:drift_dev/moor_generator.dart'; import 'package:drift_dev/moor_generator.dart';
import 'package:drift_dev/src/analyzer/options.dart'; import 'package:drift_dev/src/analyzer/options.dart';
import 'package:drift_dev/src/utils/string_escaper.dart'; import 'package:drift_dev/src/utils/string_escaper.dart';
@ -75,13 +74,6 @@ class SqlWriter extends NodeSqlBuilder {
(f) => f.variable.resolvedIndex == target.resolvedIndex); (f) => f.variable.resolvedIndex == target.resolvedIndex);
} }
@override
void identifier(String identifier,
{bool spaceBefore = true, bool spaceAfter = true}) {
final escaped = escapeIfNeeded(identifier, options.effectiveDialect);
symbol(escaped, spaceBefore: spaceBefore, spaceAfter: spaceAfter);
}
void _writeMoorVariable(FoundVariable variable) { void _writeMoorVariable(FoundVariable variable) {
if (variable.isArray) { if (variable.isArray) {
_writeRawInSpaces('(\$${expandedName(variable)})'); _writeRawInSpaces('(\$${expandedName(variable)})');

View File

@ -1,4 +1,3 @@
import 'package:drift/sqlite_keywords.dart';
import 'package:sqlparser/sqlparser.dart'; import 'package:sqlparser/sqlparser.dart';
import '../../model/model.dart'; import '../../model/model.dart';
@ -32,8 +31,8 @@ String defaultConstraints(DriftColumn column) {
for (final feature in column.features) { for (final feature in column.features) {
if (feature is ResolvedDartForeignKeyReference) { if (feature is ResolvedDartForeignKeyReference) {
final tableName = escapeIfNeeded(feature.otherTable.sqlName); final tableName = '"${feature.otherTable.sqlName}"';
final columnName = escapeIfNeeded(feature.otherColumn.name.name); final columnName = '"${feature.otherColumn.name.name}"';
var constraint = 'REFERENCES $tableName ($columnName)'; var constraint = 'REFERENCES $tableName ($columnName)';
@ -55,7 +54,7 @@ String defaultConstraints(DriftColumn column) {
} }
if (column.type == DriftSqlType.bool) { if (column.type == DriftSqlType.bool) {
final name = escapeIfNeeded(column.name.name); final name = '"${column.name.name}"';
defaultConstraints.add('CHECK ($name IN (0, 1))'); defaultConstraints.add('CHECK ($name IN (0, 1))');
} }

View File

@ -402,7 +402,7 @@ class $TodoEntriesTable extends TodoEntries
'category', aliasedName, true, 'category', aliasedName, true,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: 'REFERENCES categories (id)'); defaultConstraints: 'REFERENCES "categories" ("id")');
final VerificationMeta _dueDateMeta = const VerificationMeta('dueDate'); final VerificationMeta _dueDateMeta = const VerificationMeta('dueDate');
@override @override
late final GeneratedColumn<DateTime> dueDate = GeneratedColumn<DateTime>( late final GeneratedColumn<DateTime> dueDate = GeneratedColumn<DateTime>(

View File

@ -200,7 +200,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
'next_user', aliasedName, true, 'next_user', aliasedName, true,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: 'REFERENCES users (id)'); defaultConstraints: 'REFERENCES "users" ("id")');
@override @override
List<GeneratedColumn> get $columns => [id, name, birthday, nextUser]; List<GeneratedColumn> get $columns => [id, name, birthday, nextUser];
@override @override

View File

@ -474,7 +474,7 @@ class $FriendshipsTable extends Friendships
'really_good_friends', aliasedName, false, 'really_good_friends', aliasedName, false,
type: DriftSqlType.bool, type: DriftSqlType.bool,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: 'CHECK (really_good_friends IN (0, 1))', defaultConstraints: 'CHECK ("really_good_friends" IN (0, 1))',
defaultValue: const Constant(false)); defaultValue: const Constant(false));
@override @override
List<GeneratedColumn> get $columns => List<GeneratedColumn> get $columns =>
@ -562,7 +562,7 @@ abstract class _$Database extends GeneratedDatabase {
Selectable<FriendshipsOfResult> friendshipsOf(int user) { Selectable<FriendshipsOfResult> friendshipsOf(int user) {
return customSelect( return customSelect(
'SELECT f.really_good_friends,"user"."id" AS "nested_0.id", "user"."name" AS "nested_0.name", "user"."birth_date" AS "nested_0.birth_date", "user"."profile_picture" AS "nested_0.profile_picture", "user"."preferences" AS "nested_0.preferences" FROM friendships AS f INNER JOIN users AS "user" ON "user".id IN (f.first_user, f.second_user) AND "user".id != @1 WHERE(f.first_user = @1 OR f.second_user = @1)', 'SELECT f.really_good_friends,"user"."id" AS "nested_0.id", "user"."name" AS "nested_0.name", "user"."birth_date" AS "nested_0.birth_date", "user"."profile_picture" AS "nested_0.profile_picture", "user"."preferences" AS "nested_0.preferences" FROM friendships AS f INNER JOIN users AS user ON user.id IN (f.first_user, f.second_user) AND user.id != @1 WHERE(f.first_user = @1 OR f.second_user = @1)',
variables: [ variables: [
Variable<int>(user) Variable<int>(user)
], ],