use super::base::parse_identifier;
use crate::ast::*;
use nom::{
IResult, Parser,
branch::alt,
bytes::complete::{tag_no_case, take_while1},
character::complete::{char, multispace0, multispace1},
combinator::{map, opt, recognize, value},
multi::{many0, separated_list1},
sequence::{delimited, preceded},
};
pub fn parse_create_table<'a>(input: &'a str, table: &str) -> IResult<&'a str, Qail> {
let (input, columns) = separated_list1(
(multispace0, char(','), multispace0),
parse_column_definition,
)
.parse(input)?;
let (input, _) = multispace0(input)?;
let (input, table_constraints) = many0(parse_table_constraint).parse(input)?;
Ok((
input,
Qail {
action: Action::Make,
table: table.to_string(),
columns,
joins: vec![],
cages: vec![],
distinct: false,
distinct_on: vec![],
index_def: None,
table_constraints,
set_ops: vec![],
having: vec![],
group_by_mode: GroupByMode::default(),
ctes: vec![],
returning: None,
on_conflict: None,
source_query: None,
channel: None,
payload: None,
savepoint_name: None,
from_tables: vec![],
using_tables: vec![],
lock_mode: None,
skip_locked: false,
fetch: None,
default_values: false,
overriding: None,
sample: None,
only_table: false,
vector: None,
score_threshold: None,
vector_name: None,
with_vector: false,
vector_size: None,
distance: None,
on_disk: None,
function_def: None,
trigger_def: None,
policy_def: None,
},
))
}
pub fn parse_table_constraint(input: &str) -> IResult<&str, TableConstraint> {
let (input, _) = multispace0(input)?;
alt((
map(
(
tag_no_case("primary"),
multispace1,
tag_no_case("key"),
multispace0,
delimited(
char('('),
separated_list1((multispace0, char(','), multispace0), parse_identifier),
char(')'),
),
),
|(_, _, _, _, cols): (_, _, _, _, Vec<&str>)| {
TableConstraint::PrimaryKey(cols.iter().map(|s| s.to_string()).collect())
},
),
map(
(
tag_no_case("unique"),
multispace0,
delimited(
char('('),
separated_list1((multispace0, char(','), multispace0), parse_identifier),
char(')'),
),
),
|(_, _, cols): (_, _, Vec<&str>)| {
TableConstraint::Unique(cols.iter().map(|s| s.to_string()).collect())
},
),
))
.parse(input)
}
pub fn parse_column_definition(input: &str) -> IResult<&str, Expr> {
let (input, name) = take_while1(|c: char| c.is_alphanumeric() || c == '_').parse(input)?;
let (input, _) = char(':').parse(input)?;
let (input, data_type) = recognize((
take_while1(|c: char| c.is_alphanumeric() || c == '_'),
opt(delimited(
char('('),
take_while1(|c: char| c != ')'),
char(')'),
)),
))
.parse(input)?;
let (input, constraints) = many0(preceded(char(':'), parse_constraint)).parse(input)?;
Ok((
input,
Expr::Def {
name: name.to_string(),
data_type: data_type.to_string(),
constraints,
},
))
}
pub fn parse_constraint(input: &str) -> IResult<&str, Constraint> {
alt((
value(Constraint::PrimaryKey, tag_no_case("pk")),
value(Constraint::PrimaryKey, tag_no_case("primarykey")),
value(Constraint::Unique, tag_no_case("unique")),
value(Constraint::Unique, tag_no_case("uniq")),
value(Constraint::Nullable, tag_no_case("nullable")),
value(Constraint::Nullable, tag_no_case("null")),
map(
preceded(
alt((tag_no_case("default="), tag_no_case("def="))),
recognize(take_while1(|c: char| c != ',' && c != ':' && c != ' ')),
),
|val: &str| Constraint::Default(val.to_string()),
),
map(
preceded(
tag_no_case("check="),
recognize(take_while1(|c: char| c != ',' && c != ':' && c != ' ')),
),
|expr: &str| Constraint::Check(vec![expr.to_string()]),
),
))
.parse(input)
}
pub fn parse_create_index(input: &str) -> IResult<&str, Qail> {
let (input, _) = tag_no_case("index").parse(input)?;
let (input, _) = multispace1(input)?;
let (input, index_name) = parse_identifier(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag_no_case("on").parse(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = parse_identifier(input)?;
let (input, _) = multispace1(input)?;
let (input, columns) =
separated_list1((multispace0, char(','), multispace0), parse_identifier).parse(input)?;
let (input, _) = multispace0(input)?;
let (input, unique) = opt(tag_no_case("unique")).parse(input)?;
Ok((
input,
Qail {
action: Action::Index,
table: String::new(),
columns: vec![],
joins: vec![],
cages: vec![],
distinct: false,
distinct_on: vec![],
index_def: Some(IndexDef {
name: index_name.to_string(),
table: table_name.to_string(),
columns: columns.iter().map(|s| s.to_string()).collect(),
unique: unique.is_some(),
index_type: None,
where_clause: None,
}),
table_constraints: vec![],
set_ops: vec![],
having: vec![],
group_by_mode: GroupByMode::default(),
ctes: vec![],
returning: None,
on_conflict: None,
source_query: None,
channel: None,
payload: None,
savepoint_name: None,
from_tables: vec![],
using_tables: vec![],
lock_mode: None,
skip_locked: false,
fetch: None,
default_values: false,
overriding: None,
sample: None,
only_table: false,
vector: None,
score_threshold: None,
vector_name: None,
with_vector: false,
vector_size: None,
distance: None,
on_disk: None,
function_def: None,
trigger_def: None,
policy_def: None,
},
))
}