Parse more than ON CONFLICT clause

This commit is contained in:
Simon Binder 2021-03-13 15:21:27 +01:00
parent cf9ea89681
commit c9f8af346d
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
10 changed files with 106 additions and 56 deletions

View File

@ -1,5 +1,7 @@
## 0.15.0-dev
- __Breaking__: Change `InsertStatement.upsert` to a list of upsert clauses
- Support multiple upsert clauses
- Support `FROM` clauses in `UPDATE` statements
- Support `MATERIALIZED`/`NOT MATERIALIZED` hints in common table expressions
- Add `BuiltInMathExtension` which corresponds to the `-DSQLITE_ENABLE_MATH_FUNCTIONS`

View File

@ -102,7 +102,7 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
},
);
visitNullable(e.upsert, const NoTypeExpectation());
visitList(e.upsert, const NoTypeExpectation());
}
@override

View File

@ -7,9 +7,9 @@ import 'node.dart';
import 'statements/create_index.dart';
import 'statements/select.dart';
import 'statements/statement.dart';
import 'statements/update.dart';
import 'visitor.dart';
export 'clauses/upsert.dart';
export 'node.dart';
export 'statements/block.dart';
export 'statements/create_index.dart';
@ -27,7 +27,6 @@ export 'visitor.dart';
// todo: Split up this mega-library
part 'clauses/limit.dart';
part 'clauses/ordering.dart';
part 'clauses/upsert.dart';
part 'clauses/with.dart';
part 'common/queryables.dart';
part 'common/renamable.dart';

View File

@ -1,4 +1,6 @@
part of '../ast.dart';
import '../ast.dart'; // todo: Remove this import
import '../node.dart';
import '../statements/create_index.dart' show IndexedColumn;
class UpsertClause extends AstNode implements HasWhereClause {
final List<IndexedColumn>? onColumns;

View File

@ -1,5 +1,6 @@
import '../../analysis/analysis.dart';
import '../ast.dart'; // todo: Remove this import
import '../clauses/upsert.dart';
import '../node.dart';
import '../visitor.dart';
import 'statement.dart';
@ -20,7 +21,7 @@ class InsertStatement extends CrudStatement implements HasPrimarySource {
TableReference? table;
final List<Reference> targetColumns;
InsertSource source;
UpsertClause? upsert;
final List<UpsertClause> upsert;
List<Column?>? get resolvedTargetColumns {
if (targetColumns.isNotEmpty) {
@ -37,7 +38,7 @@ class InsertStatement extends CrudStatement implements HasPrimarySource {
required this.table,
required this.targetColumns,
required this.source,
this.upsert})
this.upsert = const []})
: super(withClause);
@override
@ -58,7 +59,7 @@ class InsertStatement extends CrudStatement implements HasPrimarySource {
table!,
...targetColumns,
source,
if (upsert != null) upsert!
...upsert,
];
}

View File

@ -584,9 +584,9 @@ extension VisitChildrenExtension<A, R> on AstVisitor<A, R?> {
}
/// Visits all [nodes] in sequence.
R? visitList(Iterable<AstNode?> nodes, A arg) {
R? visitList(Iterable<AstNode> nodes, A arg) {
for (final node in nodes) {
node!.accept(this, arg);
node.accept(this, arg);
}
return null;
}

View File

@ -1538,7 +1538,10 @@ class Parser {
'Expected clpsing parenthesis after column list');
}
final source = _insertSource();
final upsert = _upsertClauseOrNull();
final upsert = <UpsertClause>[];
while (_check(TokenType.on)) {
upsert.add(_upsertClause());
}
return InsertStatement(
withClause: withClause,
@ -1572,10 +1575,8 @@ class Parser {
}
}
UpsertClause? _upsertClauseOrNull() {
if (!_matchOne(TokenType.on)) return null;
final first = _previous;
UpsertClause _upsertClause() {
final first = _consume(TokenType.on);
_consume(TokenType.conflict, 'Expected CONFLICT keyword for upsert clause');
List<IndexedColumn>? indexedColumns;

View File

@ -715,7 +715,7 @@ class NodeSqlBuilder extends AstVisitor<void, void> {
}
visit(e.source, arg);
visitNullable(e.upsert, arg);
_join(e.upsert, '');
}
@override

View File

@ -64,7 +64,7 @@ void main() {
table: TableReference('tbl'),
targetColumns: const [],
source: DefaultValues(),
upsert: UpsertClause(action: DoNothing()),
upsert: [UpsertClause(action: DoNothing())],
),
);
});
@ -76,7 +76,8 @@ void main() {
table: TableReference('tbl'),
targetColumns: const [],
source: DefaultValues(),
upsert: UpsertClause(
upsert: [
UpsertClause(
onColumns: [
IndexedColumn(Reference(columnName: 'foo')),
IndexedColumn(
@ -86,6 +87,7 @@ void main() {
],
action: DoNothing(),
),
],
),
);
});
@ -97,7 +99,8 @@ void main() {
table: TableReference('tbl'),
targetColumns: const [],
source: DefaultValues(),
upsert: UpsertClause(
upsert: [
UpsertClause(
onColumns: [
IndexedColumn(Reference(columnName: 'foo')),
IndexedColumn(Reference(columnName: 'bar')),
@ -109,6 +112,7 @@ void main() {
),
action: DoNothing(),
),
],
),
);
});
@ -120,16 +124,19 @@ void main() {
table: TableReference('tbl'),
targetColumns: const [],
source: DefaultValues(),
upsert: UpsertClause(
upsert: [
UpsertClause(
action: DoUpdate(
[
SetComponent(
column: Reference(columnName: 'foo'),
expression: NumericLiteral(2, token(TokenType.numberLiteral)),
expression:
NumericLiteral(2, token(TokenType.numberLiteral)),
),
],
),
),
],
),
);
});
@ -141,12 +148,14 @@ void main() {
table: TableReference('tbl'),
targetColumns: const [],
source: DefaultValues(),
upsert: UpsertClause(
upsert: [
UpsertClause(
action: DoUpdate(
[
SetComponent(
column: Reference(columnName: 'foo'),
expression: NumericLiteral(2, token(TokenType.numberLiteral)),
expression:
NumericLiteral(2, token(TokenType.numberLiteral)),
),
],
where: NumberedVariable(
@ -154,6 +163,36 @@ void main() {
),
),
),
],
),
);
});
test('having more than one clause', () {
testStatement(
'$prefix (foo) DO NOTHING ON CONFLICT (bar) DO UPDATE SET x = 2',
InsertStatement(
table: TableReference('tbl'),
targetColumns: const [],
source: DefaultValues(),
upsert: [
UpsertClause(
onColumns: [IndexedColumn(Reference(columnName: 'foo'))],
action: DoNothing(),
),
UpsertClause(
onColumns: [IndexedColumn(Reference(columnName: 'bar'))],
action: DoUpdate(
[
SetComponent(
column: Reference(columnName: 'x'),
expression:
NumericLiteral(2, token(TokenType.numberLiteral)),
),
],
),
),
],
),
);
});

View File

@ -247,6 +247,12 @@ CREATE UNIQUE INDEX my_idx ON t1 (c1, c2, c3) WHERE c1 < c3;
testFormat('INSERT INTO foo VALUES (1, 2, 3) '
'ON CONFLICT DO UPDATE SET a = b, c = d WHERE d < a;');
});
test('upsert - multiple clauses', () {
testFormat('INSERT INTO foo VALUES (1, 2, 3) '
'ON CONFLICT DO NOTHING '
'ON CONFLICT DO UPDATE SET a = b, c = d WHERE d < a;');
});
});
group('update', () {