Parse NULL column constraints to fix parsing errors (#909)

This commit is contained in:
Simon Binder 2020-11-07 12:31:11 +01:00
parent def55a0d29
commit 1d7479226a
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
5 changed files with 50 additions and 1 deletions

View File

@ -56,6 +56,7 @@ abstract class ColumnConstraint extends AstNode {
T when<T>({
T Function(NotNull) notNull,
T Function(NullColumnConstraint) nullable,
T Function(PrimaryKeyColumn) primaryKey,
T Function(UniqueColumn) unique,
T Function(CheckColumn) check,
@ -66,6 +67,8 @@ abstract class ColumnConstraint extends AstNode {
}) {
if (this is NotNull) {
return notNull?.call(this as NotNull);
} else if (this is NullColumnConstraint) {
return nullable?.call(this as NullColumnConstraint);
} else if (this is PrimaryKeyColumn) {
return primaryKey?.call(this as PrimaryKeyColumn);
} else if (this is UniqueColumn) {
@ -96,6 +99,22 @@ abstract class ColumnConstraint extends AstNode {
enum ConflictClause { rollback, abort, fail, ignore, replace }
class NullColumnConstraint extends ColumnConstraint {
/// The `NULL` token forming this constraint.
Token $null;
NullColumnConstraint(String name, {this.$null}) : super(name);
@override
bool _equalToConstraint(NullColumnConstraint other) => true;
@override
Iterable<AstNode> get childNodes => const Iterable.empty();
@override
void transformChildren<A>(Transformer<A> transformer, A arg) {}
}
class NotNull extends ColumnConstraint {
final ConflictClause onConflict;

View File

@ -436,6 +436,11 @@ mixin SchemaParser on ParserBase {
autoIncrement: hasAutoInc, mode: mode, onConflict: conflict)
..setSpan(first, _previous);
}
if (_matchOne(TokenType.$null)) {
final nullToken = _previous;
return NullColumnConstraint(resolvedName, $null: nullToken)
..setSpan(nullToken, nullToken);
}
if (_matchOne(TokenType.not)) {
_suggestHint(HintDescription.token(TokenType.$null));

View File

@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS users (
id INT NOT NULL PRIMARY KEY DESC ON CONFLICT ROLLBACK AUTOINCREMENT,
email VARCHAR NOT NULL UNIQUE ON CONFLICT ABORT,
score INT CONSTRAINT "score set" NOT NULL DEFAULT 420 CHECK (score > 0),
display_name VARCHAR COLLATE BINARY
display_name VARCHAR NULL COLLATE BINARY
REFERENCES some(thing) ON UPDATE CASCADE ON DELETE SET NULL
DEFERRABLE INITIALLY DEFERRED,

View File

@ -69,6 +69,7 @@ void main() {
columnName: 'display_name',
typeName: 'VARCHAR',
constraints: [
NullColumnConstraint(null),
CollateConstraint(
null,
'BINARY',

View File

@ -0,0 +1,24 @@
import 'package:sqlparser/sqlparser.dart';
import 'package:test/test.dart';
// ignore_for_file: lines_longer_than_80_chars
void main() {
const stmts = [
'CREATE TABLE plots (owner_id VARCHAR NOT NULL, deleted INTEGER NOT NULL, last_synced INTEGER NULL, last_modified INTEGER NOT NULL, id VARCHAR NOT NULL, name VARCHAR NOT NULL, crop VARCHAR NOT NULL, crop_variety VARCHAR NOT NULL, planting_date INTEGER NOT NULL, is_outdoor_trial INTEGER NOT NULL CHECK (is_outdoor_trial in (0, 1)), uses_crop_blanket INTEGER NOT NULL CHECK (uses_crop_blanket in (0, 1)), is_raised_bed INTEGER NOT NULL CHECK (is_raised_bed in (0, 1)), plant_spacing REAL NOT NULL, row_spacing REAL NOT NULL, irrigation_type VARCHAR NOT NULL, rows_per_bed REAL NULL, bed_spacing REAL NULL, coordinates VARCHAR NOT NULL, PRIMARY KEY (id))',
'CREATE TABLE lab_reports (owner_id VARCHAR NOT NULL, deleted INTEGER NOT NULL, last_synced INTEGER NULL, last_modified INTEGER NOT NULL, id VARCHAR NOT NULL, sample_date INTEGER NOT NULL, source VARCHAR NOT NULL, PRIMARY KEY (id))',
'CREATE TABLE dissipated_active_ingredients (id VARCHAR NOT NULL, lab_report_id VARCHAR NOT NULL, active_ingredient_id VARCHAR NULL, name VARCHAR NULL, residue REAL NOT NULL, PRIMARY KEY (id))',
'CREATE TABLE sprays (owner_id VARCHAR NOT NULL, deleted INTEGER NOT NULL, last_synced INTEGER NULL, last_modified INTEGER NOT NULL, id VARCHAR NOT NULL, create_date INTEGER NOT NULL, application_method VARCHAR NOT NULL, growth_stage VARCHAR NOT NULL, PRIMARY KEY (id))',
'CREATE TABLE products (id VARCHAR NOT NULL, trade_name VARCHAR NOT NULL, type VARCHAR NOT NULL, owner_id VARCHAR NULL, PRIMARY KEY (id))',
'CREATE TABLE markets (owner_id VARCHAR NOT NULL, deleted INTEGER NOT NULL, last_synced INTEGER NULL, last_modified INTEGER NOT NULL, id VARCHAR NOT NULL, name VARCHAR NOT NULL, mrl REAL NOT NULL, arfd REAL NOT NULL, adi REAL NOT NULL, total_active_ingredients INTEGER NOT NULL, PRIMARY KEY (id))',
];
test('parses statements', () {
final engine = SqlEngine();
for (final stmt in stmts) {
final result = engine.parse(stmt);
expect(result.errors, isEmpty);
expect(result.rootNode, isA<CreateTableStatement>());
}
});
}