Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,15 @@ final class Variable<T extends Object> extends Expression<T> {
var explicitStart = context.explicitVariableIndex;

var mark = '?';
var suffix = '';
if (context.dialect == SqlDialect.postgres) {
explicitStart = 1;
mark = r'$';
}

if (explicitStart != null) {
if (explicitStart != null && context.dialect.supportsIndexedParameters) {
context.buffer
..write(mark)
..write(explicitStart + context.amountOfVariables)
..write(suffix);
..write(explicitStart + context.amountOfVariables);
context.introduceVariable(this, mapToSimpleValue(context));
} else {
context.buffer.write(mark);
Expand Down
10 changes: 10 additions & 0 deletions drift/lib/src/runtime/query_builder/query_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ enum SqlDialect {
realType: 'float8',
),

/// DuckDB (currently supported in an experimental state)
duckdb(
booleanType: 'BOOLEAN',
textType: 'TEXT',
integerType: 'BIGINT',
blobType: 'BLOB',
realType: 'DOUBLE',
supportsIndexedParameters: false,
),

/// MariaDB (currently supported in an experimental state)
mariadb(
booleanType: 'BOOLEAN',
Expand Down
21 changes: 16 additions & 5 deletions drift/lib/src/runtime/query_builder/statements/insert.dart
Original file line number Diff line number Diff line change
Expand Up @@ -511,11 +511,22 @@ enum InsertMode implements Component {
throw ArgumentError('$this not supported on postgres');
}

ctx.buffer.write(
_insertKeywords[ctx.dialect == SqlDialect.postgres
? InsertMode.insert
: this],
);
if (ctx.dialect == SqlDialect.duckdb &&
this != InsertMode.insert &&
this != InsertMode.replace &&
this != InsertMode.insertOrReplace &&
this != InsertMode.insertOrIgnore) {
throw ArgumentError('$this not supported on duckdb');
}

final effectiveMode = switch (ctx.dialect) {
SqlDialect.postgres => InsertMode.insert,
SqlDialect.duckdb when this == InsertMode.replace =>
InsertMode.insertOrReplace,
_ => this,
};

ctx.buffer.write(_insertKeywords[effectiveMode]);
}
}

Expand Down
179 changes: 179 additions & 0 deletions drift/test/database/duckdb_dialect_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import 'package:drift/drift.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

import '../generated/todos.dart';
import '../test_utils/test_utils.dart';

void main() {
String writeColumn(GeneratedColumn<Object> column) {
final context = stubContext(dialect: SqlDialect.duckdb);
column.writeColumnDefinition(context);
return context.sql;
}

group('schema generation', () {
test('writes BIGINT for integer columns', () {
expect(
writeColumn(
GeneratedColumn<int>('value', 'tbl', false, type: DriftSqlType.int),
),
'"value" BIGINT NOT NULL',
);
});

test('writes BIGINT for int64 columns', () {
expect(
writeColumn(
GeneratedColumn<BigInt>(
'value',
'tbl',
false,
type: DriftSqlType.bigInt,
),
),
'"value" BIGINT NOT NULL',
);
});

test('writes BOOLEAN for boolean columns', () {
expect(
writeColumn(
GeneratedColumn<bool>('value', 'tbl', false, type: DriftSqlType.bool),
),
'"value" BOOLEAN NOT NULL',
);
});

test('writes DOUBLE for real columns', () {
expect(
writeColumn(
GeneratedColumn<double>(
'value',
'tbl',
false,
type: DriftSqlType.double,
),
),
'"value" DOUBLE NOT NULL',
);
});
});

group('variables', () {
test('use question mark placeholders', () {
expect(
const Variable<int>(1),
generatesWithOptions('?', variables: [1], dialect: SqlDialect.duckdb),
);
});

test('do not generate sqlite-style indexed placeholders', () {
final context = stubContext(dialect: SqlDialect.duckdb)
..explicitVariableIndex = 3;

const Variable<int>(1).writeInto(context);

expect(context.sql, '?');
expect(context.boundVariables, [1]);
});
});

group('casts', () {
test('cast<int>() uses BIGINT', () {
expect(
const Variable<int>(1).cast<int>(),
generatesWithOptions(
'CAST(? AS BIGINT)',
variables: [1],
dialect: SqlDialect.duckdb,
),
);
});

test('cast<BigInt>() uses BIGINT', () {
expect(
Variable<BigInt>(BigInt.one).cast<BigInt>(),
generatesWithOptions(
'CAST(? AS BIGINT)',
variables: [BigInt.one],
dialect: SqlDialect.duckdb,
),
);
});
});

group('query generation', () {
late TodoDb db;
late MockExecutor executor;

setUp(() {
executor = MockExecutor();
when(executor.dialect).thenReturn(SqlDialect.duckdb);
db = TodoDb(executor);
});

test('inserts with question mark placeholders', () async {
await db
.into(db.tableWithoutPK)
.insert(
CustomRowClass.map(
42,
3.1415,
webSafeInt: BigInt.one,
custom: MyCustomObject('custom'),
).toInsertable(),
);

verify(
executor.runInsert(
'INSERT INTO "table_without_p_k" '
'("not_really_an_id", "some_float", "web_safe_int", "custom") '
'VALUES (?, ?, ?, ?)',
[42, 3.1415, BigInt.one, anything],
),
);
});

test('selects with question mark placeholders', () async {
when(executor.runSelect(any, any)).thenAnswer((_) async => []);

await (db.select(
db.tableWithoutPK,
)..where((t) => t.notReallyAnId.equals(42))).get();

verify(
executor.runSelect(
'SELECT * FROM "table_without_p_k" WHERE "not_really_an_id" = ?;',
[42],
),
);
});

test('supports RETURNING * for inserts', () async {
when(executor.runSelect(any, any)).thenAnswer(
(_) async => [
{
'id': 1,
'desc': 'description',
'description_in_upper_case': 'DESCRIPTION',
'priority': 1,
},
],
);

await db
.into(db.categories)
.insertReturning(
CategoriesCompanion.insert(description: 'description'),
);

verify(
executor.runSelect(
'INSERT INTO "categories" ("desc") VALUES (?) RETURNING *',
['description'],
),
);
});
});
}
1 change: 1 addition & 0 deletions drift_dev/lib/src/generated/analysis/options.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions drift_dev/lib/src/writer/utils/column_constraints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Map<SqlDialect, String> defaultConstraints(DriftColumn column) {
dialectSpecificConstraints[dialect]!.add(
'PRIMARY KEY AUTO_INCREMENT',
);
} else if (dialect == SqlDialect.duckdb) {
dialectSpecificConstraints[dialect]!.add('PRIMARY KEY');
} else {
dialectSpecificConstraints[dialect]!.add(
'PRIMARY KEY AUTOINCREMENT',
Expand Down
Loading