From a97d70754acb9e098c0d65f41f0256c387913013 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 1 Feb 2023 22:44:12 +0100 Subject: [PATCH 1/5] Experiment with postgres v3 package --- .../drift_postgres/lib/src/pg_database.dart | 144 +++++++++--------- extras/drift_postgres/pubspec.yaml | 5 +- .../test/drift_postgres_test.dart | 13 +- 3 files changed, 82 insertions(+), 80 deletions(-) diff --git a/extras/drift_postgres/lib/src/pg_database.dart b/extras/drift_postgres/lib/src/pg_database.dart index 64a9b1cd..3e3d4d24 100644 --- a/extras/drift_postgres/lib/src/pg_database.dart +++ b/extras/drift_postgres/lib/src/pg_database.dart @@ -1,13 +1,28 @@ +import 'dart:async'; + import 'package:collection/collection.dart'; import 'package:drift/backends.dart'; -import 'package:postgres/postgres.dart'; +import 'package:postgres/postgres_v3_experimental.dart'; /// A drift database implementation that talks to a postgres database. class PgDatabase extends DelegatedDatabase { + PgDatabase({ + required PgEndpoint endpoint, + PgSessionSettings? sessionSettings, + bool logStatements = false, + }) : super( + _PgDelegate( + () => PgConnection.open(endpoint, sessionSettings: sessionSettings), + true, + ), + isSequential: true, + logStatements: logStatements, + ); + /// Creates a drift database implementation from a postgres database /// [connection]. - PgDatabase(PostgreSQLConnection connection, {bool logStatements = false}) - : super(_PgDelegate(connection, connection), + PgDatabase.opened(PgSession connection, {bool logStatements = false}) + : super(_PgDelegate(() => connection, false), isSequential: true, logStatements: logStatements); @override @@ -15,67 +30,60 @@ class PgDatabase extends DelegatedDatabase { } class _PgDelegate extends DatabaseDelegate { - final PostgreSQLConnection _db; - final PostgreSQLExecutionContext _ec; - - _PgDelegate(this._db, this._ec) : closeUnderlyingWhenClosed = false; - - bool _isOpen = false; + _PgDelegate(this._open, this.closeUnderlyingWhenClosed); final bool closeUnderlyingWhenClosed; + final FutureOr Function() _open; + + PgSession? _openedSession; @override - TransactionDelegate get transactionDelegate => _PgTransactionDelegate(_db); + TransactionDelegate get transactionDelegate => const NoTransactionDelegate(); @override late DbVersionDelegate versionDelegate; @override - Future get isOpen => Future.value(_isOpen); + Future get isOpen => Future.value(_openedSession != null); @override Future open(QueryExecutorUser user) async { - final pgVersionDelegate = _PgVersionDelegate(_db); + final session = await _open(); + final pgVersionDelegate = _PgVersionDelegate(session); - await _db.open(); await pgVersionDelegate.init(); + _openedSession = session; versionDelegate = pgVersionDelegate; - _isOpen = true; - } - - Future _ensureOpen() async { - if (_db.isClosed) { - await _db.open(); - } } @override Future runBatched(BatchedStatements statements) async { - await _ensureOpen(); + final session = _openedSession!; + final prepared = []; - for (final row in statements.arguments) { - final stmt = statements.statements[row.statementIndex]; - final args = row.arguments; + try { + for (final statement in statements.statements) { + prepared.add(await session.prepare(PgSql(statement))); + } - await _ec.execute(stmt, - substitutionValues: args.asMap().map((key, value) => - MapEntry((key + 1).toString(), _convertValue(value)))); + for (final row in statements.arguments) { + final stmt = prepared[row.statementIndex]; + + await stmt.run(row.arguments); + } + } finally { + for (final stmt in prepared) { + await stmt.dispose(); + } } - - return Future.value(); } Future _runWithArgs(String statement, List args) async { - await _ensureOpen(); + final session = _openedSession!; - if (args.isEmpty) { - return _ec.execute(statement); - } else { - return _ec.execute(statement, - substitutionValues: args.asMap().map((key, value) => - MapEntry((key + 1).toString(), _convertValue(value)))); - } + final result = await session.execute(PgSql(statement), parameters: args); + return result.affectedRows; } @override @@ -85,15 +93,8 @@ class _PgDelegate extends DatabaseDelegate { @override Future runInsert(String statement, List args) async { - await _ensureOpen(); - PostgreSQLResult result; - if (args.isEmpty) { - result = await _ec.query(statement); - } else { - result = await _ec.query(statement, - substitutionValues: args.asMap().map((key, value) => - MapEntry((key + 1).toString(), _convertValue(value)))); - } + final session = _openedSession!; + final result = await session.execute(PgSql(statement), parameters: args); return result.firstOrNull?[0] as int? ?? 0; } @@ -104,19 +105,18 @@ class _PgDelegate extends DatabaseDelegate { @override Future runSelect(String statement, List args) async { - await _ensureOpen(); - final result = await _ec.query(statement, - substitutionValues: args.asMap().map((key, value) => - MapEntry((key + 1).toString(), _convertValue(value)))); + final session = _openedSession!; + final result = await session.execute(PgSql(statement), parameters: args); - return Future.value(QueryResult.fromRows( - result.map((e) => e.toColumnMap()).toList(growable: false))); + return QueryResult([ + for (final pgColumn in result.schema.columns) pgColumn.columnName ?? '', + ], result); } @override Future close() async { if (closeUnderlyingWhenClosed) { - await _db.close(); + await _openedSession?.close(); } } @@ -129,42 +129,36 @@ class _PgDelegate extends DatabaseDelegate { } class _PgVersionDelegate extends DynamicVersionDelegate { - final PostgreSQLConnection database; + final PgSession database; _PgVersionDelegate(this.database); @override Future get schemaVersion async { - final result = await database.query('SELECT version FROM __schema'); + final result = + await database.execute(PgSql('SELECT version FROM __schema')); return result[0][0] as int; } Future init() async { - await database.query('CREATE TABLE IF NOT EXISTS __schema (' - 'version integer NOT NULL DEFAULT 0)'); - final count = await database.query('SELECT COUNT(*) FROM __schema'); + await database.execute(PgSql('CREATE TABLE IF NOT EXISTS __schema (' + 'version integer NOT NULL DEFAULT 0)')); + + final count = + await database.execute(PgSql('SELECT COUNT(*) FROM __schema')); if (count[0][0] as int == 0) { - await database.query('INSERT INTO __schema (version) VALUES (0)'); + await database + .execute(PgSql('INSERT INTO __schema (version) VALUES (0)')); } } @override Future setSchemaVersion(int version) async { - await database.query('UPDATE __schema SET version = @1', - substitutionValues: {'1': version}); - } -} - -class _PgTransactionDelegate extends SupportedTransactionDelegate { - final PostgreSQLConnection _db; - - const _PgTransactionDelegate(this._db); - - @override - bool get managesLockInternally => false; - - @override - Future startTransaction(Future Function(QueryDelegate) run) async { - await _db.transaction((connection) => run(_PgDelegate(_db, connection))); + await database.execute( + PgSql(r'UPDATE __schema SET version = $1', types: [PgDataType.integer]), + parameters: [ + PgTypedParameter(PgDataType.integer, version), + ], + ); } } diff --git a/extras/drift_postgres/pubspec.yaml b/extras/drift_postgres/pubspec.yaml index 8645d719..c04bb9d9 100644 --- a/extras/drift_postgres/pubspec.yaml +++ b/extras/drift_postgres/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: collection: ^1.16.0 drift: ^2.0.0 - postgres: ^2.4.3 + postgres: meta: ^1.8.0 dev_dependencies: @@ -20,3 +20,6 @@ dev_dependencies: dependency_overrides: drift: path: ../../drift + postgres: + git: + url: https://github.com/isoos/postgresql-dart.git diff --git a/extras/drift_postgres/test/drift_postgres_test.dart b/extras/drift_postgres/test/drift_postgres_test.dart index d0f3685b..97df0a6d 100644 --- a/extras/drift_postgres/test/drift_postgres_test.dart +++ b/extras/drift_postgres/test/drift_postgres_test.dart @@ -1,6 +1,6 @@ import 'package:drift_postgres/postgres.dart'; import 'package:drift_testcases/tests.dart'; -import 'package:postgres/postgres.dart'; +import 'package:postgres/postgres_v3_experimental.dart'; class PgExecutor extends TestExecutor { @override @@ -11,9 +11,14 @@ class PgExecutor extends TestExecutor { @override DatabaseConnection createConnection() { - final pgConnection = PostgreSQLConnection('localhost', 5432, 'postgres', - username: 'postgres', password: 'postgres'); - return DatabaseConnection(PgDatabase(pgConnection)); + return DatabaseConnection(PgDatabase( + endpoint: PgEndpoint( + host: 'localhost', + database: 'postgres', + username: 'postgres', + password: 'postgres', + ), + )); } @override From 47dfdd555483e0a629120866edca25ff25822886 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 1 Feb 2023 23:02:55 +0100 Subject: [PATCH 2/5] Migrate postgres implementation to v3 API --- extras/drift_postgres/example/test.dart | 27 +++++++++++++++++++ .../test/drift_postgres_test.dart | 2 ++ 2 files changed, 29 insertions(+) create mode 100644 extras/drift_postgres/example/test.dart diff --git a/extras/drift_postgres/example/test.dart b/extras/drift_postgres/example/test.dart new file mode 100644 index 00000000..bca63c0f --- /dev/null +++ b/extras/drift_postgres/example/test.dart @@ -0,0 +1,27 @@ +import 'package:drift/backends.dart'; +import 'package:drift/src/runtime/query_builder/query_builder.dart'; +import 'package:drift_postgres/postgres.dart'; +import 'package:postgres/postgres_v3_experimental.dart'; + +void main() async { + final postgres = PgDatabase( + endpoint: PgEndpoint( + host: 'localhost', + database: 'postgres', + username: 'postgres', + password: 'postgres', + ), + logStatements: true, + ); + + await postgres.ensureOpen(_NullUser()); +} + +class _NullUser extends QueryExecutorUser { + @override + Future beforeOpen( + QueryExecutor executor, OpeningDetails details) async {} + + @override + int get schemaVersion => 1; +} diff --git a/extras/drift_postgres/test/drift_postgres_test.dart b/extras/drift_postgres/test/drift_postgres_test.dart index 97df0a6d..269d7a7d 100644 --- a/extras/drift_postgres/test/drift_postgres_test.dart +++ b/extras/drift_postgres/test/drift_postgres_test.dart @@ -11,6 +11,8 @@ class PgExecutor extends TestExecutor { @override DatabaseConnection createConnection() { + throw 'currently broken'; + return DatabaseConnection(PgDatabase( endpoint: PgEndpoint( host: 'localhost', From ed964a7bf215f3ab6e95b7e2d9535672738c5cf0 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Fri, 3 Feb 2023 13:53:38 +0100 Subject: [PATCH 3/5] Fix passing variable types to postgres --- .../query_builder/expressions/variables.dart | 2 +- .../drift_postgres/lib/src/pg_database.dart | 79 +++++++++++++++---- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/drift/lib/src/runtime/query_builder/expressions/variables.dart b/drift/lib/src/runtime/query_builder/expressions/variables.dart index a03815c0..4b6a7dab 100644 --- a/drift/lib/src/runtime/query_builder/expressions/variables.dart +++ b/drift/lib/src/runtime/query_builder/expressions/variables.dart @@ -76,7 +76,7 @@ class Variable extends Expression { var mark = '?'; if (context.dialect == SqlDialect.postgres) { explicitStart = 1; - mark = '@'; + mark = r'$'; } if (explicitStart != null) { diff --git a/extras/drift_postgres/lib/src/pg_database.dart b/extras/drift_postgres/lib/src/pg_database.dart index 3e3d4d24..0bab0232 100644 --- a/extras/drift_postgres/lib/src/pg_database.dart +++ b/extras/drift_postgres/lib/src/pg_database.dart @@ -60,21 +60,28 @@ class _PgDelegate extends DatabaseDelegate { @override Future runBatched(BatchedStatements statements) async { final session = _openedSession!; - final prepared = []; + final prepared = + List.filled(statements.statements.length, null); try { - for (final statement in statements.statements) { - prepared.add(await session.prepare(PgSql(statement))); - } + for (final instantation in statements.arguments) { + final pgArgs = _BoundArguments.ofDartArgs(instantation.arguments); - for (final row in statements.arguments) { - final stmt = prepared[row.statementIndex]; + // Lazily prepare statements when we run into them. The reason is that + // we need to know the types for variables. + final stmtIndex = instantation.statementIndex; + var stmt = prepared[stmtIndex]; + if (stmt == null) { + final sql = statements.statements[stmtIndex]; + stmt = prepared[stmtIndex] = + await session.prepare(PgSql(sql, types: pgArgs.types)); + } - await stmt.run(row.arguments); + await stmt.run(pgArgs.parameters); } } finally { for (final stmt in prepared) { - await stmt.dispose(); + await stmt?.dispose(); } } } @@ -82,7 +89,11 @@ class _PgDelegate extends DatabaseDelegate { Future _runWithArgs(String statement, List args) async { final session = _openedSession!; - final result = await session.execute(PgSql(statement), parameters: args); + final pgArgs = _BoundArguments.ofDartArgs(args); + final result = await session.execute( + PgSql(statement, types: pgArgs.types), + parameters: pgArgs.parameters, + ); return result.affectedRows; } @@ -94,7 +105,9 @@ class _PgDelegate extends DatabaseDelegate { @override Future runInsert(String statement, List args) async { final session = _openedSession!; - final result = await session.execute(PgSql(statement), parameters: args); + final pgArgs = _BoundArguments.ofDartArgs(args); + final result = await session.execute(PgSql(statement, types: pgArgs.types), + parameters: pgArgs.parameters); return result.firstOrNull?[0] as int? ?? 0; } @@ -106,7 +119,9 @@ class _PgDelegate extends DatabaseDelegate { @override Future runSelect(String statement, List args) async { final session = _openedSession!; - final result = await session.execute(PgSql(statement), parameters: args); + final pgArgs = _BoundArguments.ofDartArgs(args); + final result = await session.execute(PgSql(statement, types: pgArgs.types), + parameters: pgArgs.parameters); return QueryResult([ for (final pgColumn in result.schema.columns) pgColumn.columnName ?? '', @@ -119,12 +134,46 @@ class _PgDelegate extends DatabaseDelegate { await _openedSession?.close(); } } +} - Object? _convertValue(Object? value) { - if (value is BigInt) { - return value.toInt(); +class _BoundArguments { + final List types; + final List parameters; + + _BoundArguments(this.types, this.parameters); + + factory _BoundArguments.ofDartArgs(List args) { + final types = []; + final parameters = []; + + void add(PgTypedParameter param) { + types.add(param.type); + parameters.add(param); } - return value; + + for (final value in args) { + if (value == null) { + add(PgTypedParameter(PgDataType.text, null)); + } else if (value is int) { + add(PgTypedParameter(PgDataType.bigInteger, value)); + } else if (value is BigInt) { + // Drift only uses BigInts to represent 64-bit values on the web, so we + // can use toInt() here. + add(PgTypedParameter(PgDataType.bigInteger, value)); + } else if (value is bool) { + add(PgTypedParameter(PgDataType.boolean, value)); + } else if (value is double) { + add(PgTypedParameter(PgDataType.double, value)); + } else if (value is String) { + add(PgTypedParameter(PgDataType.text, value)); + } else if (value is List) { + add(PgTypedParameter(PgDataType.byteArray, value)); + } else { + throw ArgumentError.value(value, 'value', 'Unsupported type'); + } + } + + return _BoundArguments(types, parameters); } } From deb1b91d418b129c24db5035fddd574c421d93ef Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 5 Feb 2023 14:28:19 +0100 Subject: [PATCH 4/5] Escape postgres keywords in generated SQL --- drift_dev/lib/src/writer/queries/sql_writer.dart | 10 ++++++++++ drift_dev/pubspec.yaml | 4 ++-- drift_dev/test/writer/queries/sql_writer_test.dart | 14 +++++++++++--- .../drift_testcases/lib/database/database.dart | 2 +- .../drift_testcases/lib/database/database.g.dart | 4 ++-- sqlparser/lib/utils/node_to_text.dart | 6 +++++- sqlparser/pubspec.yaml | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/drift_dev/lib/src/writer/queries/sql_writer.dart b/drift_dev/lib/src/writer/queries/sql_writer.dart index 3ccd5db8..29da9c23 100644 --- a/drift_dev/lib/src/writer/queries/sql_writer.dart +++ b/drift_dev/lib/src/writer/queries/sql_writer.dart @@ -82,6 +82,16 @@ class SqlWriter extends NodeSqlBuilder { return _out.toString(); } + @override + bool isKeyword(String lexeme) { + switch (options.effectiveDialect) { + case SqlDialect.postgres: + return isKeywordLexeme(lexeme) || isPostgresKeywordLexeme(lexeme); + default: + return isKeywordLexeme(lexeme); + } + } + FoundVariable? _findMoorVar(Variable target) { return query!.variables.firstWhereOrNull( (f) => f.variable.resolvedIndex == target.resolvedIndex); diff --git a/drift_dev/pubspec.yaml b/drift_dev/pubspec.yaml index c3beb935..a16db2f9 100644 --- a/drift_dev/pubspec.yaml +++ b/drift_dev/pubspec.yaml @@ -1,6 +1,6 @@ name: drift_dev description: Dev-dependency for users of drift. Contains the generator and development tools. -version: 2.5.1 +version: 2.6.0-dev repository: https://github.com/simolus3/drift homepage: https://drift.simonbinder.eu/ issue_tracker: https://github.com/simolus3/drift/issues @@ -27,7 +27,7 @@ dependencies: # Drift-specific analysis and apis drift: '>=2.5.0 <2.6.0' sqlite3: '>=0.1.6 <2.0.0' - sqlparser: '^0.27.0' + sqlparser: '^0.28.0' # Dart analysis analyzer: ^5.2.0 diff --git a/drift_dev/test/writer/queries/sql_writer_test.dart b/drift_dev/test/writer/queries/sql_writer_test.dart index c7de3dbe..0b603981 100644 --- a/drift_dev/test/writer/queries/sql_writer_test.dart +++ b/drift_dev/test/writer/queries/sql_writer_test.dart @@ -1,3 +1,4 @@ +import 'package:drift/drift.dart'; import 'package:drift_dev/src/analysis/options.dart'; import 'package:drift_dev/src/analysis/results/results.dart'; import 'package:drift_dev/src/writer/queries/sql_writer.dart'; @@ -5,14 +6,14 @@ import 'package:sqlparser/sqlparser.dart'; import 'package:test/test.dart'; void main() { - void check(String sql, String expectedDart) { + void check(String sql, String expectedDart, + {DriftOptions options = const DriftOptions.defaults()}) { final engine = SqlEngine(); final context = engine.analyze(sql); final query = SqlSelectQuery('name', context, context.root, [], [], InferredResultSet(null, []), null, null); - final result = - SqlWriter(const DriftOptions.defaults(), query: query).write(); + final result = SqlWriter(options, query: query).write(); expect(result, expectedDart); } @@ -28,4 +29,11 @@ void main() { test('escapes Dart characters in SQL', () { check(r"SELECT '$hey';", r"'SELECT \'\$hey\''"); }); + + test('escapes postgres keywords', () { + check('SELECT * FROM user', "'SELECT * FROM user'"); + check('SELECT * FROM user', "'SELECT * FROM \"user\"'", + options: DriftOptions.defaults( + dialect: DialectOptions(SqlDialect.postgres, null))); + }); } diff --git a/extras/integration_tests/drift_testcases/lib/database/database.dart b/extras/integration_tests/drift_testcases/lib/database/database.dart index 40d1db76..b285842c 100644 --- a/extras/integration_tests/drift_testcases/lib/database/database.dart +++ b/extras/integration_tests/drift_testcases/lib/database/database.dart @@ -69,7 +69,7 @@ class PreferenceConverter extends NullAwareTypeConverter { 'ORDER BY (SELECT COUNT(*) FROM friendships ' 'WHERE first_user = u.id OR second_user = u.id) DESC LIMIT :amount', 'amountOfGoodFriends': 'SELECT COUNT(*) FROM friendships f WHERE ' - 'f.really_good_friends = 1 AND ' + 'f.really_good_friends = TRUE AND ' '(f.first_user = :user OR f.second_user = :user)', 'friendshipsOf': ''' SELECT f.really_good_friends, "user".** diff --git a/extras/integration_tests/drift_testcases/lib/database/database.g.dart b/extras/integration_tests/drift_testcases/lib/database/database.g.dart index b5689ce6..61310c1b 100644 --- a/extras/integration_tests/drift_testcases/lib/database/database.g.dart +++ b/extras/integration_tests/drift_testcases/lib/database/database.g.dart @@ -555,7 +555,7 @@ abstract class _$Database extends GeneratedDatabase { Selectable amountOfGoodFriends(int user) { return customSelect( - 'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = 1 AND(f.first_user = @1 OR f.second_user = @1)', + 'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = TRUE AND(f.first_user = @1 OR f.second_user = @1)', variables: [ Variable(user) ], @@ -566,7 +566,7 @@ abstract class _$Database extends GeneratedDatabase { Selectable friendshipsOf(int user) { 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: [ Variable(user) ], diff --git a/sqlparser/lib/utils/node_to_text.dart b/sqlparser/lib/utils/node_to_text.dart index 71b393bf..dd2e5f7d 100644 --- a/sqlparser/lib/utils/node_to_text.dart +++ b/sqlparser/lib/utils/node_to_text.dart @@ -24,6 +24,10 @@ class NodeSqlBuilder extends AstVisitor { } } + bool isKeyword(String lexeme) { + return isKeywordLexeme(lexeme); + } + @override void visitAggregateFunctionInvocation( AggregateFunctionInvocation e, void arg) { @@ -1321,7 +1325,7 @@ class NodeSqlBuilder extends AstVisitor { /// Writes an identifier, escaping it if necessary. void identifier(String identifier, {bool spaceBefore = true, bool spaceAfter = true}) { - if (isKeywordLexeme(identifier) || _notAKeywordRegex.hasMatch(identifier)) { + if (isKeyword(identifier) || _notAKeywordRegex.hasMatch(identifier)) { identifier = '"$identifier"'; } diff --git a/sqlparser/pubspec.yaml b/sqlparser/pubspec.yaml index 4db115c8..62cf4bf8 100644 --- a/sqlparser/pubspec.yaml +++ b/sqlparser/pubspec.yaml @@ -1,6 +1,6 @@ name: sqlparser description: Parses sqlite statements and performs static analysis on them -version: 0.27.0 +version: 0.28.0-dev homepage: https://github.com/simolus3/drift/tree/develop/sqlparser repository: https://github.com/simolus3/drift #homepage: https://drift.simonbinder.eu/ From ca75323a6a74513f578301a61b97bfc57060fa33 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Thu, 9 Feb 2023 10:53:41 +0100 Subject: [PATCH 5/5] Fix postgres tests --- drift_dev/lib/src/writer/queries/sql_writer.dart | 2 +- extras/drift_postgres/example/test.dart | 5 +++++ extras/drift_postgres/test/drift_postgres_test.dart | 2 -- .../drift_testcases/lib/database/database.g.dart | 10 +++++----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drift_dev/lib/src/writer/queries/sql_writer.dart b/drift_dev/lib/src/writer/queries/sql_writer.dart index 29da9c23..43e4c7d8 100644 --- a/drift_dev/lib/src/writer/queries/sql_writer.dart +++ b/drift_dev/lib/src/writer/queries/sql_writer.dart @@ -101,7 +101,7 @@ class SqlWriter extends NodeSqlBuilder { if (variable.isArray) { _writeRawInSpaces('(\$${expandedName(variable)})'); } else { - final mark = _isPostgres ? '@' : '?'; + final mark = _isPostgres ? '\\\$' : '?'; _writeRawInSpaces('$mark${variable.index}'); } } diff --git a/extras/drift_postgres/example/test.dart b/extras/drift_postgres/example/test.dart index bca63c0f..87c2fa30 100644 --- a/extras/drift_postgres/example/test.dart +++ b/extras/drift_postgres/example/test.dart @@ -15,6 +15,11 @@ void main() async { ); await postgres.ensureOpen(_NullUser()); + + final rows = await postgres.runSelect(r'SELECT $1', [true]); + final row = rows.single; + print(row); + print(row.values.map((e) => e.runtimeType).toList()); } class _NullUser extends QueryExecutorUser { diff --git a/extras/drift_postgres/test/drift_postgres_test.dart b/extras/drift_postgres/test/drift_postgres_test.dart index 269d7a7d..97df0a6d 100644 --- a/extras/drift_postgres/test/drift_postgres_test.dart +++ b/extras/drift_postgres/test/drift_postgres_test.dart @@ -11,8 +11,6 @@ class PgExecutor extends TestExecutor { @override DatabaseConnection createConnection() { - throw 'currently broken'; - return DatabaseConnection(PgDatabase( endpoint: PgEndpoint( host: 'localhost', diff --git a/extras/integration_tests/drift_testcases/lib/database/database.g.dart b/extras/integration_tests/drift_testcases/lib/database/database.g.dart index 61310c1b..2a4c8172 100644 --- a/extras/integration_tests/drift_testcases/lib/database/database.g.dart +++ b/extras/integration_tests/drift_testcases/lib/database/database.g.dart @@ -543,7 +543,7 @@ abstract class _$Database extends GeneratedDatabase { late final $FriendshipsTable friendships = $FriendshipsTable(this); Selectable mostPopularUsers(int amount) { return customSelect( - 'SELECT * FROM users AS u ORDER BY (SELECT COUNT(*) FROM friendships WHERE first_user = u.id OR second_user = u.id) DESC LIMIT @1', + 'SELECT * FROM users AS u ORDER BY (SELECT COUNT(*) FROM friendships WHERE first_user = u.id OR second_user = u.id) DESC LIMIT \$1', variables: [ Variable(amount) ], @@ -555,7 +555,7 @@ abstract class _$Database extends GeneratedDatabase { Selectable amountOfGoodFriends(int user) { return customSelect( - 'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = TRUE AND(f.first_user = @1 OR f.second_user = @1)', + 'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = TRUE AND(f.first_user = \$1 OR f.second_user = \$1)', variables: [ Variable(user) ], @@ -566,7 +566,7 @@ abstract class _$Database extends GeneratedDatabase { Selectable friendshipsOf(int user) { 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: [ Variable(user) ], @@ -590,7 +590,7 @@ abstract class _$Database extends GeneratedDatabase { } Selectable settingsFor(int user) { - return customSelect('SELECT preferences FROM users WHERE id = @1', + return customSelect('SELECT preferences FROM users WHERE id = \$1', variables: [ Variable(user) ], @@ -615,7 +615,7 @@ abstract class _$Database extends GeneratedDatabase { Future> returning(int var1, int var2, bool var3) { return customWriteReturning( - 'INSERT INTO friendships VALUES (@1, @2, @3) RETURNING *', + 'INSERT INTO friendships VALUES (\$1, \$2, \$3) RETURNING *', variables: [ Variable(var1), Variable(var2),