diff --git a/extras/integration_tests/tests/lib/database/database.dart b/extras/integration_tests/tests/lib/database/database.dart index a0eb2411d..e53263ef0 100644 --- a/extras/integration_tests/tests/lib/database/database.dart +++ b/extras/integration_tests/tests/lib/database/database.dart @@ -69,6 +69,10 @@ class PreferenceConverter extends TypeConverter { 'SELECT * FROM users u 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 AND (f.first_user = :user OR f.second_user = :user)', + 'friendsOf': '''SELECT u.* FROM friendships f + INNER JOIN users u ON u.id IN (f.first_user, f.second_user) AND + u.id != :user + WHERE (f.first_user = :user OR f.second_user = :user)''', 'userCount': 'SELECT COUNT(id) FROM users', 'settingsFor': 'SELECT preferences FROM users WHERE id = :user', }, diff --git a/extras/integration_tests/tests/lib/database/database.g.dart b/extras/integration_tests/tests/lib/database/database.g.dart index 1e968e9b4..3f6477c30 100644 --- a/extras/integration_tests/tests/lib/database/database.g.dart +++ b/extras/integration_tests/tests/lib/database/database.g.dart @@ -147,6 +147,20 @@ class UsersCompanion extends UpdateCompanion { this.profilePicture = const Value.absent(), this.preferences = const Value.absent(), }); + UsersCompanion copyWith( + {Value id, + Value name, + Value birthDate, + Value profilePicture, + Value preferences}) { + return UsersCompanion( + id: id ?? this.id, + name: name ?? this.name, + birthDate: birthDate ?? this.birthDate, + profilePicture: profilePicture ?? this.profilePicture, + preferences: preferences ?? this.preferences, + ); + } } class $UsersTable extends Users with TableInfo<$UsersTable, User> { @@ -388,6 +402,16 @@ class FriendshipsCompanion extends UpdateCompanion { this.secondUser = const Value.absent(), this.reallyGoodFriends = const Value.absent(), }); + FriendshipsCompanion copyWith( + {Value firstUser, + Value secondUser, + Value reallyGoodFriends}) { + return FriendshipsCompanion( + firstUser: firstUser ?? this.firstUser, + secondUser: secondUser ?? this.secondUser, + reallyGoodFriends: reallyGoodFriends ?? this.reallyGoodFriends, + ); + } } class $FriendshipsTable extends Friendships @@ -585,6 +609,29 @@ abstract class _$Database extends GeneratedDatabase { }).map((rows) => rows.map(_rowToAmountOfGoodFriendsResult).toList()); } + Future> friendsOf( + int user, + {@Deprecated('No longer needed with Moor 1.6 - see the changelog for details') + QueryEngine operateOn}) { + return (operateOn ?? this).customSelect( + 'SELECT u.* FROM friendships f\n INNER JOIN users u ON u.id IN (f.first_user, f.second_user) AND\n u.id != :user\n WHERE (f.first_user = :user OR f.second_user = :user)', + variables: [ + Variable.withInt(user), + ]).then((rows) => rows.map(_rowToUser).toList()); + } + + Stream> watchFriendsOf(int user) { + return customSelectStream( + 'SELECT u.* FROM friendships f\n INNER JOIN users u ON u.id IN (f.first_user, f.second_user) AND\n u.id != :user\n WHERE (f.first_user = :user OR f.second_user = :user)', + variables: [ + Variable.withInt(user), + ], + readsFrom: { + friendships, + users + }).map((rows) => rows.map(_rowToUser).toList()); + } + UserCountResult _rowToUserCountResult(QueryRow row) { return UserCountResult( cOUNTid: row.readInt('COUNT(id)'), diff --git a/extras/integration_tests/tests/lib/suite/crud_tests.dart b/extras/integration_tests/tests/lib/suite/crud_tests.dart new file mode 100644 index 000000000..1495bf410 --- /dev/null +++ b/extras/integration_tests/tests/lib/suite/crud_tests.dart @@ -0,0 +1,26 @@ +import 'package:test/test.dart'; +import 'package:tests/database/database.dart'; +import 'package:tests/suite/suite.dart'; + +void crudTests(TestExecutor executor) { + test('inserting updates a select stream', () async { + final db = Database(executor.createExecutor()); + final friends = db.watchFriendsOf(1); + + final a = await db.getUserById(1); + final b = await db.getUserById(2); + + final expectation = expectLater( + friends, + emitsInOrder( + [ + isEmpty, // initial state without friendships + [b] // after we called makeFriends(a,b) + ], + ), + ); + + await db.makeFriends(a, b); + await expectation; + }); +} diff --git a/extras/integration_tests/tests/lib/suite/suite.dart b/extras/integration_tests/tests/lib/suite/suite.dart index 5204ab65f..9c25ec1f3 100644 --- a/extras/integration_tests/tests/lib/suite/suite.dart +++ b/extras/integration_tests/tests/lib/suite/suite.dart @@ -1,5 +1,6 @@ import 'package:moor/moor.dart'; import 'package:test/test.dart'; +import 'package:tests/suite/crud_tests.dart'; import 'package:tests/suite/transactions.dart'; import 'custom_objects.dart'; @@ -17,6 +18,7 @@ void runAllTests(TestExecutor executor) { await executor.deleteData(); }); + crudTests(executor); migrationTests(executor); customObjectTests(executor); transactionTests(executor); diff --git a/extras/integration_tests/tests/pubspec.yaml b/extras/integration_tests/tests/pubspec.yaml index f2c1b74e0..56229c101 100644 --- a/extras/integration_tests/tests/pubspec.yaml +++ b/extras/integration_tests/tests/pubspec.yaml @@ -22,4 +22,6 @@ dependency_overrides: moor: path: ../../../moor moor_generator: - path: ../../../moor_generator \ No newline at end of file + path: ../../../moor_generator + sqlparser: + path: ../../../sqlparser \ No newline at end of file diff --git a/moor_generator/lib/src/parser/sql/sql_parser.dart b/moor_generator/lib/src/parser/sql/sql_parser.dart index 0217a2781..a5c45c432 100644 --- a/moor_generator/lib/src/parser/sql/sql_parser.dart +++ b/moor_generator/lib/src/parser/sql/sql_parser.dart @@ -1,4 +1,5 @@ import 'package:analyzer/dart/constant/value.dart'; +import 'package:build/build.dart'; import 'package:moor_generator/src/state/errors.dart'; import 'package:moor_generator/src/model/specified_table.dart'; import 'package:moor_generator/src/model/sql_query.dart'; @@ -49,8 +50,8 @@ class SqlParser { try { foundQueries.add(QueryHandler(name, context, _mapper).handle()); - } catch (e) { - print('Error while generating APIs for ${context.sql}: $e'); + } catch (e, s) { + log.warning('Error while generating APIs for ${context.sql}', e, s); } }); } diff --git a/netlify.toml b/netlify.toml index f09b00e60..8873b8ac1 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,7 @@ # We use netlify to deploy the documentation. [build] - base = "docs/" + base = "docs" publish = "docs/public" command = 'git submodule update --init --recursive && HUGO_ENV="production" hugo' diff --git a/sqlparser/lib/src/analysis/types/resolver.dart b/sqlparser/lib/src/analysis/types/resolver.dart index 528aae7a7..9ba8dab72 100644 --- a/sqlparser/lib/src/analysis/types/resolver.dart +++ b/sqlparser/lib/src/analysis/types/resolver.dart @@ -136,7 +136,9 @@ class TypeResolver { if (sqlParameters is ExprFunctionParameters) { return sqlParameters.parameters; } else if (sqlParameters is StarFunctionParameter) { - return call.scope.availableColumns; + // if * is used as a parameter, it refers to all columns in all tables + // that are available in the current scope. + return call.scope.availableColumns.whereType().toList(); } throw ArgumentError('Unknown parameters: $sqlParameters'); } diff --git a/sqlparser/lib/src/reader/tokenizer/scanner.dart b/sqlparser/lib/src/reader/tokenizer/scanner.dart index 450392877..989d56bdf 100644 --- a/sqlparser/lib/src/reader/tokenizer/scanner.dart +++ b/sqlparser/lib/src/reader/tokenizer/scanner.dart @@ -95,6 +95,11 @@ class Scanner { _addToken(TokenType.more); } break; + case '!': + if (_match('=')) { + _addToken(TokenType.exclamationEqual); + } + break; case '=': _addToken(_match('=') ? TokenType.doubleEqual : TokenType.equal); break; @@ -138,8 +143,7 @@ class Scanner { } else if (canStartColumnName(char)) { _identifier(); } else { - errors.add(TokenizerError( - 'Unexpected character.', SourceLocation(_currentOffset))); + errors.add(TokenizerError('Unexpected character.', _currentLocation)); } break; } diff --git a/sqlparser/test/scanner/single_token_tests.dart b/sqlparser/test/scanner/single_token_tests.dart index 764b2c6c0..5f7ae1f15 100644 --- a/sqlparser/test/scanner/single_token_tests.dart +++ b/sqlparser/test/scanner/single_token_tests.dart @@ -35,6 +35,7 @@ Map testCases = { '<': TokenType.less, '>=': TokenType.moreEqual, '>': TokenType.more, + '!=': TokenType.exclamationEqual, "'hello there'": TokenType.stringLiteral, '1.123': TokenType.numberLiteral, '1.32e5': TokenType.numberLiteral,