use sql_dialect_fmt_parser::{parse, SyntaxKind};
use sql_dialect_fmt_test_support::parser::{assert_parse_clean, assert_parse_roundtrip};
const CLEAN: &[&str] = &[
"CREATE TABLE t (id INT)",
"CREATE TABLE t (id INT, name VARCHAR(100))",
"CREATE TABLE mydb.sch.t (id NUMBER(38, 0), ts TIMESTAMP_NTZ, payload VARIANT)",
"CREATE TABLE t (id INT NOT NULL)",
"CREATE TABLE t (id INT DEFAULT 0)",
"CREATE TABLE t (name STRING DEFAULT 'anon')",
"CREATE TABLE t (id INT NOT NULL DEFAULT 0, name STRING)",
"CREATE TABLE t (id INT PRIMARY KEY)",
"CREATE TABLE t (id INT UNIQUE)",
"CREATE TABLE t (a VARCHAR(10) COLLATE 'en-ci')",
"CREATE TABLE t (a INT COMMENT 'the a column')",
"CREATE TABLE t (id INT AUTOINCREMENT, name STRING)",
"CREATE TABLE t (a INT, b INT, PRIMARY KEY (a))",
"CREATE TABLE t (a INT, b INT, PRIMARY KEY (a, b))",
"CREATE TABLE t (a INT, b INT, UNIQUE (a, b))",
"CREATE TABLE t (a INT, b INT, FOREIGN KEY (b) REFERENCES u (id))",
"CREATE TABLE t (a INT, CONSTRAINT pk PRIMARY KEY (a))",
"CREATE TABLE t (a INT, b INT, CONSTRAINT fk FOREIGN KEY (b) REFERENCES u (id))",
"CREATE TABLE t (a INT, CHECK (a > 0))",
"CREATE TABLE t (a INT, b INT, c INT, PRIMARY KEY (a), UNIQUE (b), FOREIGN KEY (c) REFERENCES u (id))",
"CREATE OR REPLACE TABLE t (a INT)",
"CREATE TEMPORARY TABLE t (a INT)",
"CREATE TRANSIENT TABLE t (a INT)",
"CREATE VOLATILE TABLE t (a INT)",
"CREATE LOCAL TEMPORARY TABLE t (a INT)",
"CREATE TABLE IF NOT EXISTS t (a INT)",
"CREATE TABLE t (a INT, b INT) CLUSTER BY (a)",
"CREATE TABLE t (a INT, b INT) CLUSTER BY (a, b)",
"CREATE TABLE t CLONE src",
"CREATE OR REPLACE TABLE t CLONE mydb.sch.src",
"CREATE TABLE t AS SELECT a, b FROM u",
"CREATE TABLE t (x, y) AS SELECT a, b FROM s",
"CREATE TABLE t AS WITH c AS (SELECT 1 AS n) SELECT n FROM c",
"CREATE TABLE t AS SELECT a FROM u UNION ALL SELECT a FROM v",
"CREATE VIEW v AS SELECT a FROM t",
"CREATE OR REPLACE VIEW v AS SELECT * FROM t",
"CREATE SECURE VIEW v AS SELECT a FROM t",
"CREATE MATERIALIZED VIEW mv AS SELECT a FROM t WHERE a > 0",
"CREATE RECURSIVE VIEW rv AS SELECT 1",
"CREATE OR REPLACE SECURE VIEW v (a, b) AS SELECT a, b FROM t",
"CREATE VIEW IF NOT EXISTS v AS SELECT a FROM t",
"CREATE VIEW v (id, total) COMMENT = 'a view' AS SELECT id, sum(x) FROM t GROUP BY id",
"CREATE OR REPLACE SECURE MATERIALIZED VIEW mv AS SELECT a FROM t",
"DROP TABLE t",
"DROP TABLE IF EXISTS t",
"DROP TABLE IF EXISTS db.s.t",
"DROP TABLE t CASCADE",
"DROP TABLE IF EXISTS t CASCADE",
"DROP TABLE IF EXISTS t RESTRICT",
"DROP VIEW v",
"DROP VIEW IF EXISTS v",
"DROP VIEW IF EXISTS db.s.v RESTRICT",
];
#[test]
fn all_ddl_parses_clean_and_round_trips() {
for s in CLEAN {
assert_parse_clean(s);
}
}
fn has(s: &str, kind: SyntaxKind) -> bool {
parse(s).syntax().descendants().any(|n| n.kind() == kind)
}
#[test]
fn create_table_exposes_structure() {
let sql = "CREATE TABLE t (id INT NOT NULL, name STRING, PRIMARY KEY (id))";
assert_parse_clean(sql);
assert!(has(sql, SyntaxKind::CREATE_STMT));
assert!(has(sql, SyntaxKind::COLUMN_DEF_LIST));
assert!(has(sql, SyntaxKind::COLUMN_DEF));
}
#[test]
fn create_table_column_def_count() {
let sql = "CREATE TABLE t (a INT, b INT, PRIMARY KEY (a))";
let p = parse(sql);
assert!(p.errors().is_empty(), "{:?}", p.errors());
let list = p
.syntax()
.descendants()
.find(|n| n.kind() == SyntaxKind::COLUMN_DEF_LIST)
.expect("a COLUMN_DEF_LIST");
let defs = list
.children()
.filter(|n| n.kind() == SyntaxKind::COLUMN_DEF)
.count();
assert_eq!(defs, 3, "expected 3 column/constraint defs");
}
#[test]
fn ctas_exposes_select_inside_create() {
let sql = "CREATE TABLE t AS SELECT a FROM u";
assert_parse_clean(sql);
assert!(has(sql, SyntaxKind::CREATE_STMT));
assert!(has(sql, SyntaxKind::SELECT_STMT));
}
#[test]
fn create_view_exposes_select() {
let sql = "CREATE OR REPLACE SECURE VIEW v AS SELECT a, b FROM t";
assert_parse_clean(sql);
assert!(has(sql, SyntaxKind::CREATE_STMT));
assert!(has(sql, SyntaxKind::SELECT_STMT));
}
#[test]
fn drop_exposes_statement_node() {
for sql in ["DROP TABLE t", "DROP VIEW IF EXISTS v CASCADE"] {
assert_parse_clean(sql);
assert!(has(sql, SyntaxKind::DROP_STMT));
}
}
#[test]
fn ddl_round_trips_with_broken_or_partial_input() {
for s in [
"CREATE TABLE",
"CREATE TABLE t (",
"CREATE TABLE t (a INT,",
"CREATE OR REPLACE TABLE t (a INT, PRIMARY KEY (",
"CREATE VIEW v AS",
"DROP TABLE",
"DROP TABLE IF EXISTS",
"CREATE TABLE t CLONE",
] {
assert_parse_roundtrip(s);
}
}
#[test]
fn ddl_word_as_quoted_identifier_round_trips() {
for s in [
"CREATE TABLE t (\"comment\" INT, \"key\" STRING)",
"CREATE TABLE t (\"default\" INT)",
] {
assert_parse_clean(s);
}
}