use faucet_core::idempotency::{
COMMIT_TOKEN_SCOPE_COL, COMMIT_TOKEN_TABLE, COMMIT_TOKEN_TOKEN_COL,
};
use faucet_core::util::quote_ident;
const UPDATED_AT_COL: &str = "updated_at";
pub const TRANSACTION_STATEMENT_COUNT: usize = 4;
pub const COMMIT_ONLY_STATEMENT_COUNT: usize = 3;
fn commit_table_ref(database: &str, schema: &str) -> String {
format!(
"{}.{}.{}",
quote_ident(database),
quote_ident(schema),
quote_ident(COMMIT_TOKEN_TABLE)
)
}
pub fn build_create_commit_table(database: &str, schema: &str) -> String {
format!(
"CREATE TABLE IF NOT EXISTS {t} ({scope} STRING NOT NULL PRIMARY KEY, {token} STRING NOT NULL, {updated} TIMESTAMP_NTZ)",
t = commit_table_ref(database, schema),
scope = quote_ident(COMMIT_TOKEN_SCOPE_COL),
token = quote_ident(COMMIT_TOKEN_TOKEN_COL),
updated = quote_ident(UPDATED_AT_COL),
)
}
fn build_merge_token(database: &str, schema: &str) -> String {
let scope = quote_ident(COMMIT_TOKEN_SCOPE_COL);
let token = quote_ident(COMMIT_TOKEN_TOKEN_COL);
let updated = quote_ident(UPDATED_AT_COL);
format!(
"MERGE INTO {t} t USING (SELECT ? AS {scope}, ? AS {token}) s ON t.{scope} = s.{scope} \
WHEN MATCHED THEN UPDATE SET t.{token} = s.{token}, t.{updated} = CURRENT_TIMESTAMP() \
WHEN NOT MATCHED THEN INSERT ({scope}, {token}, {updated}) VALUES (s.{scope}, s.{token}, CURRENT_TIMESTAMP())",
t = commit_table_ref(database, schema),
)
}
pub fn build_transaction_statement(insert_sql: &str, database: &str, schema: &str) -> String {
format!(
"BEGIN;\n{insert_sql};\n{merge};\nCOMMIT;",
merge = build_merge_token(database, schema),
)
}
pub fn build_commit_only_statement(database: &str, schema: &str) -> String {
format!(
"BEGIN;\n{merge};\nCOMMIT;",
merge = build_merge_token(database, schema),
)
}
pub fn build_select_token(database: &str, schema: &str) -> String {
format!(
"SELECT {token} FROM {t} WHERE {scope} = ? LIMIT 1",
token = quote_ident(COMMIT_TOKEN_TOKEN_COL),
t = commit_table_ref(database, schema),
scope = quote_ident(COMMIT_TOKEN_SCOPE_COL),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_commit_table_sql_is_exact() {
assert_eq!(
build_create_commit_table("MY_DB", "PUBLIC"),
"CREATE TABLE IF NOT EXISTS \"MY_DB\".\"PUBLIC\".\"_faucet_commit_token\" \
(\"scope\" STRING NOT NULL PRIMARY KEY, \"token\" STRING NOT NULL, \"updated_at\" TIMESTAMP_NTZ)"
);
}
#[test]
fn create_commit_table_escapes_identifiers() {
let sql = build_create_commit_table("we\"ird", "sch\"ema");
assert!(sql.contains("\"we\"\"ird\".\"sch\"\"ema\""), "sql: {sql}");
}
#[test]
fn select_token_sql_is_exact() {
assert_eq!(
build_select_token("MY_DB", "PUBLIC"),
"SELECT \"token\" FROM \"MY_DB\".\"PUBLIC\".\"_faucet_commit_token\" \
WHERE \"scope\" = ? LIMIT 1"
);
}
#[test]
fn merge_token_sql_is_exact() {
assert_eq!(
build_merge_token("db", "sch"),
"MERGE INTO \"db\".\"sch\".\"_faucet_commit_token\" t \
USING (SELECT ? AS \"scope\", ? AS \"token\") s ON t.\"scope\" = s.\"scope\" \
WHEN MATCHED THEN UPDATE SET t.\"token\" = s.\"token\", t.\"updated_at\" = CURRENT_TIMESTAMP() \
WHEN NOT MATCHED THEN INSERT (\"scope\", \"token\", \"updated_at\") \
VALUES (s.\"scope\", s.\"token\", CURRENT_TIMESTAMP())"
);
}
#[test]
fn transaction_statement_wraps_insert_and_merge_in_order() {
let insert = "INSERT INTO \"db\".\"sch\".\"tbl\" (\"id\") \
SELECT value:\"id\"::string FROM TABLE(FLATTEN(input => PARSE_JSON(?)))";
let sql = build_transaction_statement(insert, "db", "sch");
assert!(sql.starts_with("BEGIN;\n"), "sql: {sql}");
assert!(sql.ends_with("\nCOMMIT;"), "sql: {sql}");
let i = sql.find("INSERT INTO").expect("insert present");
let m = sql.find("MERGE INTO").expect("merge present");
let c = sql.find("COMMIT;").expect("commit present");
assert!(i < m && m < c, "statement order wrong: {sql}");
assert_eq!(sql.matches(';').count(), TRANSACTION_STATEMENT_COUNT);
assert_eq!(sql.matches('?').count(), 3, "sql: {sql}");
}
#[test]
fn commit_only_statement_has_no_insert_and_three_statements() {
let sql = build_commit_only_statement("db", "sch");
assert!(sql.starts_with("BEGIN;\n"), "sql: {sql}");
assert!(sql.ends_with("\nCOMMIT;"), "sql: {sql}");
assert!(!sql.contains("INSERT INTO \"db\".\"sch\".\"tbl\""));
assert!(sql.contains("MERGE INTO"), "sql: {sql}");
assert_eq!(sql.matches(';').count(), COMMIT_ONLY_STATEMENT_COUNT);
assert_eq!(sql.matches('?').count(), 2, "sql: {sql}");
}
#[test]
fn statement_counts_match_the_generated_text() {
assert_eq!(TRANSACTION_STATEMENT_COUNT, 4);
assert_eq!(COMMIT_ONLY_STATEMENT_COUNT, 3);
}
}