diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index defcb5f0..94764a6d 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -926,6 +926,22 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { readsFrom: {config}).map(_rowToConfig); } + ReadRowIdResult _rowToReadRowIdResult(QueryRow row) { + return ReadRowIdResult( + rowid: row.readInt('rowid'), + configKey: row.readString('config_key'), + configValue: row.readString('config_value'), + ); + } + + Selectable readRowId(Expression expr) { + final generatedexpr = $write(expr); + return customSelectQuery( + 'SELECT oid, * FROM config WHERE _rowid_ = ${generatedexpr.sql}', + variables: [...generatedexpr.introducedVariables], + readsFrom: {config}).map(_rowToReadRowIdResult); + } + Future writeConfig(String key, String value) { return customInsert( 'REPLACE INTO config VALUES (:key, :value)', @@ -938,3 +954,24 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { List get allTables => [noIds, withDefaults, withConstraints, config, mytable]; } + +class ReadRowIdResult { + final int rowid; + final String configKey; + final String configValue; + ReadRowIdResult({ + this.rowid, + this.configKey, + this.configValue, + }); + @override + int get hashCode => $mrjf( + $mrjc(rowid.hashCode, $mrjc(configKey.hashCode, configValue.hashCode))); + @override + bool operator ==(other) => + identical(this, other) || + (other is ReadRowIdResult && + other.rowid == this.rowid && + other.configKey == this.configKey && + other.configValue == this.configValue); +} diff --git a/moor/test/data/tables/tables.moor b/moor/test/data/tables/tables.moor index 2bbd7403..972fecb8 100644 --- a/moor/test/data/tables/tables.moor +++ b/moor/test/data/tables/tables.moor @@ -29,4 +29,6 @@ CREATE TABLE mytable ( readConfig: SELECT * FROM config WHERE config_key = ?; readMultiple: SELECT * FROM config WHERE config_key IN ? ORDER BY $clause; -readDynamic: SELECT * FROM config WHERE $predicate; \ No newline at end of file +readDynamic: SELECT * FROM config WHERE $predicate; + +readRowId: SELECT oid, * FROM config WHERE _rowid_ = $expr; \ No newline at end of file diff --git a/moor_generator/lib/src/analyzer/sql_queries/query_handler.dart b/moor_generator/lib/src/analyzer/sql_queries/query_handler.dart index 2d3d65c1..5efd061f 100644 --- a/moor_generator/lib/src/analyzer/sql_queries/query_handler.dart +++ b/moor_generator/lib/src/analyzer/sql_queries/query_handler.dart @@ -124,7 +124,7 @@ class QueryHandler { // we have established that all columns in resultEntryToColumn do appear // in the moor table. Now check for set equality. - if (resultEntryToColumn.length != moorTable.columns.length) { + if (rawColumns.length != moorTable.columns.length) { matches = false; } diff --git a/sqlparser/lib/src/analysis/schema/column.dart b/sqlparser/lib/src/analysis/schema/column.dart index 3cec6e8b..51154bfa 100644 --- a/sqlparser/lib/src/analysis/schema/column.dart +++ b/sqlparser/lib/src/analysis/schema/column.dart @@ -98,9 +98,12 @@ class ReferenceExpressionColumn extends ExpressionColumn { Reference get reference => expression as Reference; @override - String get name => reference.resolvedColumn.name; + String get name => overriddenName ?? reference.resolvedColumn?.name; - ReferenceExpressionColumn(Reference ref) : super(name: null, expression: ref); + final String overriddenName; + + ReferenceExpressionColumn(Reference ref, {this.overriddenName}) + : super(name: null, expression: ref); } /// The result column of a [CompoundSelectStatement]. diff --git a/sqlparser/lib/src/analysis/schema/table.dart b/sqlparser/lib/src/analysis/schema/table.dart index 2364180c..04279479 100644 --- a/sqlparser/lib/src/analysis/schema/table.dart +++ b/sqlparser/lib/src/analysis/schema/table.dart @@ -68,7 +68,8 @@ class Table with ResultSet, VisibleToChildren, HasMetaMixin { // handle aliases to rowids, see https://www.sqlite.org/lang_createtable.html#rowid if (aliasesForRowId.contains(name.toLowerCase()) && !withoutRowId) { - return _rowIdColumn ?? RowId(); + return _rowIdColumn ?? RowId() + ..table = this; } return null; } diff --git a/sqlparser/lib/src/analysis/steps/column_resolver.dart b/sqlparser/lib/src/analysis/steps/column_resolver.dart index 16506971..8124fc52 100644 --- a/sqlparser/lib/src/analysis/steps/column_resolver.dart +++ b/sqlparser/lib/src/analysis/steps/column_resolver.dart @@ -131,7 +131,8 @@ class ColumnResolver extends RecursiveVisitor { String name; if (expression is Reference) { - column = ReferenceExpressionColumn(expression); + column = ReferenceExpressionColumn(expression, + overriddenName: resultColumn.as); if (resultColumn.as != null) name = resultColumn.as; } else { name = _nameOfResultColumn(resultColumn); diff --git a/sqlparser/lib/src/analysis/steps/type_resolver.dart b/sqlparser/lib/src/analysis/steps/type_resolver.dart index 987cce4b..0d84d04b 100644 --- a/sqlparser/lib/src/analysis/steps/type_resolver.dart +++ b/sqlparser/lib/src/analysis/steps/type_resolver.dart @@ -12,7 +12,7 @@ class TypeResolvingVisitor extends RecursiveVisitor { @override void visitChildren(AstNode e) { // called for every ast node, so we implement this here - if (e is Expression) { + if (e is Expression && !types.needsToBeInferred(e)) { types.resolveExpression(e); } else if (e is SelectStatement) { e.resolvedColumns.forEach(types.resolveColumn); @@ -41,8 +41,12 @@ class TypeResolvingVisitor extends RecursiveVisitor { } } } - } - visitChildren(e); + // we already handled the source tuples, don't visit them + visitChildren(e.table); + e.targetColumns.forEach(visitChildren); + } else { + visitChildren(e); + } } } diff --git a/sqlparser/lib/src/analysis/types/resolver.dart b/sqlparser/lib/src/analysis/types/resolver.dart index 5e9badcc..da6f93a1 100644 --- a/sqlparser/lib/src/analysis/types/resolver.dart +++ b/sqlparser/lib/src/analysis/types/resolver.dart @@ -49,10 +49,14 @@ class TypeResolver { return !containsVariable; } + bool needsToBeInferred(Typeable t) { + return t is Variable || t is DartExpressionPlaceholder; + } + ResolveResult resolveOrInfer(Typeable t) { if (t is Column) { return resolveColumn(t); - } else if (t is Variable || t is DartExpressionPlaceholder) { + } else if (needsToBeInferred(t)) { return inferType(t as Expression); } else if (t is Expression) { return resolveExpression(t); diff --git a/sqlparser/lib/src/engine/sql_engine.dart b/sqlparser/lib/src/engine/sql_engine.dart index 56aca60c..93411dce 100644 --- a/sqlparser/lib/src/engine/sql_engine.dart +++ b/sqlparser/lib/src/engine/sql_engine.dart @@ -132,9 +132,8 @@ class SqlEngine { ..accept(ReferenceResolver(context)) ..accept(TypeResolvingVisitor(context)); } - } catch (e) { - // todo should we do now? AFAIK, everything that causes an exception - // is added as an error contained in the context. + } catch (_) { + rethrow; } } }