mirror of https://github.com/AMT-Cheif/drift.git
Provide lints on insert statements that will fail
This commit is contained in:
parent
3cb00a4b31
commit
876db0671e
|
@ -10,6 +10,8 @@ final _leadingDigits = RegExp(r'^\d*');
|
|||
abstract class SqlQuery {
|
||||
final String name;
|
||||
final AnalysisContext fromContext;
|
||||
List<AnalysisError> lints;
|
||||
|
||||
String get sql => fromContext.sql;
|
||||
|
||||
/// The variables that appear in the [sql] query. We support three kinds of
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import '../query_handler.dart';
|
||||
|
||||
class Linter {
|
||||
final QueryHandler handler;
|
||||
final List<AnalysisError> lints = [];
|
||||
|
||||
Linter(this.handler);
|
||||
|
||||
void reportLints() {
|
||||
handler.context.root.accept(_LintingVisitor(this));
|
||||
}
|
||||
}
|
||||
|
||||
class _LintingVisitor extends RecursiveVisitor<void> {
|
||||
final Linter linter;
|
||||
|
||||
_LintingVisitor(this.linter);
|
||||
|
||||
@override
|
||||
void visitInsertStatement(InsertStatement e) {
|
||||
final targeted = e.resolvedTargetColumns;
|
||||
if (targeted == null) return;
|
||||
|
||||
// First, check that the amount of values matches the declaration.
|
||||
e.source.when(
|
||||
isValues: (values) {
|
||||
for (var tuple in values.values) {
|
||||
if (tuple.expressions.length != targeted.length) {
|
||||
linter.lints.add(AnalysisError(
|
||||
type: AnalysisErrorType.other,
|
||||
message: 'Expected tuple to have ${targeted.length} values',
|
||||
relevantNode: tuple,
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
isSelect: (select) {
|
||||
final columns = select.stmt.resolvedColumns;
|
||||
|
||||
if (columns.length != targeted.length) {
|
||||
linter.lints.add(AnalysisError(
|
||||
type: AnalysisErrorType.other,
|
||||
message: 'This select statement should return ${targeted.length} '
|
||||
'columns, but actually returns ${columns.length}',
|
||||
relevantNode: select.stmt,
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// second, check that no required columns are left out
|
||||
final specifiedTable =
|
||||
linter.handler.mapper.tableToMoor(e.table.resolved as Table);
|
||||
final required =
|
||||
specifiedTable.columns.where((c) => c.requiredDuringInsert).toList();
|
||||
|
||||
if (required.isNotEmpty && e.source is DefaultValues) {
|
||||
linter.lints.add(AnalysisError(
|
||||
type: AnalysisErrorType.other,
|
||||
message: 'This table has columns without default values, so defaults '
|
||||
'can\'t be used for insert.',
|
||||
relevantNode: e.table,
|
||||
));
|
||||
} else {
|
||||
final notPresent = required.where((c) => !targeted
|
||||
.any((t) => t.name.toUpperCase() == c.name.name.toUpperCase()));
|
||||
|
||||
if (notPresent.isNotEmpty) {
|
||||
final msg = notPresent.join(', ');
|
||||
|
||||
linter.lints.add(AnalysisError(
|
||||
type: AnalysisErrorType.other,
|
||||
message: 'Some columns are required but not present here. Expected '
|
||||
'values for $msg.',
|
||||
relevantNode: e.source.childNodes.first,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
|||
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
|
||||
|
||||
import 'affected_tables_visitor.dart';
|
||||
import 'lints/linter.dart';
|
||||
|
||||
class QueryHandler {
|
||||
final String name;
|
||||
|
@ -19,11 +20,20 @@ class QueryHandler {
|
|||
QueryHandler(this.name, this.context, this.mapper);
|
||||
|
||||
SqlQuery handle() {
|
||||
final root = context.root;
|
||||
_foundVariables = mapper.extractVariables(context);
|
||||
|
||||
_verifyNoSkippedIndexes();
|
||||
final query = _mapToMoor();
|
||||
|
||||
final linter = Linter(this);
|
||||
linter.reportLints();
|
||||
query.lints = linter.lints;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
SqlQuery _mapToMoor() {
|
||||
final root = context.root;
|
||||
if (root is SelectStatement) {
|
||||
return _handleSelect();
|
||||
} else if (root is UpdateStatement ||
|
||||
|
|
|
@ -54,5 +54,15 @@ class SqlParser {
|
|||
log.warning('Error while generating APIs for ${context.sql}', e, s);
|
||||
}
|
||||
});
|
||||
|
||||
// report lints
|
||||
for (var query in foundQueries) {
|
||||
for (var lint in query.lints) {
|
||||
session.errors.add(MoorError(
|
||||
critical: false,
|
||||
message: 'Lint for ${query.name}: $lint',
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,4 +56,5 @@ enum AnalysisErrorType {
|
|||
ambiguousReference,
|
||||
|
||||
unknownFunction,
|
||||
other,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue