mirror of https://github.com/AMT-Cheif/drift.git
Merge branch 'simolus3:develop' into develop
This commit is contained in:
commit
3bd837517c
|
@ -4,6 +4,7 @@
|
||||||
generated companion class.
|
generated companion class.
|
||||||
- Add the `TypeConverter.extensionType` factory to create type converters for
|
- Add the `TypeConverter.extensionType` factory to create type converters for
|
||||||
extension types.
|
extension types.
|
||||||
|
- Fix invalid SQL syntax being generated for `BLOB` literals on postgres.
|
||||||
|
|
||||||
## 2.16.0
|
## 2.16.0
|
||||||
|
|
||||||
|
|
|
@ -115,9 +115,17 @@ final class SqlTypes {
|
||||||
return (dart.millisecondsSinceEpoch ~/ 1000).toString();
|
return (dart.millisecondsSinceEpoch ~/ 1000).toString();
|
||||||
}
|
}
|
||||||
} else if (dart is Uint8List) {
|
} else if (dart is Uint8List) {
|
||||||
// BLOB literals are string literals containing hexadecimal data and
|
final String hexString = hex.encode(dart);
|
||||||
// preceded by a single "x" or "X" character. Example: X'53514C697465'
|
|
||||||
return "x'${hex.encode(dart)}'";
|
if (dialect == SqlDialect.postgres) {
|
||||||
|
// Postgres BYTEA hex format
|
||||||
|
// https://www.postgresql.org/docs/current/datatype-binary.html#DATATYPE-BINARY-BYTEA-HEX-FORMAT
|
||||||
|
return "'\\x$hexString'::bytea";
|
||||||
|
} else {
|
||||||
|
// BLOB literals are string literals containing hexadecimal data and
|
||||||
|
// preceded by a single "x" or "X" character. Example: X'53514C697465'
|
||||||
|
return "x'$hexString'";
|
||||||
|
}
|
||||||
} else if (dart is DriftAny) {
|
} else if (dart is DriftAny) {
|
||||||
return mapToSqlLiteral(dart.rawSqlValue);
|
return mapToSqlLiteral(dart.rawSqlValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
- Fix drift using the wrong import alias in generated part files.
|
- Fix drift using the wrong import alias in generated part files.
|
||||||
- Add the `use_sql_column_name_as_json_key` builder option.
|
- Add the `use_sql_column_name_as_json_key` builder option.
|
||||||
|
- Add a `setup` parameter to `SchemaVerifier`. It is called when the verifier
|
||||||
|
creates database connections (similar to the callback on `NativeDatabase`)
|
||||||
|
and can be used to register custom functions.
|
||||||
|
|
||||||
## 2.16.0
|
## 2.16.0
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,18 @@ export 'package:drift_dev/src/services/schema/verifier_common.dart'
|
||||||
show SchemaMismatch;
|
show SchemaMismatch;
|
||||||
|
|
||||||
abstract class SchemaVerifier {
|
abstract class SchemaVerifier {
|
||||||
factory SchemaVerifier(SchemaInstantiationHelper helper) =
|
/// Creates a schema verifier for the drift-generated [helper].
|
||||||
VerifierImplementation;
|
///
|
||||||
|
/// See [tests] for more information.
|
||||||
|
/// The optional [setup] parameter is used internally by the verifier for
|
||||||
|
/// every database connection it opens. This can be used to, for instance,
|
||||||
|
/// register custom functions expected by your database.
|
||||||
|
///
|
||||||
|
/// [tests]: https://drift.simonbinder.eu/docs/migrations/tests/
|
||||||
|
factory SchemaVerifier(
|
||||||
|
SchemaInstantiationHelper helper, {
|
||||||
|
void Function(Database raw)? setup,
|
||||||
|
}) = VerifierImplementation;
|
||||||
|
|
||||||
/// Creates a [DatabaseConnection] that contains empty tables created for the
|
/// Creates a [DatabaseConnection] that contains empty tables created for the
|
||||||
/// known schema [version].
|
/// known schema [version].
|
||||||
|
|
|
@ -13,8 +13,9 @@ Expando<List<Input>> expectedSchema = Expando();
|
||||||
class VerifierImplementation implements SchemaVerifier {
|
class VerifierImplementation implements SchemaVerifier {
|
||||||
final SchemaInstantiationHelper helper;
|
final SchemaInstantiationHelper helper;
|
||||||
final Random _random = Random();
|
final Random _random = Random();
|
||||||
|
final void Function(Database)? setup;
|
||||||
|
|
||||||
VerifierImplementation(this.helper);
|
VerifierImplementation(this.helper, {this.setup});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> migrateAndValidate(GeneratedDatabase db, int expectedVersion,
|
Future<void> migrateAndValidate(GeneratedDatabase db, int expectedVersion,
|
||||||
|
@ -57,14 +58,20 @@ class VerifierImplementation implements SchemaVerifier {
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Database _setupDatabase(String uri) {
|
||||||
|
final database = sqlite3.open(uri, uri: true);
|
||||||
|
setup?.call(database);
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<InitializedSchema> schemaAt(int version) async {
|
Future<InitializedSchema> schemaAt(int version) async {
|
||||||
// Use distinct executors for setup and use, allowing us to close the helper
|
// Use distinct executors for setup and use, allowing us to close the helper
|
||||||
// db here and avoid creating it twice.
|
// db here and avoid creating it twice.
|
||||||
// https://www.sqlite.org/inmemorydb.html#sharedmemdb
|
// https://www.sqlite.org/inmemorydb.html#sharedmemdb
|
||||||
final uri = 'file:mem${_randomString()}?mode=memory&cache=shared';
|
final uri = 'file:mem${_randomString()}?mode=memory&cache=shared';
|
||||||
final dbForSetup = sqlite3.open(uri, uri: true);
|
final dbForSetup = _setupDatabase(uri);
|
||||||
final dbForUse = sqlite3.open(uri, uri: true);
|
final dbForUse = _setupDatabase(uri);
|
||||||
|
|
||||||
final executor = NativeDatabase.opened(dbForSetup);
|
final executor = NativeDatabase.opened(dbForSetup);
|
||||||
final db = helper.databaseForVersion(executor, version);
|
final db = helper.databaseForVersion(executor, version);
|
||||||
|
@ -74,7 +81,7 @@ class VerifierImplementation implements SchemaVerifier {
|
||||||
await db.close();
|
await db.close();
|
||||||
|
|
||||||
return InitializedSchema(dbForUse, () {
|
return InitializedSchema(dbForUse, () {
|
||||||
final db = sqlite3.open(uri, uri: true);
|
final db = _setupDatabase(uri);
|
||||||
return DatabaseConnection(NativeDatabase.opened(db));
|
return DatabaseConnection(NativeDatabase.opened(db));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,11 @@ import 'package:drift_dev/src/services/schema/verifier_impl.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final verifier = SchemaVerifier(_TestHelper());
|
final verifier = SchemaVerifier(
|
||||||
|
_TestHelper(),
|
||||||
|
setup: (rawDb) => rawDb.createFunction(
|
||||||
|
functionName: 'test_function', function: (args) => 1),
|
||||||
|
);
|
||||||
|
|
||||||
group('startAt', () {
|
group('startAt', () {
|
||||||
test('starts at the requested version', () async {
|
test('starts at the requested version', () async {
|
||||||
|
@ -15,6 +19,12 @@ void main() {
|
||||||
expect(details.hadUpgrade, isFalse, reason: 'no upgrade expected');
|
expect(details.hadUpgrade, isFalse, reason: 'no upgrade expected');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('registers custom functions', () async {
|
||||||
|
final db = (await verifier.startAt(17)).executor;
|
||||||
|
await db.ensureOpen(_DelegatedUser(17, (_, details) async {}));
|
||||||
|
await db.runSelect('select test_function()', []);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('migrateAndValidate', () {
|
group('migrateAndValidate', () {
|
||||||
|
|
|
@ -26,19 +26,19 @@ void main() {
|
||||||
return row.read(expression)!;
|
return row.read(expression)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testWith<T extends Object>(CustomSqlType<T>? type, T value) {
|
||||||
|
test('with variable', () async {
|
||||||
|
final variable = Variable(value, type);
|
||||||
|
expect(await eval(variable), value);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with constant', () async {
|
||||||
|
final constant = Constant(value, type);
|
||||||
|
expect(await eval(constant), value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
group('custom types pass through', () {
|
group('custom types pass through', () {
|
||||||
void testWith<T extends Object>(CustomSqlType<T> type, T value) {
|
|
||||||
test('with variable', () async {
|
|
||||||
final variable = Variable(value, type);
|
|
||||||
expect(await eval(variable), value);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with constant', () async {
|
|
||||||
final constant = Constant(value, type);
|
|
||||||
expect(await eval(constant), value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
group('uuid', () => testWith(PgTypes.uuid, Uuid().v4obj()));
|
group('uuid', () => testWith(PgTypes.uuid, Uuid().v4obj()));
|
||||||
group(
|
group(
|
||||||
'interval',
|
'interval',
|
||||||
|
@ -60,6 +60,8 @@ void main() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('bytea', () => testWith(null, Uint8List.fromList([1, 2, 3, 4, 5])));
|
||||||
|
|
||||||
test('compare datetimes', () async {
|
test('compare datetimes', () async {
|
||||||
final time = DateTime.now();
|
final time = DateTime.now();
|
||||||
final before = Variable(
|
final before = Variable(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
## 3.35.0-dev
|
## 3.35.0-dev
|
||||||
|
|
||||||
|
- Fix parsing binary literals.
|
||||||
- Drift extensions: Allow custom class names for `CREATE VIEW` statements.
|
- Drift extensions: Allow custom class names for `CREATE VIEW` statements.
|
||||||
|
|
||||||
## 0.34.1
|
## 0.34.1
|
||||||
|
|
|
@ -290,7 +290,7 @@ class Scanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
final value = source
|
final value = source
|
||||||
.substring(_startOffset + 1, _currentOffset - 1)
|
.substring(_startOffset + (binary ? 2 : 1), _currentOffset - 1)
|
||||||
.replaceAll("''", "'");
|
.replaceAll("''", "'");
|
||||||
tokens.add(StringLiteralToken(value, _currentSpan, binary: binary));
|
tokens.add(StringLiteralToken(value, _currentSpan, binary: binary));
|
||||||
}
|
}
|
||||||
|
|
|
@ -642,7 +642,7 @@ class EqualityEnforcingVisitor implements AstVisitor<void, void> {
|
||||||
@override
|
@override
|
||||||
void visitStringLiteral(StringLiteral e, void arg) {
|
void visitStringLiteral(StringLiteral e, void arg) {
|
||||||
final current = _currentAs<StringLiteral>(e);
|
final current = _currentAs<StringLiteral>(e);
|
||||||
_assert(current.value == e.value, e);
|
_assert(current.value == e.value && current.isBinary == e.isBinary, e);
|
||||||
_checkChildren(e);
|
_checkChildren(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1114,6 +1114,9 @@ class NodeSqlBuilder extends AstVisitor<void, void> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void visitStringLiteral(StringLiteral e, void arg) {
|
void visitStringLiteral(StringLiteral e, void arg) {
|
||||||
|
if (e.isBinary) {
|
||||||
|
symbol('X', spaceBefore: true);
|
||||||
|
}
|
||||||
_stringLiteral(e.value);
|
_stringLiteral(e.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,26 @@ void main() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('binary string literal', () {
|
||||||
|
final scanner = Scanner("X'1234' x'5678'");
|
||||||
|
scanner.scanTokens();
|
||||||
|
|
||||||
|
expect(scanner.tokens, hasLength(3));
|
||||||
|
expect(
|
||||||
|
scanner.tokens[0],
|
||||||
|
const TypeMatcher<StringLiteralToken>()
|
||||||
|
.having((token) => token.binary, 'binary', isTrue)
|
||||||
|
.having((token) => token.value, 'value', '1234'),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
scanner.tokens[1],
|
||||||
|
const TypeMatcher<StringLiteralToken>()
|
||||||
|
.having((token) => token.binary, 'binary', isTrue)
|
||||||
|
.having((token) => token.value, 'value', '5678'),
|
||||||
|
);
|
||||||
|
expect(scanner.tokens[2].type, TokenType.eof);
|
||||||
|
});
|
||||||
|
|
||||||
group('parses numeric literals', () {
|
group('parses numeric literals', () {
|
||||||
void checkLiteral(String lexeme, NumericToken other, num value) {
|
void checkLiteral(String lexeme, NumericToken other, num value) {
|
||||||
final scanner = Scanner(lexeme)..scanTokens();
|
final scanner = Scanner(lexeme)..scanTokens();
|
|
@ -568,6 +568,11 @@ CREATE UNIQUE INDEX my_idx ON t1 (c1, c2, c3) WHERE c1 < c3;
|
||||||
testFormat('SELECT a -> b');
|
testFormat('SELECT a -> b');
|
||||||
testFormat('SELECT a ->> b');
|
testFormat('SELECT a ->> b');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('blob literal', () {
|
||||||
|
testFormat(
|
||||||
|
"select typeof(X'0100000300000000000000000000803F000000000000003F0000803F');");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('identifiers', () {
|
test('identifiers', () {
|
||||||
|
|
Loading…
Reference in New Issue