use serde_json::{json, Map, Value};
use crate::lexer::TokenKind;
use super::helpers::{o_quoted_string, o_string_or_ident, s_identifier, s_number};
use super::stream::Stream;
pub fn one_of_keywords(s: &mut Stream, names: &[&str]) -> Option<String> {
for name in names {
if let Some(v) = s.eat_keyword(name) {
return Some(v);
}
}
None
}
pub fn ws_or_equals(s: &mut Stream) -> bool {
let save = s.pos();
let had_ws = s.ws1();
if s.eat(&TokenKind::Equal).is_some() {
s.ws0();
return true;
}
if had_ws {
return true;
}
s.set(save);
false
}
pub fn p_index_column(s: &mut Stream) -> Option<Value> {
let column = s_identifier(s)?;
let mut length: Option<Value> = None;
let mut sort: Option<String> = None;
let save = s.pos();
s.ws0();
if s.eat(&TokenKind::LParens).is_some() {
s.ws0();
if let Some(n) = s_number(s) {
s.ws0();
if s.eat(&TokenKind::RParens).is_some() {
length = Some(n);
} else {
s.set(save);
}
} else {
s.set(save);
}
} else {
s.set(save);
}
let save = s.pos();
s.ws0();
if let Some(v) = one_of_keywords(s, &["ASC", "DESC"]) {
sort = Some(v);
} else {
s.set(save);
}
let mut def = Map::new();
def.insert("column".into(), Value::String(column));
if let Some(l) = length {
def.insert("length".into(), l);
}
if let Some(so) = sort {
def.insert("sort".into(), Value::String(so));
}
Some(json!({ "id": "P_INDEX_COLUMN", "def": def }))
}
pub fn index_column_list(s: &mut Stream) -> Option<Vec<Value>> {
let save = s.pos();
s.ws0();
if s.eat(&TokenKind::LParens).is_none() {
s.set(save);
return None;
}
s.ws0();
let first = match p_index_column(s) {
Some(v) => v,
None => {
s.set(save);
return None;
}
};
let mut cols = vec![first];
loop {
let item = s.pos();
s.ws0();
if s.eat(&TokenKind::Comma).is_none() {
s.set(item);
break;
}
s.ws0();
match p_index_column(s) {
Some(v) => cols.push(v),
None => {
s.set(item);
break;
}
}
}
s.ws0();
if s.eat(&TokenKind::RParens).is_none() {
s.set(save);
return None;
}
Some(cols)
}
pub fn p_index_type(s: &mut Stream) -> Option<Value> {
let save = s.pos();
s.eat_keyword("USING")?;
if !s.ws1() {
s.set(save);
return None;
}
match one_of_keywords(s, &["BTREE", "HASH", "RTREE"]) {
Some(v) => Some(json!({ "id": "P_INDEX_TYPE", "def": v })),
None => {
s.set(save);
None
}
}
}
pub fn o_index_option(s: &mut Stream) -> Option<Value> {
let save = s.pos();
if s.eat_keyword("KEY_BLOCK_SIZE").is_some() {
if ws_or_equals(s) {
if let Some(n) = s_number(s) {
return Some(json!({ "id": "O_INDEX_OPTION", "def": { "keyBlockSize": n } }));
}
}
s.set(save);
}
if let Some(it) = p_index_type(s) {
return Some(json!({ "id": "O_INDEX_OPTION", "def": { "indexType": it } }));
}
s.set(save);
if s.eat_keyword("WITH").is_some() {
if s.ws1() && s.eat_keyword("PARSER").is_some() && s.ws1() {
if let Some(p) = s_identifier(s) {
return Some(json!({ "id": "O_INDEX_OPTION", "def": { "parser": p } }));
}
}
s.set(save);
}
if s.eat_keyword("COMMENT").is_some() {
if s.ws1() {
if let Some(c) = o_quoted_string(s) {
return Some(json!({ "id": "O_INDEX_OPTION", "def": { "comment": c } }));
}
}
s.set(save);
}
None
}
pub fn index_options(s: &mut Stream) -> Vec<Value> {
let mut opts = Vec::new();
loop {
let save = s.pos();
s.ws0();
match o_index_option(s) {
Some(o) => opts.push(o),
None => {
s.set(save);
break;
}
}
}
opts
}
pub fn p_index_algorithm_option(s: &mut Stream) -> Option<Value> {
let save = s.pos();
s.eat_keyword("ALGORITHM")?;
if !ws_or_equals(s) {
s.set(save);
return None;
}
match one_of_keywords(s, &["DEFAULT", "INPLACE", "COPY"]) {
Some(v) => Some(json!({
"id": "P_INDEX_ALGORITHM_OPTION",
"def": { "algorithm": v }
})),
None => {
s.set(save);
None
}
}
}
pub fn p_lock_option(s: &mut Stream) -> Option<Value> {
let save = s.pos();
s.eat_keyword("LOCK")?;
if !ws_or_equals(s) {
s.set(save);
return None;
}
match one_of_keywords(s, &["DEFAULT", "NONE", "SHARED", "EXCLUSIVE"]) {
Some(v) => Some(json!({ "id": "P_LOCK_OPTION", "def": { "lock": v } })),
None => {
s.set(save);
None
}
}
}
pub fn o_default_value(s: &mut Stream) -> Option<Value> {
if let Some(n) = s_number(s) {
return Some(n);
}
if let Some(v) = s.eat(&TokenKind::BitFormat) {
return Some(Value::String(v));
}
if let Some(v) = s.eat(&TokenKind::HexaFormat) {
return Some(Value::String(v));
}
let save = s.pos();
if let Some(ident) = s_identifier(s) {
let mut suffix = String::new();
let psave = s.pos();
s.ws0();
if s.eat(&TokenKind::LParens).is_some() {
s.ws0();
let num = s.eat(&TokenKind::Number);
s.ws0();
if s.eat(&TokenKind::RParens).is_some() {
suffix = format!("({})", num.unwrap_or_default());
} else {
s.set(psave);
}
} else {
s.set(psave);
}
return Some(Value::String(format!("{}{}", ident, suffix)));
}
s.set(save);
o_quoted_string(s).map(Value::String)
}
pub fn o_column_definition(s: &mut Stream) -> Option<Value> {
let save = s.pos();
if s.eat_keyword("UNSIGNED").is_some() {
return Some(col_def(json!({ "unsigned": true })));
}
if s.eat_keyword("ZEROFILL").is_some() {
return Some(col_def(json!({ "zerofill": true })));
}
if s.eat_keyword("CHARSET").is_some() {
if s.ws1() {
if let Some(cs) = o_string_or_ident(s) {
return Some(col_def(json!({ "charset": cs })));
}
}
s.set(save);
}
if s.eat_keyword("CHARACTER").is_some() {
if s.ws1() && s.eat_keyword("SET").is_some() && s.ws1() {
if let Some(cs) = o_string_or_ident(s) {
return Some(col_def(json!({ "charset": cs })));
}
}
s.set(save);
}
if s.eat_keyword("COLLATE").is_some() {
if s.ws1() {
if let Some(c) = o_string_or_ident(s) {
return Some(col_def(json!({ "collation": c })));
}
}
s.set(save);
}
if s.eat_keyword("NOT").is_some() {
if s.ws1() && s.eat_keyword("NULL").is_some() {
return Some(col_def(json!({ "nullable": false })));
}
s.set(save);
}
if s.eat_keyword("NULL").is_some() {
return Some(col_def(json!({ "nullable": true })));
}
if s.eat_keyword("DEFAULT").is_some() {
if s.ws1() {
if let Some(v) = o_default_value(s) {
return Some(col_def(json!({ "default": v })));
}
}
s.set(save);
}
if s.eat_keyword("AUTO_INCREMENT").is_some() {
return Some(col_def(json!({ "autoincrement": true })));
}
if s.eat_keyword("UNIQUE").is_some() {
let ksave = s.pos();
s.ws1();
if s.eat_keyword("KEY").is_none() {
s.set(ksave);
}
return Some(col_def(json!({ "unique": true })));
}
let psave = s.pos();
if s.eat_keyword("PRIMARY").is_some() {
if s.ws1() && s.eat_keyword("KEY").is_some() {
return Some(col_def(json!({ "primary": true })));
}
s.set(psave);
}
if s.eat_keyword("KEY").is_some() {
return Some(col_def(json!({ "primary": true })));
}
if s.eat_keyword("COMMENT").is_some() {
if s.ws1() {
if let Some(c) = o_quoted_string(s) {
return Some(col_def(json!({ "comment": c })));
}
}
s.set(save);
}
if s.eat_keyword("INVISIBLE").is_some() {
let isave = s.pos();
if s.ws1()
&& s.eat_keyword("WITH").is_some()
&& s.ws1()
&& s.eat_keyword("SYSTEM").is_some()
&& s.ws1()
&& s.eat_keyword("VERSIONING").is_some()
{
return Some(col_def(json!({ "invisibleWithSystemVersioning": true })));
}
s.set(isave);
if s.ws1()
&& s.eat_keyword("WITHOUT").is_some()
&& s.ws1()
&& s.eat_keyword("SYSTEM").is_some()
&& s.ws1()
&& s.eat_keyword("VERSIONING").is_some()
{
return Some(col_def(json!({ "invisibleWithoutSystemVersioning": true })));
}
s.set(isave);
return Some(col_def(json!({ "invisible": true })));
}
if s.eat_keyword("COLUMN_FORMAT").is_some() {
if s.ws1() {
if let Some(v) = one_of_keywords(s, &["FIXED", "DYNAMIC", "DEFAULT"]) {
return Some(col_def(json!({ "format": v })));
}
}
s.set(save);
}
if s.eat_keyword("STORAGE").is_some() {
if s.ws1() {
if let Some(v) = one_of_keywords(s, &["DISK", "MEMORY", "DEFAULT"]) {
return Some(col_def(json!({ "storage": v })));
}
}
s.set(save);
}
if s.eat_keyword("ON").is_some() {
if s.ws1()
&& s.eat_keyword("UPDATE").is_some()
&& s.ws1()
&& s.eat_keyword("CURRENT_TIMESTAMP").is_some()
{
let mut on_update = "CURRENT_TIMESTAMP".to_string();
let osave = s.pos();
s.ws0();
if s.eat(&TokenKind::LParens).is_some() {
s.ws0();
let num = s.eat(&TokenKind::Number);
s.ws0();
if s.eat(&TokenKind::RParens).is_some() {
on_update.push_str(&format!("({})", num.unwrap_or_default()));
} else {
s.set(osave);
}
} else {
s.set(osave);
}
return Some(col_def(json!({ "onUpdate": on_update })));
}
s.set(save);
}
None
}
fn col_def(def: Value) -> Value {
json!({ "id": "O_COLUMN_DEFINITION", "def": def })
}
pub fn column_definitions(s: &mut Stream) -> Vec<Value> {
let mut defs = Vec::new();
loop {
let save = s.pos();
if !s.ws1() {
s.set(save);
break;
}
match o_column_definition(s) {
Some(d) => defs.push(d),
None => {
s.set(save);
break;
}
}
}
defs
}
pub fn p_column_reference(s: &mut Stream) -> Option<Value> {
let save = s.pos();
s.eat_keyword("REFERENCES")?;
if !s.ws1() {
s.set(save);
return None;
}
let table = match s_identifier(s) {
Some(t) => t,
None => {
s.set(save);
return None;
}
};
let columns = match reference_columns(s) {
Some(c) => c,
None => {
s.set(save);
return None;
}
};
let mut match_val: Option<String> = None;
let msave = s.pos();
if s.eat_keyword("MATCH").is_some() {
if s.ws1() {
if let Some(v) = one_of_keywords(s, &["FULL", "PARTIAL", "SIMPLE"]) {
match_val = Some(v);
s.ws0();
} else {
s.set(msave);
}
} else {
s.set(msave);
}
} else {
s.set(msave);
}
let mut on = Vec::new();
loop {
let osave = s.pos();
if s.eat_keyword("ON").is_none() {
s.set(osave);
break;
}
if !s.ws1() {
s.set(osave);
break;
}
let trigger = match one_of_keywords(s, &["DELETE", "UPDATE"]) {
Some(v) => v,
None => {
s.set(osave);
break;
}
};
if !s.ws1() {
s.set(osave);
break;
}
let action = match reference_action(s) {
Some(v) => v,
None => {
s.set(osave);
break;
}
};
s.ws0();
on.push(json!({ "trigger": trigger, "action": action }));
}
let mut def = Map::new();
def.insert("table".into(), Value::String(table));
def.insert("columns".into(), Value::Array(columns));
def.insert(
"match".into(),
match_val.map(Value::String).unwrap_or(Value::Null),
);
def.insert("on".into(), Value::Array(on));
Some(json!({ "id": "P_COLUMN_REFERENCE", "def": def }))
}
fn reference_columns(s: &mut Stream) -> Option<Vec<Value>> {
let cols = index_column_list(s)?;
s.ws0();
Some(cols)
}
fn reference_action(s: &mut Stream) -> Option<String> {
let save = s.pos();
if let Some(v) = s.eat_keyword("RESTRICT") {
return Some(v);
}
if let Some(v) = s.eat_keyword("CASCADE") {
return Some(v);
}
if let Some(set) = s.eat_keyword("SET") {
if s.ws1() {
if let Some(null) = s.eat_keyword("NULL") {
return Some(format!("{} {}", set, null));
}
if let Some(def) = s.eat_keyword("DEFAULT") {
return Some(format!("{} {}", set, def));
}
}
s.set(save);
}
if let Some(no) = s.eat_keyword("NO") {
if s.ws1() {
if let Some(action) = s.eat_keyword("ACTION") {
return Some(format!("{} {}", no, action));
}
}
s.set(save);
}
None
}