mirror of https://github.com/AMT-Cheif/drift.git
Parse CREATE TABLE statements
This commit is contained in:
parent
888e429467
commit
dee9993c83
|
@ -65,11 +65,11 @@ package to generate type-safe methods from sql.
|
||||||
Most on this list is just not supported yet because I didn't found a use case for
|
Most on this list is just not supported yet because I didn't found a use case for
|
||||||
them yet. If you need them, just leave an issue and I'll try to implement them soon.
|
them yet. If you need them, just leave an issue and I'll try to implement them soon.
|
||||||
|
|
||||||
- For now, only `INSERT` statements are not supported, but they will be soon
|
- For now, `INSERT` statements are not supported, but they will be soon.
|
||||||
- Windowing is not supported yet
|
- Windowing is not supported yet
|
||||||
- Compound select statements (`UNION` / `INTERSECT`) are not supported yet
|
- Compound select statements (`UNION` / `INTERSECT`) are not supported yet
|
||||||
- Common table expressions are not supported
|
- Common table expressions are not supported
|
||||||
- Some advanced expressions, like `COLLATE` or `CAST`s aren't supported yet.
|
- Some advanced expressions, like `CAST`s aren't supported yet.
|
||||||
|
|
||||||
If you run into parsing errors with what you think is valid sql, please create an issue.
|
If you run into parsing errors with what you think is valid sql, please create an issue.
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,10 @@ class ColumnDefinition extends AstNode {
|
||||||
abstract class ColumnConstraint extends AstNode {
|
abstract class ColumnConstraint extends AstNode {
|
||||||
// todo foreign key clause
|
// todo foreign key clause
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
ColumnConstraint(this.name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
T accept<T>(AstVisitor<T> visitor) => visitor.visitColumnConstraint(this);
|
T accept<T>(AstVisitor<T> visitor) => visitor.visitColumnConstraint(this);
|
||||||
|
|
||||||
|
@ -54,43 +58,60 @@ abstract class ColumnConstraint extends AstNode {
|
||||||
throw Exception('Did not expect $runtimeType as a ColumnConstraint');
|
throw Exception('Did not expect $runtimeType as a ColumnConstraint');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForOverriding
|
||||||
|
bool _equalToConstraint(covariant ColumnConstraint other);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool contentEquals(ColumnConstraint other) {
|
||||||
|
return other.name == name && _equalToConstraint(other);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ConflictClause { rollback, abort, fail, ignore, replace }
|
enum ConflictClause { rollback, abort, fail, ignore, replace }
|
||||||
|
|
||||||
class NotNull extends ColumnConstraint {
|
class NotNull extends ColumnConstraint {
|
||||||
|
final ConflictClause onConflict;
|
||||||
|
|
||||||
|
NotNull(String name, {this.onConflict}) : super(name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Iterable<AstNode> childNodes = const [];
|
final Iterable<AstNode> childNodes = const [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(NotNull other) => true;
|
bool _equalToConstraint(NotNull other) => onConflict == other.onConflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrimaryKey extends ColumnConstraint {
|
class PrimaryKey extends ColumnConstraint {
|
||||||
final bool autoIncrement;
|
final bool autoIncrement;
|
||||||
|
final ConflictClause onConflict;
|
||||||
final OrderingMode mode;
|
final OrderingMode mode;
|
||||||
|
|
||||||
PrimaryKey(this.autoIncrement, this.mode);
|
PrimaryKey(String name,
|
||||||
|
{this.autoIncrement = false, this.mode, this.onConflict})
|
||||||
|
: super(name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Iterable<AstNode> get childNodes => null;
|
Iterable<AstNode> get childNodes => const [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(PrimaryKey other) {
|
bool _equalToConstraint(PrimaryKey other) {
|
||||||
return other.autoIncrement == autoIncrement && other.mode == mode;
|
return other.autoIncrement == autoIncrement &&
|
||||||
|
other.mode == mode &&
|
||||||
|
other.onConflict == onConflict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Unique extends ColumnConstraint {
|
class Unique extends ColumnConstraint {
|
||||||
final ConflictClause onConflict;
|
final ConflictClause onConflict;
|
||||||
|
|
||||||
Unique(this.onConflict);
|
Unique(String name, this.onConflict) : super(name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Iterable<AstNode> get childNodes => const [];
|
Iterable<AstNode> get childNodes => const [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(Unique other) {
|
bool _equalToConstraint(Unique other) {
|
||||||
return other.onConflict == onConflict;
|
return other.onConflict == onConflict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,35 +119,35 @@ class Unique extends ColumnConstraint {
|
||||||
class Check extends ColumnConstraint {
|
class Check extends ColumnConstraint {
|
||||||
final Expression expression;
|
final Expression expression;
|
||||||
|
|
||||||
Check(this.expression);
|
Check(String name, this.expression) : super(name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Iterable<AstNode> get childNodes => [expression];
|
Iterable<AstNode> get childNodes => [expression];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(Check other) => true;
|
bool _equalToConstraint(Check other) => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Default extends ColumnConstraint {
|
class Default extends ColumnConstraint {
|
||||||
final Expression expression;
|
final Expression expression;
|
||||||
|
|
||||||
Default(this.expression);
|
Default(String name, this.expression) : super(name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Iterable<AstNode> get childNodes => [expression];
|
Iterable<AstNode> get childNodes => [expression];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(Default other) => true;
|
bool _equalToConstraint(Default other) => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CollateConstraint extends ColumnConstraint {
|
class CollateConstraint extends ColumnConstraint {
|
||||||
final String collation;
|
final String collation;
|
||||||
|
|
||||||
CollateConstraint(this.collation);
|
CollateConstraint(String name, this.collation) : super(name);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Iterable<AstNode> childNodes = const [];
|
final Iterable<AstNode> childNodes = const [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool contentEquals(CollateConstraint other) => true;
|
bool _equalToConstraint(CollateConstraint other) => true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ class Parser {
|
||||||
Token get _peekNext => tokens[_current + 1];
|
Token get _peekNext => tokens[_current + 1];
|
||||||
Token get _previous => tokens[_current - 1];
|
Token get _previous => tokens[_current - 1];
|
||||||
|
|
||||||
bool _match(List<TokenType> types) {
|
bool _match(Iterable<TokenType> types) {
|
||||||
for (var type in types) {
|
for (var type in types) {
|
||||||
if (_check(type)) {
|
if (_check(type)) {
|
||||||
_advance();
|
_advance();
|
||||||
|
@ -110,7 +110,7 @@ class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement statement() {
|
Statement statement() {
|
||||||
final stmt = select() ?? _deleteStmt() ?? _update();
|
final stmt = select() ?? _deleteStmt() ?? _update() ?? _createTable();
|
||||||
|
|
||||||
_matchOne(TokenType.semicolon);
|
_matchOne(TokenType.semicolon);
|
||||||
if (!_isAtEnd) {
|
if (!_isAtEnd) {
|
||||||
|
@ -390,14 +390,17 @@ class Parser {
|
||||||
OrderingTerm _orderingTerm() {
|
OrderingTerm _orderingTerm() {
|
||||||
final expr = expression();
|
final expr = expression();
|
||||||
|
|
||||||
|
return OrderingTerm(expression: expr, orderingMode: _orderingModeOrNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderingMode _orderingModeOrNull() {
|
||||||
if (_match(const [TokenType.asc, TokenType.desc])) {
|
if (_match(const [TokenType.asc, TokenType.desc])) {
|
||||||
final mode = _previous.type == TokenType.asc
|
final mode = _previous.type == TokenType.asc
|
||||||
? OrderingMode.ascending
|
? OrderingMode.ascending
|
||||||
: OrderingMode.descending;
|
: OrderingMode.descending;
|
||||||
return OrderingTerm(expression: expr, orderingMode: mode);
|
return mode;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return OrderingTerm(expression: expr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a [Limit] clause, or returns null if there is no limit token after
|
/// Parses a [Limit] clause, or returns null if there is no limit token after
|
||||||
|
@ -463,6 +466,177 @@ class Parser {
|
||||||
or: failureMode, table: table, set: set, where: where);
|
or: failureMode, table: table, set: set, where: where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreateTableStatement _createTable() {
|
||||||
|
if (!_matchOne(TokenType.create)) return null;
|
||||||
|
final first = _previous;
|
||||||
|
|
||||||
|
_consume(TokenType.table, 'Expected TABLE keyword here');
|
||||||
|
|
||||||
|
var ifNotExists = false;
|
||||||
|
|
||||||
|
if (_matchOne(TokenType.$if)) {
|
||||||
|
_consume(TokenType.not, 'Expected IF to be followed by NOT EXISTS');
|
||||||
|
_consume(TokenType.exists, 'Expected IF NOT to be followed by EXISTS');
|
||||||
|
ifNotExists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final tableIdentifier =
|
||||||
|
_consume(TokenType.identifier, 'Expected a table name')
|
||||||
|
as IdentifierToken;
|
||||||
|
|
||||||
|
// we don't currently support CREATE TABLE x AS SELECT ... statements
|
||||||
|
_consume(
|
||||||
|
TokenType.leftParen, 'Expected opening parenthesis to list columns');
|
||||||
|
|
||||||
|
final columns = <ColumnDefinition>[];
|
||||||
|
do {
|
||||||
|
columns.add(_columnDefinition());
|
||||||
|
} while (_matchOne(TokenType.comma));
|
||||||
|
// todo parse table constraints
|
||||||
|
|
||||||
|
_consume(TokenType.rightParen, 'Expected closing parenthesis');
|
||||||
|
|
||||||
|
var withoutRowId = false;
|
||||||
|
if (_matchOne(TokenType.without)) {
|
||||||
|
_consume(
|
||||||
|
TokenType.rowid, 'Expected ROWID to complete the WITHOUT ROWID part');
|
||||||
|
withoutRowId = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateTableStatement(
|
||||||
|
ifNotExists: ifNotExists,
|
||||||
|
tableName: tableIdentifier.identifier,
|
||||||
|
withoutRowId: withoutRowId,
|
||||||
|
columns: columns,
|
||||||
|
)..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnDefinition _columnDefinition() {
|
||||||
|
final name = _consume(TokenType.identifier, 'Expected a column name')
|
||||||
|
as IdentifierToken;
|
||||||
|
IdentifierToken typeName;
|
||||||
|
|
||||||
|
if (_matchOne(TokenType.identifier)) {
|
||||||
|
typeName = _previous as IdentifierToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
final constraints = <ColumnConstraint>[];
|
||||||
|
ColumnConstraint constraint;
|
||||||
|
while ((constraint = _columnConstraint(orNull: true)) != null) {
|
||||||
|
constraints.add(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColumnDefinition(
|
||||||
|
columnName: name.identifier,
|
||||||
|
typeName: typeName?.identifier,
|
||||||
|
constraints: constraints,
|
||||||
|
)..setSpan(name, _previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnConstraint _columnConstraint({bool orNull = false}) {
|
||||||
|
Token first;
|
||||||
|
IdentifierToken name;
|
||||||
|
if (_matchOne(TokenType.constraint)) {
|
||||||
|
first = _previous;
|
||||||
|
name = _consume(
|
||||||
|
TokenType.identifier, 'Expect a name for the constraint here')
|
||||||
|
as IdentifierToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
final resolvedName = name?.identifier;
|
||||||
|
|
||||||
|
if (_matchOne(TokenType.primary)) {
|
||||||
|
// set reference to first token in this constraint if not set because of
|
||||||
|
// the CONSTRAINT token
|
||||||
|
first ??= _previous;
|
||||||
|
_consume(TokenType.key, 'Expected KEY to complete PRIMARY KEY clause');
|
||||||
|
|
||||||
|
final mode = _orderingModeOrNull();
|
||||||
|
final conflict = _conflictClauseOrNull();
|
||||||
|
final hasAutoInc = _matchOne(TokenType.autoincrement);
|
||||||
|
|
||||||
|
return PrimaryKey(resolvedName,
|
||||||
|
autoIncrement: hasAutoInc, mode: mode, onConflict: conflict)
|
||||||
|
..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.not)) {
|
||||||
|
first ??= _previous;
|
||||||
|
_consume(TokenType.$null, 'Expected NULL to complete NOT NULL');
|
||||||
|
|
||||||
|
return NotNull(resolvedName, onConflict: _conflictClauseOrNull())
|
||||||
|
..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.unique)) {
|
||||||
|
first ??= _previous;
|
||||||
|
return Unique(resolvedName, _conflictClauseOrNull())
|
||||||
|
..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.check)) {
|
||||||
|
first ??= _previous;
|
||||||
|
_consume(TokenType.leftParen, 'Expected opening parenthesis');
|
||||||
|
final expr = expression();
|
||||||
|
_consume(TokenType.rightParen, 'Expected closing parenthesis');
|
||||||
|
|
||||||
|
return Check(resolvedName, expr)..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.$default)) {
|
||||||
|
first ??= _previous;
|
||||||
|
Expression expr = _literalOrNull();
|
||||||
|
|
||||||
|
if (expr == null) {
|
||||||
|
// no literal, expect (expression)
|
||||||
|
_consume(TokenType.leftParen,
|
||||||
|
'Expected opening parenthesis before expression');
|
||||||
|
expr = expression();
|
||||||
|
_consume(TokenType.rightParen, 'Expected closing parenthesis');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Default(resolvedName, expr);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.collate)) {
|
||||||
|
first ??= _previous;
|
||||||
|
final collation =
|
||||||
|
_consume(TokenType.identifier, 'Expected the collation name')
|
||||||
|
as IdentifierToken;
|
||||||
|
|
||||||
|
return CollateConstraint(resolvedName, collation.identifier)
|
||||||
|
..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo foreign key clauses
|
||||||
|
|
||||||
|
// no known column constraint matched. If orNull is set and we're not
|
||||||
|
// guaranteed to be in a constraint clause (started with CONSTRAINT), we
|
||||||
|
// can return null
|
||||||
|
if (orNull && name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
_error('Expected a constraint (primary key, nullability, etc.)');
|
||||||
|
}
|
||||||
|
|
||||||
|
ConflictClause _conflictClauseOrNull() {
|
||||||
|
if (_matchOne(TokenType.on)) {
|
||||||
|
_consume(TokenType.conflict,
|
||||||
|
'Expected CONFLICT to complete ON CONFLICT clause');
|
||||||
|
|
||||||
|
const modes = {
|
||||||
|
TokenType.rollback: ConflictClause.rollback,
|
||||||
|
TokenType.abort: ConflictClause.abort,
|
||||||
|
TokenType.fail: ConflictClause.fail,
|
||||||
|
TokenType.ignore: ConflictClause.ignore,
|
||||||
|
TokenType.replace: ConflictClause.replace,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_match(modes.keys)) {
|
||||||
|
return modes[_previous.type];
|
||||||
|
} else {
|
||||||
|
_error('Expected a conflict handler (rollback, abort, etc.) here');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/* We parse expressions here.
|
/* We parse expressions here.
|
||||||
* Operators have the following precedence:
|
* Operators have the following precedence:
|
||||||
* - + ~ NOT (unary)
|
* - + ~ NOT (unary)
|
||||||
|
@ -681,21 +855,41 @@ class Parser {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Literal _literalOrNull() {
|
||||||
|
final token = _peek;
|
||||||
|
|
||||||
|
Literal _parseInner() {
|
||||||
|
if (_matchOne(TokenType.numberLiteral)) {
|
||||||
|
return NumericLiteral(_parseNumber(token.lexeme), token);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.stringLiteral)) {
|
||||||
|
return StringLiteral(token as StringLiteralToken);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.$null)) {
|
||||||
|
return NullLiteral(token);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.$true)) {
|
||||||
|
return BooleanLiteral.withTrue(token);
|
||||||
|
}
|
||||||
|
if (_matchOne(TokenType.$false)) {
|
||||||
|
return BooleanLiteral.withFalse(token);
|
||||||
|
}
|
||||||
|
// todo CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final literal = _parseInner();
|
||||||
|
literal?.setSpan(token, token);
|
||||||
|
return literal;
|
||||||
|
}
|
||||||
|
|
||||||
Expression _primary() {
|
Expression _primary() {
|
||||||
|
final literal = _literalOrNull();
|
||||||
|
if (literal != null) return literal;
|
||||||
|
|
||||||
final token = _advance();
|
final token = _advance();
|
||||||
final type = token.type;
|
final type = token.type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TokenType.numberLiteral:
|
|
||||||
return NumericLiteral(_parseNumber(token.lexeme), token);
|
|
||||||
case TokenType.stringLiteral:
|
|
||||||
return StringLiteral(token as StringLiteralToken);
|
|
||||||
case TokenType.$null:
|
|
||||||
return NullLiteral(token);
|
|
||||||
case TokenType.$true:
|
|
||||||
return BooleanLiteral.withTrue(token);
|
|
||||||
case TokenType.$false:
|
|
||||||
return BooleanLiteral.withFalse(token);
|
|
||||||
// todo CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP
|
|
||||||
case TokenType.leftParen:
|
case TokenType.leftParen:
|
||||||
// Opening brackets could be three things: An inner select statement
|
// Opening brackets could be three things: An inner select statement
|
||||||
// (SELECT ...), a parenthesised expression, or a tuple of expressions
|
// (SELECT ...), a parenthesised expression, or a tuple of expressions
|
||||||
|
|
|
@ -100,6 +100,14 @@ enum TokenType {
|
||||||
$if,
|
$if,
|
||||||
without,
|
without,
|
||||||
rowid,
|
rowid,
|
||||||
|
constraint,
|
||||||
|
autoincrement,
|
||||||
|
primary,
|
||||||
|
key,
|
||||||
|
unique,
|
||||||
|
check,
|
||||||
|
$default,
|
||||||
|
conflict,
|
||||||
|
|
||||||
semicolon,
|
semicolon,
|
||||||
eof,
|
eof,
|
||||||
|
@ -165,6 +173,14 @@ const Map<String, TokenType> keywords = {
|
||||||
'IF': TokenType.$if,
|
'IF': TokenType.$if,
|
||||||
'WITHOUT': TokenType.without,
|
'WITHOUT': TokenType.without,
|
||||||
'ROWID': TokenType.rowid,
|
'ROWID': TokenType.rowid,
|
||||||
|
'CONSTRAINT': TokenType.constraint,
|
||||||
|
'AUTOINCREMENT': TokenType.autoincrement,
|
||||||
|
'PRIMARY': TokenType.primary,
|
||||||
|
'KEY': TokenType.key,
|
||||||
|
'UNIQUE': TokenType.unique,
|
||||||
|
'CHECK': TokenType.check,
|
||||||
|
'DEFAULT': TokenType.$default,
|
||||||
|
'CONFLICT': TokenType.conflict,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Token {
|
class Token {
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:sqlparser/src/ast/ast.dart';
|
||||||
|
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
|
final statement = '''
|
||||||
|
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
|
||||||
|
)
|
||||||
|
''';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testStatement(
|
||||||
|
statement,
|
||||||
|
CreateTableStatement(
|
||||||
|
tableName: 'users',
|
||||||
|
ifNotExists: true,
|
||||||
|
withoutRowId: false,
|
||||||
|
columns: [
|
||||||
|
ColumnDefinition(
|
||||||
|
columnName: 'id',
|
||||||
|
typeName: 'INT',
|
||||||
|
constraints: [
|
||||||
|
NotNull(null),
|
||||||
|
PrimaryKey(
|
||||||
|
null,
|
||||||
|
autoIncrement: true,
|
||||||
|
onConflict: ConflictClause.rollback,
|
||||||
|
mode: OrderingMode.descending,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ColumnDefinition(
|
||||||
|
columnName: 'email',
|
||||||
|
typeName: 'VARCHAR',
|
||||||
|
constraints: [
|
||||||
|
NotNull(null),
|
||||||
|
Unique(null, ConflictClause.abort),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ColumnDefinition(
|
||||||
|
columnName: 'score',
|
||||||
|
typeName: 'INT',
|
||||||
|
constraints: [
|
||||||
|
NotNull('score set'),
|
||||||
|
Default(null, NumericLiteral(420, token(TokenType.numberLiteral))),
|
||||||
|
Check(
|
||||||
|
null,
|
||||||
|
BinaryExpression(
|
||||||
|
Reference(columnName: 'score'),
|
||||||
|
token(TokenType.more),
|
||||||
|
NumericLiteral(
|
||||||
|
0,
|
||||||
|
token(TokenType.numberLiteral),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ColumnDefinition(
|
||||||
|
columnName: 'display_name',
|
||||||
|
typeName: 'VARCHAR',
|
||||||
|
constraints: [
|
||||||
|
CollateConstraint(
|
||||||
|
null,
|
||||||
|
'BINARY',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue