From ac98ee53dfca5a6107a3651f39fa0bc716245523 Mon Sep 17 00:00:00 2001 From: Janez Stupar Date: Fri, 11 Apr 2025 12:01:58 +0200 Subject: [PATCH 1/2] Transform automatic parameters to explicit as a part of CRDT injection. --- lib/src/crdt_executor.dart | 8 +++++-- lib/src/sql_crdt.dart | 2 +- lib/src/sql_util.dart | 22 +++++++++++++++++++ pubspec.yaml | 1 + test/sql_util_test.dart | 45 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 3 deletions(-) diff --git a/lib/src/crdt_executor.dart b/lib/src/crdt_executor.dart index 4215994..b300cd6 100644 --- a/lib/src/crdt_executor.dart +++ b/lib/src/crdt_executor.dart @@ -3,6 +3,7 @@ import 'package:sqlparser/sqlparser.dart'; import 'package:sqlparser/utils/node_to_text.dart'; import 'database_api.dart'; +import 'sql_util.dart'; final _sqlEngine = SqlEngine(); @@ -36,7 +37,8 @@ class CrdtTableExecutor extends _CrdtTableExecutor implements CrdtApi { @override Future>> query(String sql, [List? args]) => - (_db as ReadWriteApi).query(sql, args); + (_db as ReadWriteApi) + .query(SqlUtil.transformAutomaticExplicitSql(sql), args); } class _CrdtTableExecutor { @@ -122,7 +124,8 @@ class CrdtExecutor extends CrdtWriteExecutor implements CrdtApi { @override Future>> query(String sql, [List? args]) => - (_db as ReadWriteApi).query(sql, args); + (_db as ReadWriteApi) + .query(SqlUtil.transformAutomaticExplicitSql(sql), args); } class CrdtWriteExecutor extends _CrdtTableExecutor { @@ -136,6 +139,7 @@ class CrdtWriteExecutor extends _CrdtTableExecutor { @override Future _executeStatement( Statement statement, List? args) async { + SqlUtil.transformAutomaticExplicit(statement); final table = await switch (statement) { CreateTableStatement statement => _createTable(statement, args), InsertStatement statement => _insert(statement, args), diff --git a/lib/src/sql_crdt.dart b/lib/src/sql_crdt.dart index 2b69f37..35277d5 100644 --- a/lib/src/sql_crdt.dart +++ b/lib/src/sql_crdt.dart @@ -40,7 +40,7 @@ abstract class SqlCrdt extends Crdt implements CrdtApi { @override Future>> query(String sql, [List? args]) => - _db.query(sql, args); + _db.query(SqlUtil.transformAutomaticExplicitSql(sql), args); @override Future execute(String sql, [List? args]) async { diff --git a/lib/src/sql_util.dart b/lib/src/sql_util.dart index 504f067..3739cff 100644 --- a/lib/src/sql_util.dart +++ b/lib/src/sql_util.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:crdt/crdt.dart'; import 'package:source_span/source_span.dart'; import 'package:sqlparser/sqlparser.dart'; @@ -27,6 +28,27 @@ class SqlUtil { .fold({}, (prev, e) => prev..addAll(_getAffectedTables(e))); } + /// function takes a SQL [statement] + /// transforms the SQL statement to change parameters from automatic + /// index into parameters with explicit index + static void transformAutomaticExplicit(Statement statement) { + statement.allDescendants + .whereType() + .forEachIndexed((i, ref) { + ref.explicitIndex ??= i + 1; + }); + } + + static String transformAutomaticExplicitSql(String sql) { + final statement = _sqlEngine.parse(sql).rootNode as Statement; + + // if statement is of InvalidStatement type, return the original SQL string + if (statement is InvalidStatement) return sql; + + transformAutomaticExplicit(statement); + return statement.toSql(); + } + static String addChangesetClauses( String table, String sql, { diff --git a/pubspec.yaml b/pubspec.yaml index 1cf86f1..fe0735d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: # path: ../crdt source_span: ^1.10.0 sqlparser: ^0.39.2 + collection: ^1.19.1 dev_dependencies: lints: any diff --git a/test/sql_util_test.dart b/test/sql_util_test.dart index 8c4d794..fe3ae81 100644 --- a/test/sql_util_test.dart +++ b/test/sql_util_test.dart @@ -71,4 +71,49 @@ void main() { "SELECT * FROM test WHERE node_id != 'node_id' AND modified > '1970-01-01T00:00:00.000Z-0000-node_id' AND a != ?1 AND b = ?2"); }); }); + + group('Transform automatic to explicit parameters', () { + test('Simple automatic parameters', () { + final sql = 'SELECT * FROM users WHERE name = ? AND age = ?'; + final transformed = SqlUtil.transformAutomaticExplicitSql(sql); + expect(transformed, 'SELECT * FROM users WHERE name = ?1 AND age = ?2'); + }); + + test('Already explicit parameters', () { + final sql = 'SELECT * FROM users WHERE name = ?1 AND age = ?2'; + final transformed = SqlUtil.transformAutomaticExplicitSql(sql); + expect(transformed, 'SELECT * FROM users WHERE name = ?1 AND age = ?2'); + }); + + test('Mixed automatic and explicit parameters', () { + final sql = + 'SELECT * FROM users WHERE name = ? AND age = ?2 AND city = ?'; + final transformed = SqlUtil.transformAutomaticExplicitSql(sql); + expect(transformed, + 'SELECT * FROM users WHERE name = ?1 AND age = ?2 AND city = ?3'); + }); + + test('Multiple automatic parameters in complex query', () { + final sql = '''SELECT * FROM users + JOIN orders ON users.id = orders.user_id + WHERE users.name = ? AND orders.status = ? AND orders.total > ? + '''; + final transformed = SqlUtil.transformAutomaticExplicitSql(sql); + expect(transformed, + 'SELECT * FROM users JOIN orders ON users.id = orders.user_id WHERE users.name = ?1 AND orders.status = ?2 AND orders.total > ?3'); + }); + + test('Automatic parameters in INSERT statement', () { + final sql = 'INSERT INTO users (name, age, city) VALUES (?, ?, ?)'; + final transformed = SqlUtil.transformAutomaticExplicitSql(sql); + expect(transformed, + 'INSERT INTO users (name, age, city) VALUES (?1, ?2, ?3)'); + }); + + test('Automatic parameters in UPDATE statement', () { + final sql = 'UPDATE users SET name = ?, age = ? WHERE id = ?'; + final transformed = SqlUtil.transformAutomaticExplicitSql(sql); + expect(transformed, 'UPDATE users SET name = ?1, age = ?2 WHERE id = ?3'); + }); + }); } From 9d49939a95b4a0db0508ee99e468f57cd1b2543f Mon Sep 17 00:00:00 2001 From: Janez Stupar Date: Fri, 11 Apr 2025 12:28:32 +0200 Subject: [PATCH 2/2] Bump version and dependencies. --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index fe0735d..e55d142 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: sql_crdt description: Base package for Conflict-free Replicated Data Types (CRDTs) using SQL databases -version: 3.0.2 +version: 3.0.3 homepage: https://github.com/cachapa/sql_crdt repository: https://github.com/cachapa/sql_crdt issue_tracker: https://github.com/cachapa/sql_crdt/issues @@ -12,7 +12,7 @@ dependencies: crdt: ^5.1.3 # path: ../crdt source_span: ^1.10.0 - sqlparser: ^0.39.2 + sqlparser: ^0.41.0 collection: ^1.19.1 dev_dependencies: