///|
pub(all) struct MySQL {}
///|
pub impl Dialect for MySQL with supports_string_literal_backslash_escape(_self) {
true
}
///|
pub impl Dialect for MySQL with supports_boolean_literals(_self) {
true
}
///|
pub impl Dialect for MySQL with supports_filter_during_aggregation(_self) {
false
}
///|
pub impl Dialect for MySQL with supports_within_after_array_aggregation(_self) {
false
}
///|
pub impl Dialect for MySQL with requires_column_types_in_create_table(_self) {
false
}
///|
pub impl Dialect for MySQL with supports_double_quoted_identifiers(_self) {
false
}
///|
pub impl Dialect for MySQL with supports_if_not_exists(_self) {
true
}
///|
pub impl Dialect for MySQL with read_keyword(_self : MySQL, word : String) -> Keyword? {
match word {
// MySQL-specific LOAD DATA keywords
"load" => Some(Load)
"data" => Some(Data)
"local" => Some(Local)
"infile" => Some(Infile)
"fields" => Some(Fields)
"lines" => Some(Lines)
"terminated" => Some(Terminated)
"enclosed" => Some(Enclosed)
"escaped" => Some(Escaped)
"starting" => Some(Starting)
"optionally" => Some(Optionally)
_ => None
}
}
///|
pub impl Dialect for MySQL with parse_statement(
_self : MySQL,
parser : Parser,
tokens : ArrayView[Token],
) -> ParserResult[Statement]? raise ParserError {
match tokens {
// Handle various combinations of LOAD DATA keyword/identifier recognition
[Keyword(Load), Keyword(Data), .. _rest] => {
let (load_data_stmt, tokens) = parser.parse_load_data_statement(tokens)
let tokens = if tokens is [Semicolon, .. tokens] {
tokens
} else {
tokens
}
Some((Statement::LoadData(load_data_stmt), tokens))
}
[Keyword(Lock), Keyword(Tables), Identifier(table_name), Semicolon, .. rest] => {
// MySQL LOCK TABLES statement
let table = ObjectName::{ parts: [table_name] }
Some((Statement::LockTables([table]), rest))
}
[Keyword(Unlock), Keyword(Tables), Semicolon, .. rest] =>
// MySQL UNLOCK TABLES statement
Some((Statement::UnlockTables, rest))
_ => None // Fall back to generic parsing
}
}
///|
pub impl Dialect for MySQL with parse_expr(
_self : MySQL,
_tokens : ArrayView[Token],
) -> ParserResult[Expr]? raise ParserError {
// MySQL DIV operator needs to be parsed in the main parser as it's infix
// For now, returning None to use the generic parsing
None
}
///|
test "MySQL SHOW TABLES" {
let tokens = "SHOW TABLES;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW TABLES;
),
)
}
///|
test "MySQL SHOW COLUMNS FROM table" {
let tokens = "SHOW COLUMNS FROM users;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW COLUMNS FROM users;
),
)
}
///|
test "MySQL SHOW TABLES LIKE pattern" {
let tokens = "SHOW TABLES LIKE 'user%';"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW TABLES LIKE 'user%';
),
)
}
///|
test "MySQL with backticks in SHOW" {
let tokens = "SHOW COLUMNS FROM `table name`;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW COLUMNS FROM `table name`;
),
)
}
///|
test "MySQL LOCK TABLES" {
let tokens = "LOCK TABLES users;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|LOCK TABLES users;
),
)
}
///|
test "MySQL UNLOCK TABLES" {
let tokens = "UNLOCK TABLES;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|UNLOCK TABLES;
),
)
}
///|
test "MySQL DIV operator" {
let tokens = "SELECT 10 DIV 3 FROM test;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SELECT
#| 10 DIV 3
#|FROM
#| test;
),
)
}
///|
test "MySQL SHOW VARIABLES" {
let tokens = "SHOW VARIABLES;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW VARIABLES;
),
)
}
///|
test "MySQL SHOW GLOBAL STATUS" {
let tokens = "SHOW GLOBAL STATUS;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW GLOBAL STATUS;
),
)
}
///|
test "MySQL SHOW FULL PROCESSLIST" {
let tokens = "SHOW FULL PROCESSLIST;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW FULL PROCESSLIST;
),
)
}
///|
test "MySQL SHOW CREATE TABLE" {
let tokens = "SHOW CREATE TABLE users;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW CREATE TABLE users;
),
)
}
///|
test "MySQL SHOW EXTENDED COLUMNS" {
let tokens = "SHOW EXTENDED COLUMNS FROM users LIKE 'name%';"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SHOW EXTENDED COLUMNS FROM users LIKE 'name%';
),
)
}
///|
test "MySQL SET session variable" {
let tokens = "SET SESSION sql_mode = 'STRICT_TRANS_TABLES';"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
),
)
}
///|
test "MySQL SET global variable" {
let tokens = "SET GLOBAL max_connections = 200;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SET GLOBAL max_connections = 200;
),
)
}
///|
test "MySQL SET user variable" {
let tokens = "SET @counter = 1;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SET @counter = 1;
),
)
}
///|
test "MySQL SET multiple variables" {
let tokens = "SET @name = 'John', @age = 30, @salary = 50000.50;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SET @name = 'John', @age = 30, @salary = 50000.5;
),
)
}
///|
test "MySQL SET local variable" {
let tokens = "SET autocommit = 0;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|SET autocommit = 0;
),
)
}
///|
test "MySQL REPLACE INTO statement" {
let tokens = "REPLACE INTO users (id, name) VALUES (1, 'John');"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|REPLACE INTO users (id, name) VALUES (1, 'John');
),
)
}
///|
test "MySQL REPLACE INTO with multiple values" {
let tokens = "REPLACE INTO products (id, name, price) VALUES (1, 'Widget', 10.50), (2, 'Gadget', 15.00);"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|REPLACE INTO products (id, name, price) VALUES (1, 'Widget', 10.5), (2, 'Gadget', 15);
),
)
}
///|
test "MySQL INSERT OR REPLACE statement" {
let tokens = "INSERT OR REPLACE INTO settings (key, value) VALUES ('theme', 'dark');"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|REPLACE INTO settings (key, value) VALUES ('theme', 'dark');
),
)
}
///|
test "MySQL INSERT with ON DUPLICATE KEY UPDATE" {
let tokens = "INSERT INTO users (id, name, email) VALUES (1, 'John', 'john@example.com') ON DUPLICATE KEY UPDATE name = 'John Updated', email = 'john.new@example.com';"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|INSERT INTO users (id, name, email) VALUES (1, 'John', 'john@example.com') ON DUPLICATE KEY UPDATE name = 'John Updated', email = 'john.new@example.com';
),
)
}
///|
test "MySQL INSERT single assignment ON DUPLICATE KEY UPDATE" {
let tokens = "INSERT INTO counters (id, count) VALUES (1, 1) ON DUPLICATE KEY UPDATE count = count + 1;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|INSERT INTO counters (id, count) VALUES (1, 1) ON DUPLICATE KEY UPDATE count = count + 1;
),
)
}
///|
test "MySQL REPLACE with ON DUPLICATE KEY UPDATE" {
let tokens = "REPLACE INTO settings (setting_key, setting_value) VALUES ('theme', 'dark') ON DUPLICATE KEY UPDATE setting_value = 'light';"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|REPLACE INTO settings (setting_key, setting_value) VALUES ('theme', 'dark') ON DUPLICATE KEY UPDATE setting_value = 'light';
),
)
}
///|
test "MySQL basic LOAD DATA statement" {
let tokens = "LOAD DATA INFILE '/tmp/data.csv' INTO TABLE users;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|LOAD DATA INFILE '/tmp/data.csv'
#| INTO TABLE users;
),
)
}
///|
test "MySQL LOAD DATA with LOCAL and REPLACE" {
let tokens = "LOAD DATA LOCAL INFILE '/tmp/users.txt' REPLACE INTO TABLE employees;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|LOAD DATA LOCAL INFILE '/tmp/users.txt'
#| REPLACE INTO TABLE employees;
),
)
}
///|
test "MySQL LOAD DATA with FIELDS options" {
let tokens = "LOAD DATA INFILE '/data/products.csv' INTO TABLE products CHARACTER SET utf8 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"';"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|LOAD DATA INFILE '/data/products.csv'
#| INTO TABLE products
#| CHARACTER SET utf8
#| FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"';
),
)
}
///|
test "MySQL LOAD DATA with LINES options" {
let tokens = "LOAD DATA INFILE '/tmp/logs.txt' INTO TABLE logs LINES STARTING BY '>>>' TERMINATED BY '\n' IGNORE 5 LINES;"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|LOAD DATA INFILE '/tmp/logs.txt'
#| INTO TABLE logs
#| LINES STARTING BY '>>>' TERMINATED BY '
#|'
#| IGNORE 5 LINES;
),
)
}
///|
test "MySQL LOAD DATA with column list and SET" {
let tokens = "LOAD DATA INFILE '/data/sales.csv' IGNORE INTO TABLE sales FIELDS TERMINATED BY ',' (date, amount, customer) SET created_at = now();"
let stmt = parse_sql(dialect=MySQL::{ }, tokens).stmts[0] |> pretty_print
inspect(
stmt,
content=(
#|LOAD DATA INFILE '/data/sales.csv'
#| IGNORE INTO TABLE sales
#| FIELDS TERMINATED BY ','
#| (date, amount, customer)
#| SET created_at = now();
),
)
}