///|
fn parse_create_table_statement(
tokens : ArrayView[Token]
) -> Parser[CreateTableStmt] raise ParserError {
let tokens = expect_token(tokens, Keyword(Create))
let tokens = expect_token(tokens, Keyword(Table))
match tokens {
[Token::Identifier(name), .. tokens] => {
let (columns, tokens) = parse_column_defs(tokens)
({ name, columns, primary_key: None }, tokens)
}
_ =>
raise UnexpectedTokenError(
tokens[0],
Token::Identifier("table_name".to_string()),
)
}
}
///|
test "Create table" {
let tokens = "CREATE TABLE users;"
let stmt = parse_sql(tokens)[0]
inspect(
stmt,
content=(
#|CREATE TABLE users (
#|
#|);
),
)
}
///|
fn parse_column_defs(
tokens : ArrayView[Token]
) -> Parser[Array[ColumnDef]] raise ParserError {
let columns = []
let tokens = if tokens is [Token::LParen, .. tokens] {
loop tokens {
[Identifier(name), .. tokens] => {
let (data_type, tokens) = parse_data_type(tokens)
let (options, tokens) = parse_column_def_option(tokens)
columns.push({ name, data_type, options })
// let (option, tokens) = try parse_column_option() {} noraise {}
match tokens {
[Comma, .. tokens] => continue tokens
[RParen, .. tokens] => break tokens
[token, ..] =>
raise UnexpectedTokenMessageError(
token, "expected ',' or ')', found",
)
[] => raise InternalBug("parse_column_defs: unexpected end of tokens")
}
continue tokens
}
[token, .. _tokens] =>
raise UnexpectedTokenMessageError(token, "expected column definition")
[] => raise InternalBug("parse_column_defs: unexpected end of tokens")
}
} else {
tokens
}
(columns, tokens)
}
///|
fn parse_data_type(
tokens : ArrayView[Token]
) -> Parser[DataType] raise ParserError {
match tokens {
[Keyword(Integer), .. tokens] => (Integer, tokens)
[Keyword(Int), .. tokens] => (Integer, tokens)
[Keyword(Float), LParen, Number(n), RParen, .. tokens] => {
let n = @strconv.parse_int(n) catch {
_ => raise ParserError::InvalidDataTypeError(n)
}
(Float(Some(n)), tokens)
}
[Keyword(Float), .. tokens] => (Float(None), tokens)
[Keyword(Real), .. tokens] => (Real, tokens)
[Keyword(Double), .. tokens] => (Double, tokens)
[Keyword(Char), LParen, Number(n), RParen, .. tokens] => {
let n = @strconv.parse_int(n) catch {
_ => raise ParserError::InvalidDataTypeError(n)
}
(Char(n), tokens)
}
[Keyword(Varchar), LParen, Number(n), RParen, .. tokens] => {
let n = @strconv.parse_int(n) catch {
_ => raise ParserError::InvalidDataTypeError(n)
}
(Varchar(n), tokens)
}
[Keyword(Text), .. tokens] => (Text, tokens)
[Keyword(Boolean), .. tokens] => (Boolean, tokens)
[Keyword(Timestamp), .. tokens] => (Timestamp, tokens)
[Keyword(Blob), .. tokens] => (Blob, tokens)
[token, .. _tokens] =>
raise UnexpectedTokenMessageError(token, "expected data type")
[] => raise InternalBug("parse_data_type: unexpected end of tokens")
}
}
///|
test "Create table with two columns" {
let tokens = "CREATE TABLE users (id INTEGER, name INTEGER);"
let stmt = parse_sql(tokens)[0]
inspect(
stmt,
content=(
#|CREATE TABLE users (
#| id INTEGER,
#| name INTEGER
#|);
),
)
}
///|
test "Many kinds of data types" {
let tokens =
#|CREATE TABLE users (
#| id INTEGER,
#| name VARCHAR(100),
#| age INT,
#| active BOOLEAN,
#| balance FLOAT(2),
#| created_at TIMESTAMP,
#| updated_at TIMESTAMP,
#| description TEXT,
#| profile_picture BLOB
#|);
let stmt = parse_sql(tokens)[0]
inspect(
stmt,
content=(
#|CREATE TABLE users (
#| id INTEGER,
#| name VARCHAR(100),
#| age INTEGER,
#| active BOOLEAN,
#| balance FLOAT(2),
#| created_at TIMESTAMP,
#| updated_at TIMESTAMP,
#| description TEXT,
#| profile_picture BLOB
#|);
),
)
}
///|
fn parse_column_def_option(
tokens : ArrayView[Token]
) -> Parser[Array[ColumnDefOption]] raise ParserError {
let options = []
let tokens = loop tokens {
[Keyword(Unique), .. tokens] => {
options.push(ColumnDefOption::Unique)
continue tokens
}
[Keyword(Not), Keyword(Null), .. tokens] => {
options.push(NotNull)
continue tokens
}
[Keyword(Default), .. tokens] => {
let (expr, tokens) = parse_expr(tokens)
options.push(Default(expr))
continue tokens
}
tokens => break tokens
}
(options, tokens)
}
///|
test "With options" {
let tokens = "CREATE TABLE users (id INTEGER NOT NULL UNIQUE, name INTEGER DEFAULT 18);"
let stmt = parse_sql(tokens)[0]
inspect(
stmt,
content=(
#|CREATE TABLE users (
#| id INTEGER NOT NULL UNIQUE,
#| name INTEGER DEFAULT 18
#|);
),
)
}