///|
// A shared, closed ALTER TABLE header. Keep the complete action suffix so an
// unrecognized action cannot inherit the lock level of a recognized neighbor.
fn alter_table_actions(words : ArrayView[String]) -> ArrayView[String]? {
let mut i = 2
if words.get(i) == Some("IF") {
if words.get(i + 1) != Some("EXISTS") {
return None
}
i += 2
}
let only = words.get(i) == Some("ONLY")
if only {
i += 1
}
i = match relation_name_end(words, i) {
Some(end) => end
None => return None
}
if words.get(i) == Some("*") {
if only {
return None
}
i += 1
}
if i == words.length() {
None
} else {
Some(words[i:])
}
}
///|
fn classify_alter_table(words : ArrayView[String]) -> Command {
let actions = match alter_table_actions(words) {
Some(actions) => actions
None => return Unsupported
}
match actions {
["VALIDATE", ..] => classify_validate_constraint(actions)
["ALTER", ..] => classify_set_not_null(actions)
["ADD", .. tail] => {
let body = match tail {
["CONSTRAINT", name, .. body] if index_identifier(name) => body
["CONSTRAINT", ..] => return Unsupported
_ => tail
}
match body {
["FOREIGN", "KEY", ..] => classify_foreign_key(body)
_ => classify_index_constraint(body)
}
}
_ => Unsupported
}
}
///|
// Deliberately support the documented order with an explicit DEFERRABLE when
// requesting deferred checks. Repeated/reordered categories remain incomplete.
fn constraint_timing(words : ArrayView[String]) -> Bool? {
match words {
[]
| ["NOT", "DEFERRABLE"]
| ["INITIALLY", "IMMEDIATE"]
| ["NOT", "DEFERRABLE", "INITIALLY", "IMMEDIATE"] => Some(false)
["DEFERRABLE"] | ["DEFERRABLE", "INITIALLY", "IMMEDIATE" | "DEFERRED"] =>
Some(true)
_ => None
}
}