Skip to content

Commit

Permalink
Use common class for AST variables
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Jan 21, 2025
1 parent c5b8cae commit 8eb5d38
Show file tree
Hide file tree
Showing 21 changed files with 126 additions and 58 deletions.
8 changes: 4 additions & 4 deletions drift_dev/lib/src/analysis/resolver/file_analysis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ class FileAnalyzer {
final variable = parameter.variable;

if (parameter.isRequired) {
if (variable is ColonNamedVariable) {
requiredName.add(variable.name);
if (variable is NamedVariable) {
requiredName.add(variable.fullName);
} else if (variable is NumberedVariable) {
requiredIndex.add(variable.resolvedIndex!);
}
Expand All @@ -234,8 +234,8 @@ class FileAnalyzer {
.resolveColumnType(parameter.typeName)
.withNullable(parameter.orNull);

if (variable is ColonNamedVariable) {
namedHints[variable.name] = type;
if (variable is NamedVariable) {
namedHints[variable.fullName] = type;
} else if (variable is NumberedVariable) {
indexedHints[variable.resolvedIndex!] = type;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ class QueryAnalyzer {
}

currentIndex = used.resolvedIndex!;
final name = (used is ColonNamedVariable) ? used.name : null;
final name = (used is NamedVariable) ? used.fullName : null;
final explicitIndex =
(used is NumberedVariable) ? used.explicitIndex : null;
final forCapture = used.meta<CapturedVariable>();
Expand Down
4 changes: 2 additions & 2 deletions drift_dev/lib/src/analysis/results/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,12 @@ class CapturedVariable {
///
/// This variable is not mounted to the same syntax tree as [reference], it
/// will be mounted into the tree returned by [addHelperNodes].
final ColonNamedVariable introducedVariable;
final NamedVariable introducedVariable;

String get helperColumn => '\$n_$queryGlobalId';

CapturedVariable(this.reference, this.queryGlobalId)
: introducedVariable = ColonNamedVariable.synthetic(':r$queryGlobalId') {
: introducedVariable = NamedVariable.synthetic(':', 'r$queryGlobalId') {
introducedVariable.setMeta<CapturedVariable>(this);
}
}
Expand Down
2 changes: 1 addition & 1 deletion drift_dev/lib/src/writer/queries/sql_writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class SqlWriter extends NodeSqlBuilder {
}

@override
void visitNamedVariable(ColonNamedVariable e, void arg) {
void visitNamedVariable(NamedVariable e, void arg) {
final found = _findVariable(e);
if (found != null) {
_writeAnalyzedVariable(found);
Expand Down
2 changes: 1 addition & 1 deletion drift_dev/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dependencies:
# Drift-specific analysis and apis
drift: ">=2.23.0 <2.24.0"
sqlite3: ^2.4.6
sqlparser: ^0.40.0
sqlparser: ^0.41.0

# Dart analysis
analyzer: ^6.4.1
Expand Down
4 changes: 4 additions & 0 deletions sqlparser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.41.0

- Replace `ColonNamedVariable` with `NamedVariable` for all named variables.

## 0.40.0

- Add support for the `dbstat` module.
Expand Down
4 changes: 2 additions & 2 deletions sqlparser/lib/src/analysis/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class AnalyzeStatementOptions {
final index = variable.resolvedIndex;
if (index != null && indexedVariableTypes.containsKey(index)) {
return indexedVariableTypes[index];
} else if (variable is ColonNamedVariable) {
return namedVariableTypes[variable.name];
} else if (variable is NamedVariable) {
return namedVariableTypes[variable.fullName];
}
return null;
}
Expand Down
6 changes: 3 additions & 3 deletions sqlparser/lib/src/analysis/steps/prepare_ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class AstPreparingVisitor extends RecursiveVisitor<void, void> {
}

@override
void visitNamedVariable(ColonNamedVariable e, void arg) {
void visitNamedVariable(NamedVariable e, void arg) {
_foundVariables.add(e);
visitChildren(e, arg);
}
Expand All @@ -177,11 +177,11 @@ class AstPreparingVisitor extends RecursiveVisitor<void, void> {
variable.resolvedIndex = largestAssigned + 1;
largestAssigned++;
}
} else if (variable is ColonNamedVariable) {
} else if (variable is NamedVariable) {
// named variables behave just like numbered vars without an explicit
// index, but of course two variables with the same name must have the
// same index.
final index = resolvedNames.putIfAbsent(variable.name, () {
final index = resolvedNames.putIfAbsent(variable.fullName, () {
return ++largestAssigned;
});
variable.resolvedIndex = index;
Expand Down
11 changes: 8 additions & 3 deletions sqlparser/lib/src/ast/expressions/variables.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ class NumberedVariable extends Expression implements Variable {
Iterable<AstNode> get childNodes => const [];
}

class ColonNamedVariable extends Expression implements Variable {
class NamedVariable extends Expression implements Variable {
final String name;

/// The [name] plus a variable prefix (e.g. `:user`)
final String fullName;

@override
int? resolvedIndex;

ColonNamedVariable.synthetic(this.name);
NamedVariable.synthetic(String prefix, this.name) : fullName = '$prefix$name';

ColonNamedVariable(ColonVariableToken token) : name = token.name;
NamedVariable(NamedVariableToken token)
: fullName = token.fullName,
name = token.name;

@override
R accept<A, R>(AstVisitor<A, R> visitor, A arg) {
Expand Down
4 changes: 2 additions & 2 deletions sqlparser/lib/src/ast/visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ abstract class AstVisitor<A, R> {
R visitIndexedColumn(IndexedColumn e, A arg);

R visitNumberedVariable(NumberedVariable e, A arg);
R visitNamedVariable(ColonNamedVariable e, A arg);
R visitNamedVariable(NamedVariable e, A arg);

R visitBlock(Block block, A arg);
R visitSemicolonSeparatedStatements(SemicolonSeparatedStatements e, A arg);
Expand Down Expand Up @@ -564,7 +564,7 @@ class RecursiveVisitor<A, R> implements AstVisitor<A, R?> {
}

@override
R? visitNamedVariable(ColonNamedVariable e, A arg) {
R? visitNamedVariable(NamedVariable e, A arg) {
return visitVariable(e, arg);
}

Expand Down
11 changes: 10 additions & 1 deletion sqlparser/lib/src/reader/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -974,9 +974,18 @@ class Parser {
..token = token
..setSpan(token, token);
} else if (_matchOne(TokenType.colonVariable)) {
return ColonNamedVariable(_previous as ColonVariableToken)
return NamedVariable(_previous as ColonVariableToken)
..setSpan(_previous, _previous);
} else if (!enableDriftExtensions) {
// These have special meanings in drift files, but are general variables
// otherwise.
if (_match(
const [TokenType.atSignVariable, TokenType.dollarSignVariable])) {
return NamedVariable(_previous as NamedVariableToken)
..setSpan(_previous, _previous);
}
}

return null;
}

Expand Down
2 changes: 1 addition & 1 deletion sqlparser/lib/src/reader/tokenizer/scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class Scanner {
if (name == null) {
_addToken(TokenType.colon);
} else {
tokens.add(ColonVariableToken(_currentSpan, ':$name'));
tokens.add(ColonVariableToken(_currentSpan, name));
}
break;
case $dollar:
Expand Down
44 changes: 30 additions & 14 deletions sqlparser/lib/src/reader/tokenizer/token.dart
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,24 @@ class IdentifierToken extends Token {
: super(TokenType.identifier, span);
}

abstract class VariableToken extends Token {
sealed class VariableToken extends Token {
VariableToken(super.type, super.span);
}

class QuestionMarkVariableToken extends Token {
sealed class NamedVariableToken extends VariableToken {
/// The name of this variable, not including the [prefix].
final String name;

/// The prefix introducing this variable, specific to the token type. E.g.
/// ":" for [ColonVariableToken].
String get prefix;

String get fullName => prefix + name;

NamedVariableToken(this.name, super.type, super.span);
}

class QuestionMarkVariableToken extends VariableToken {
/// The explicit index, if this variable was of the form `?123`. Otherwise
/// null.
final int? explicitIndex;
Expand All @@ -530,25 +543,28 @@ class QuestionMarkVariableToken extends Token {
: super(TokenType.questionMarkVariable, span);
}

class ColonVariableToken extends Token {
final String name;
class ColonVariableToken extends NamedVariableToken {
@override
String get prefix => ':';

ColonVariableToken(FileSpan span, this.name)
: super(TokenType.colonVariable, span);
ColonVariableToken(FileSpan span, String name)
: super(name, TokenType.colonVariable, span);
}

class DollarSignVariableToken extends Token {
final String name;
class DollarSignVariableToken extends NamedVariableToken {
@override
String get prefix => r'$';

DollarSignVariableToken(FileSpan span, this.name)
: super(TokenType.dollarSignVariable, span);
DollarSignVariableToken(FileSpan span, String name)
: super(name, TokenType.dollarSignVariable, span);
}

class AtSignVariableToken extends Token {
final String name;
class AtSignVariableToken extends NamedVariableToken {
@override
String get prefix => '@';

AtSignVariableToken(FileSpan span, this.name)
: super(TokenType.atSignVariable, span);
AtSignVariableToken(FileSpan span, String name)
: super(name, TokenType.atSignVariable, span);
}

/// Inline Dart appearing in a create table statement. Only parsed when the
Expand Down
6 changes: 3 additions & 3 deletions sqlparser/lib/src/utils/ast_equality.dart
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,9 @@ class EqualityEnforcingVisitor implements AstVisitor<void, void> {
}

@override
void visitNamedVariable(ColonNamedVariable e, void arg) {
final current = _currentAs<ColonNamedVariable>(e);
_assert(current.name == e.name, e);
void visitNamedVariable(NamedVariable e, void arg) {
final current = _currentAs<NamedVariable>(e);
_assert(current.fullName == e.fullName, e);
_checkChildren(e);
}

Expand Down
5 changes: 2 additions & 3 deletions sqlparser/lib/utils/node_to_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -906,9 +906,8 @@ class NodeSqlBuilder extends AstVisitor<void, void> {
}

@override
void visitNamedVariable(ColonNamedVariable e, void arg) {
// Note: The name already starts with the colon
symbol(e.name, spaceBefore: true, spaceAfter: true);
void visitNamedVariable(NamedVariable e, void arg) {
symbol(e.fullName, spaceBefore: true, spaceAfter: true);
}

@override
Expand Down
2 changes: 1 addition & 1 deletion sqlparser/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: sqlparser
description: Parses sqlite statements and performs static analysis on them
version: 0.40.0
version: 0.41.0
homepage: https://github.com/simolus3/drift/tree/develop/sqlparser
repository: https://github.com/simolus3/drift
#homepage: https://drift.simonbinder.eu/
Expand Down
8 changes: 4 additions & 4 deletions sqlparser/test/analysis/types2/resolver_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,10 @@ WITH RECURSIVE
).session;

Variable? start, end;
for (final variable in session.context.root.allDescendants
.whereType<ColonNamedVariable>()) {
if (variable.name == ':start') start = variable;
if (variable.name == ':end') end = variable;
for (final variable
in session.context.root.allDescendants.whereType<NamedVariable>()) {
if (variable.name == 'start') start = variable;
if (variable.name == 'end') end = variable;
}
assert(start != null && end != null);

Expand Down
14 changes: 7 additions & 7 deletions sqlparser/test/parser/drift_file_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,17 @@ void main() {
SelectStatement(
columns: [
ExpressionResultColumn(
expression: ColonNamedVariable(
ColonVariableToken(fakeSpan(':foo'), ':foo'),
expression: NamedVariable(
ColonVariableToken(fakeSpan(':foo'), 'foo'),
),
),
],
where: DartExpressionPlaceholder(name: 'predicate'),
),
parameters: [
VariableTypeHint(
ColonNamedVariable(
ColonVariableToken(fakeSpan(':foo'), ':foo'),
NamedVariable(
ColonVariableToken(fakeSpan(':foo'), 'foo'),
),
'TEXT',
orNull: true,
Expand Down Expand Up @@ -205,20 +205,20 @@ SELECT DISTINCT A.* FROM works A, works B ON A.id =
});

test('parses REQUIRED without type hint', () {
final variable = ColonVariableToken(fakeSpan(':category'), ':category');
final variable = ColonVariableToken(fakeSpan(':category'), 'category');
testDriftFile(
'test(REQUIRED :category): SELECT :category;',
DriftFile([
DeclaredStatement(
SimpleName('test'),
SelectStatement(columns: [
ExpressionResultColumn(
expression: ColonNamedVariable(variable),
expression: NamedVariable(variable),
),
]),
parameters: [
VariableTypeHint(
ColonNamedVariable(variable),
NamedVariable(variable),
null,
isRequired: true,
),
Expand Down
29 changes: 27 additions & 2 deletions sqlparser/test/parser/expression_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:sqlparser/src/ast/ast.dart';
import 'package:sqlparser/sqlparser.dart';
import 'package:sqlparser/src/reader/parser.dart';
import 'package:sqlparser/src/reader/tokenizer/scanner.dart';
import 'package:sqlparser/src/utils/ast_equality.dart';
Expand Down Expand Up @@ -48,7 +48,7 @@ final Map<String, Expression> _testCases = {
NumberedVariable(2),
),
token(TokenType.doubleEqual),
ColonNamedVariable(ColonVariableToken(fakeSpan(':test'), ':test')),
NamedVariable(ColonVariableToken(fakeSpan(':test'), 'test')),
),
'CASE x WHEN a THEN b WHEN c THEN d ELSE e END': CaseExpression(
base: Reference(columnName: 'x'),
Expand Down Expand Up @@ -238,4 +238,29 @@ void main() {
});
});
});

group('named variables', () {
test('support all kinds when not in drift mode', () {
for (final prefix in [':', '@', r'$']) {
final result = SqlEngine().parse('SELECT ${prefix}name;');
final value = ((result.rootNode as SelectStatement).columns.single
as ExpressionResultColumn)
.expression as NamedVariable;

expect(value.name, 'name');
expect(value.fullName, '${prefix}name');
}
});

test('does not parse at and dollar signs as variables in drift mode', () {
final engine = SqlEngine(EngineOptions(driftOptions: DriftSqlOptions()));
expect(engine.parse('SELECT @name').rootNode, isA<InvalidStatement>());

final result = engine.parse(r'SELECT $name;');
final value = ((result.rootNode as SelectStatement).columns.single
as ExpressionResultColumn)
.expression;
expect(value, isA<DartExpressionPlaceholder>());
});
});
}
Loading

0 comments on commit 8eb5d38

Please sign in to comment.