Improved error messages (#2566)
This commit is contained in:
parent
fe78ca3c32
commit
b02567d233
90 changed files with 2225 additions and 1268 deletions
|
@ -1,5 +1,6 @@
|
||||||
use crate::iam::Error as IamError;
|
use crate::iam::Error as IamError;
|
||||||
use crate::idx::ft::MatchRef;
|
use crate::idx::ft::MatchRef;
|
||||||
|
use crate::sql::error::RenderedError as RenderedParserError;
|
||||||
use crate::sql::idiom::Idiom;
|
use crate::sql::idiom::Idiom;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
@ -117,12 +118,8 @@ pub enum Error {
|
||||||
UnknownAuth,
|
UnknownAuth,
|
||||||
|
|
||||||
/// There was an error with the SQL query
|
/// There was an error with the SQL query
|
||||||
#[error("Parse error on line {line} at character {char} when parsing '{sql}'")]
|
#[error("Parse error: {0}")]
|
||||||
InvalidQuery {
|
InvalidQuery(RenderedParserError),
|
||||||
line: usize,
|
|
||||||
char: usize,
|
|
||||||
sql: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// There was an error with the SQL query
|
/// There was an error with the SQL query
|
||||||
#[error("Can not use {value} in a CONTENT clause")]
|
#[error("Can not use {value} in a CONTENT clause")]
|
||||||
|
|
|
@ -419,7 +419,8 @@ mod tests {
|
||||||
let (quote, _) = line.split_once("=>").unwrap();
|
let (quote, _) = line.split_once("=>").unwrap();
|
||||||
let name = quote.trim().trim_matches('"');
|
let name = quote.trim().trim_matches('"');
|
||||||
|
|
||||||
if crate::sql::function::function_names(name).is_err() {
|
let builtin_name = crate::sql::builtin::builtin_name(name);
|
||||||
|
if builtin_name.is_err() {
|
||||||
problems.push(format!("couldn't parse {name} function"));
|
problems.push(format!("couldn't parse {name} function"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,17 +191,13 @@ mod tests {
|
||||||
"بدل", "من", "الجر",
|
"بدل", "من", "الجر",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(arabic);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(arabic);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(ar);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ar);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(ara);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ara);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -235,20 +231,16 @@ mod tests {
|
||||||
".",
|
".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(danish);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(danish);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(dan);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(dan);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(da);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(da);", input, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -259,17 +251,13 @@ mod tests {
|
||||||
"klein", "hond", "slaapt", "liever", "in", "zijn", "mand", "dan", "te", "renn", ".",
|
"klein", "hond", "slaapt", "liever", "in", "zijn", "mand", "dan", "te", "renn", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(dutch);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(dutch);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(nl);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(nl);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(nld);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(nld);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -283,20 +271,16 @@ mod tests {
|
||||||
"read", "in", "her", "spare", "time", "rather", "than", "teach", ".",
|
"read", "in", "her", "spare", "time", "rather", "than", "teach", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(english);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(english);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(eng);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(eng);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(en);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(en);", input, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -307,17 +291,13 @@ mod tests {
|
||||||
"chien", "aim", "plutôt", "se", "blott", "sur", "le", "canap", "que", "de", "cour",
|
"chien", "aim", "plutôt", "se", "blott", "sur", "le", "canap", "que", "de", "cour",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(french);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(french);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(fr);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(fr);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(fra);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(fra);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -332,17 +312,13 @@ mod tests {
|
||||||
"zu", "lauf", ".",
|
"zu", "lauf", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(german);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(german);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(de);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(de);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(deu);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(deu);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -377,20 +353,16 @@ mod tests {
|
||||||
".",
|
".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(greek);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(greek);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ell);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(ell);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(el);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(el);", input, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -401,17 +373,13 @@ mod tests {
|
||||||
"inkább", "alsz", "a", "kosar", ",", "mints", "fu", ".",
|
"inkább", "alsz", "a", "kosar", ",", "mints", "fu", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(hungarian);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(hungarian);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(hu);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(hu);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(hun);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(hun);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -425,17 +393,13 @@ mod tests {
|
||||||
"prefer", "dorm", "nel", "suo", "cest", "piuttost", "che", "corr", ".",
|
"prefer", "dorm", "nel", "suo", "cest", "piuttost", "che", "corr", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(italian);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(italian);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(it);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(it);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(ita);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ita);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -469,17 +433,13 @@ mod tests {
|
||||||
".",
|
".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(norwegian);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(norwegian);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(no);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(no);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(nor);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(nor);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -493,17 +453,13 @@ mod tests {
|
||||||
"prefer", "dorm", "na", "sua", "cam", "em", "vez", "de", "corr", ".",
|
"prefer", "dorm", "na", "sua", "cam", "em", "vez", "de", "corr", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(portuguese);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(portuguese);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(pt);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(pt);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(por);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(por);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -517,17 +473,13 @@ mod tests {
|
||||||
"să", "doarm", "în", "coș", "lui", "decât", "să", "alerg", ".",
|
"să", "doarm", "în", "coș", "lui", "decât", "să", "alerg", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(romanian);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(romanian);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(ro);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ro);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(ron);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ron);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -559,17 +511,13 @@ mod tests {
|
||||||
".",
|
".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(russian);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(russian);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(ru);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ru);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(rus);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(rus);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -583,17 +531,13 @@ mod tests {
|
||||||
"prefier", "dorm", "en", "su", "cam", "en", "lug", "de", "corr", ".",
|
"prefier", "dorm", "en", "su", "cam", "en", "lug", "de", "corr", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(spanish);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(spanish);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(es);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(es);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(spa);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(spa);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -627,17 +571,13 @@ mod tests {
|
||||||
".",
|
".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(swedish);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(swedish);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(sv);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(sv);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(swe);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(swe);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -674,17 +614,13 @@ mod tests {
|
||||||
".",
|
".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(tamil);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(tamil);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(ta);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(ta);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(tam);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(tam);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -698,17 +634,13 @@ mod tests {
|
||||||
"yatak", "uyuma", "tercih", "eder", ".",
|
"yatak", "uyuma", "tercih", "eder", ".",
|
||||||
];
|
];
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(turkish);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(turkish);",
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
test_analyzer("ANALYZER test TOKENIZERS blank,class FILTERS snowball(tr);", input, &output);
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(tr);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS snowball(tur);",
|
||||||
input,
|
|
||||||
&output,
|
|
||||||
);
|
|
||||||
test_analyzer(
|
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS snowball(tur);",
|
|
||||||
input,
|
input,
|
||||||
&output,
|
&output,
|
||||||
);
|
);
|
||||||
|
@ -717,7 +649,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ngram() {
|
fn test_ngram() {
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS lowercase,ngram(2,3);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS lowercase,ngram(2,3);",
|
||||||
"Ālea iacta est",
|
"Ālea iacta est",
|
||||||
&["āl", "āle", "le", "lea", "ia", "iac", "ac", "act", "ct", "cta", "es", "est"],
|
&["āl", "āle", "le", "lea", "ia", "iac", "ac", "act", "ct", "cta", "es", "est"],
|
||||||
);
|
);
|
||||||
|
@ -726,7 +658,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_edgengram() {
|
fn test_edgengram() {
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS lowercase,edgengram(2,3);",
|
"ANALYZER test TOKENIZERS blank,class FILTERS lowercase,edgengram(2,3);",
|
||||||
"Ālea iacta est",
|
"Ālea iacta est",
|
||||||
&["āl", "āle", "ia", "iac", "es", "est"],
|
&["āl", "āle", "ia", "iac", "es", "est"],
|
||||||
);
|
);
|
||||||
|
|
|
@ -313,7 +313,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokenize_blank_class() {
|
fn test_tokenize_blank_class() {
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class FILTERS lowercase",
|
"ANALYZER test TOKENIZERS blank,class FILTERS lowercase",
|
||||||
"Abc12345xYZ DL1809 item123456 978-3-16-148410-0 1HGCM82633A123456",
|
"Abc12345xYZ DL1809 item123456 978-3-16-148410-0 1HGCM82633A123456",
|
||||||
&[
|
&[
|
||||||
"abc", "12345", "xyz", "dl", "1809", "item", "123456", "978", "-", "3", "-", "16",
|
"abc", "12345", "xyz", "dl", "1809", "item", "123456", "978", "-", "3", "-", "16",
|
||||||
|
@ -325,7 +325,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokenize_source_code() {
|
fn test_tokenize_source_code() {
|
||||||
test_analyzer(
|
test_analyzer(
|
||||||
"DEFINE ANALYZER test TOKENIZERS blank,class,camel,punct FILTERS lowercase",
|
"ANALYZER test TOKENIZERS blank,class,camel,punct FILTERS lowercase",
|
||||||
r#"struct MyRectangle {
|
r#"struct MyRectangle {
|
||||||
// specified by corners
|
// specified by corners
|
||||||
top_left: Point,
|
top_left: Point,
|
||||||
|
|
|
@ -537,7 +537,7 @@ mod tests {
|
||||||
#[test(tokio::test)]
|
#[test(tokio::test)]
|
||||||
async fn test_ft_index() {
|
async fn test_ft_index() {
|
||||||
let ds = Datastore::new("memory").await.unwrap();
|
let ds = Datastore::new("memory").await.unwrap();
|
||||||
let (_, az) = analyzer("DEFINE ANALYZER test TOKENIZERS blank;").unwrap();
|
let (_, az) = analyzer("ANALYZER test TOKENIZERS blank;").unwrap();
|
||||||
|
|
||||||
let btree_order = 5;
|
let btree_order = 5;
|
||||||
|
|
||||||
|
@ -641,7 +641,7 @@ mod tests {
|
||||||
// Therefore it makes sense to do multiple runs.
|
// Therefore it makes sense to do multiple runs.
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let ds = Datastore::new("memory").await.unwrap();
|
let ds = Datastore::new("memory").await.unwrap();
|
||||||
let (_, az) = analyzer("DEFINE ANALYZER test TOKENIZERS blank;").unwrap();
|
let (_, az) = analyzer("ANALYZER test TOKENIZERS blank;").unwrap();
|
||||||
|
|
||||||
let doc1: Thing = ("t", "doc1").into();
|
let doc1: Thing = ("t", "doc1").into();
|
||||||
let doc2: Thing = ("t", "doc2").into();
|
let doc2: Thing = ("t", "doc2").into();
|
||||||
|
|
|
@ -8,6 +8,8 @@ use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
pub enum Base {
|
pub enum Base {
|
||||||
|
@ -35,14 +37,17 @@ impl fmt::Display for Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base(i: &str) -> IResult<&str, Base> {
|
pub fn base(i: &str) -> IResult<&str, Base> {
|
||||||
alt((
|
expected(
|
||||||
value(Base::Ns, tag_no_case("NAMESPACE")),
|
"a base, one of NAMESPACE, DATABASE, ROOT or KV",
|
||||||
value(Base::Db, tag_no_case("DATABASE")),
|
alt((
|
||||||
value(Base::Root, tag_no_case("ROOT")),
|
value(Base::Ns, tag_no_case("NAMESPACE")),
|
||||||
value(Base::Ns, tag_no_case("NS")),
|
value(Base::Db, tag_no_case("DATABASE")),
|
||||||
value(Base::Db, tag_no_case("DB")),
|
value(Base::Root, tag_no_case("ROOT")),
|
||||||
value(Base::Root, tag_no_case("KV")),
|
value(Base::Ns, tag_no_case("NS")),
|
||||||
))(i)
|
value(Base::Db, tag_no_case("DB")),
|
||||||
|
value(Base::Root, tag_no_case("KV")),
|
||||||
|
)),
|
||||||
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base_or_scope(i: &str) -> IResult<&str, Base> {
|
pub fn base_or_scope(i: &str) -> IResult<&str, Base> {
|
||||||
|
|
|
@ -32,6 +32,8 @@ use std::cmp::Ordering;
|
||||||
use std::fmt::{self, Display, Formatter, Write};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use super::util::expect_delimited;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Block";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Block";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
|
@ -179,11 +181,15 @@ impl Display for Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block(i: &str) -> IResult<&str, Block> {
|
pub fn block(i: &str) -> IResult<&str, Block> {
|
||||||
let (i, _) = openbraces(i)?;
|
expect_delimited(
|
||||||
let (i, v) = separated_list0(colons, entry)(i)?;
|
openbraces,
|
||||||
let (i, _) = many0(colons)(i)?;
|
|i| {
|
||||||
let (i, _) = closebraces(i)?;
|
let (i, v) = separated_list0(colons, entry)(i)?;
|
||||||
Ok((i, Block(v)))
|
let (i, _) = many0(colons)(i)?;
|
||||||
|
Ok((i, Block(v)))
|
||||||
|
},
|
||||||
|
closebraces,
|
||||||
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
|
|
485
lib/src/sql/builtin.rs
Normal file
485
lib/src/sql/builtin.rs
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
use crate::sql::{constant, error::ParseError, ident::ident_raw};
|
||||||
|
use nom::{
|
||||||
|
bytes::complete::{tag, tag_no_case},
|
||||||
|
combinator::{opt, peek, value},
|
||||||
|
Err, IResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
pub enum BuiltinName<I> {
|
||||||
|
Function(I),
|
||||||
|
Constant(constant::Constant),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A macro to generate a parser which is able to parse all the different functions, returning an
|
||||||
|
/// error of the function does not exists.
|
||||||
|
macro_rules! impl_builtins {
|
||||||
|
($($name:ident$( ( $s:ident ) )? $(= $rename:expr)? => { $($t:tt)* }),*$(,)?) => {
|
||||||
|
fn _parse_builtin_name(i: &str) -> IResult<&str, BuiltinName<&str>, ParseError<&str>> {
|
||||||
|
$(
|
||||||
|
impl_builtins!{
|
||||||
|
@variant,
|
||||||
|
impl_builtins!(@rename, $name, $($rename)?),
|
||||||
|
$name,
|
||||||
|
$($s)?,
|
||||||
|
$($rename)?,
|
||||||
|
{ $($t)* }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
$(
|
||||||
|
if let (i, Some(x)) = opt($name)(i)?{
|
||||||
|
return Ok((i,x))
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
Err(Err::Error(ParseError::Base(i)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(@variant, $full:expr, $name:ident, $($s:ident)?,$($rename:expr)?, { fn }) => {
|
||||||
|
fn $name(i: &str) -> IResult<&str, BuiltinName<&str>, ParseError<&str>>{
|
||||||
|
let parser = tag_no_case(impl_builtins!(@rename,$name,$($rename)?));
|
||||||
|
let res = value(BuiltinName::Function($full),parser)(i)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(@variant, $full:expr, $name:ident,$($s:ident)?,$($rename:expr)?, { const = $value:expr}) => {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn $name(i: &str) -> IResult<&str, BuiltinName<&str>, ParseError<&str>>{
|
||||||
|
let parser = tag_no_case(impl_builtins!(@rename,$name,$($rename)?));
|
||||||
|
let res = value(BuiltinName::Constant($value),parser)(i)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(@variant, $full:expr, $name:ident,$($s:ident)*,$($rename:expr)?, { $($t:tt)* }) => {
|
||||||
|
fn $name(i: &str) -> IResult<&str, BuiltinName<&str>, ParseError<&str>>{
|
||||||
|
let (i,_) = tag_no_case(impl_builtins!(@rename,$name,$($rename)?))(i)?;
|
||||||
|
let (i,_) = impl_builtins!(@sep, i,$full, $($s)*);
|
||||||
|
|
||||||
|
let (i,_) = impl_builtins!{@block,i, $full, { $($t)* }};
|
||||||
|
|
||||||
|
if let Ok((i, Some(_))) = peek(opt(ident_raw))(i){
|
||||||
|
Err(Err::Failure(ParseError::InvalidPath{
|
||||||
|
tried: i,
|
||||||
|
parent: $full
|
||||||
|
}))
|
||||||
|
}else{
|
||||||
|
Err(Err::Failure(ParseError::Expected{
|
||||||
|
tried: i,
|
||||||
|
expected: "a identifier"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(@block, $i:ident, $full:expr, { $($name:ident $(($s:ident))? $(= $rename:expr)? => { $($t:tt)* }),* $(,)? }) => {
|
||||||
|
{
|
||||||
|
$(
|
||||||
|
impl_builtins!{@variant,
|
||||||
|
concat!($full,"::",impl_builtins!(@rename, $name, $($rename)?)),
|
||||||
|
$name,
|
||||||
|
$($s)?,
|
||||||
|
$($rename)?,
|
||||||
|
{ $($t) * }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
|
$(
|
||||||
|
if let Ok((i, x)) = $name($i){
|
||||||
|
return Ok((i,x))
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
($i,())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(@sep, $input:expr, $full:expr, func) => {
|
||||||
|
match tag::<_,_,ParseError<&str>>("::")($input) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(($input, BuiltinName::Function($full)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(@sep, $input:expr, $full:expr, cons) => {
|
||||||
|
match tag::<_,_,ParseError<&str>>("::")($input) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(($input, BuiltinName::Constant($full)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(@sep, $input:expr,$full:expr, ) => {{
|
||||||
|
match tag::<_,_,ParseError<&str>>("::")($input) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(Err::Error(ParseError::Expected{
|
||||||
|
tried: $input,
|
||||||
|
expected: "a path seperator `::`"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
(@rename, $name:ident, $rename:expr) => {
|
||||||
|
$rename
|
||||||
|
};
|
||||||
|
|
||||||
|
(@rename, $name:ident,) => {
|
||||||
|
stringify!($name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn builtin_name(i: &str) -> IResult<&str, BuiltinName<&str>, ParseError<&str>> {
|
||||||
|
impl_builtins! {
|
||||||
|
array => {
|
||||||
|
add => { fn },
|
||||||
|
all => { fn },
|
||||||
|
any => { fn },
|
||||||
|
append => { fn },
|
||||||
|
at => { fn },
|
||||||
|
boolean_and => { fn },
|
||||||
|
boolean_not => { fn },
|
||||||
|
boolean_or => { fn },
|
||||||
|
boolean_xor => { fn },
|
||||||
|
clump => { fn },
|
||||||
|
combine => { fn },
|
||||||
|
complement => { fn },
|
||||||
|
concat => { fn },
|
||||||
|
difference => { fn },
|
||||||
|
distinct => { fn },
|
||||||
|
filter_index => { fn },
|
||||||
|
find_index => { fn },
|
||||||
|
first => { fn },
|
||||||
|
flatten => { fn },
|
||||||
|
group => { fn },
|
||||||
|
insert => { fn },
|
||||||
|
intersect=> { fn },
|
||||||
|
join => { fn },
|
||||||
|
last=> { fn },
|
||||||
|
len => { fn },
|
||||||
|
logical_and => { fn },
|
||||||
|
logical_or => { fn },
|
||||||
|
logical_xor => { fn },
|
||||||
|
matches => { fn },
|
||||||
|
max => { fn },
|
||||||
|
min => { fn },
|
||||||
|
pop => { fn },
|
||||||
|
prepend => { fn },
|
||||||
|
push => { fn },
|
||||||
|
remove => { fn },
|
||||||
|
reverse => { fn },
|
||||||
|
slice => { fn },
|
||||||
|
// says that sort is also itself a function
|
||||||
|
sort(func) => {
|
||||||
|
asc => {fn },
|
||||||
|
desc => {fn },
|
||||||
|
},
|
||||||
|
transpose => { fn },
|
||||||
|
r#union = "union" => { fn },
|
||||||
|
},
|
||||||
|
bytes => {
|
||||||
|
len => { fn }
|
||||||
|
},
|
||||||
|
crypto => {
|
||||||
|
argon2 => {
|
||||||
|
compare => { fn },
|
||||||
|
generate => { fn }
|
||||||
|
},
|
||||||
|
bcrypt => {
|
||||||
|
compare => { fn },
|
||||||
|
generate => { fn }
|
||||||
|
},
|
||||||
|
pbkdf2 => {
|
||||||
|
compare => { fn },
|
||||||
|
generate => { fn }
|
||||||
|
},
|
||||||
|
scrypt => {
|
||||||
|
compare => { fn },
|
||||||
|
generate => { fn }
|
||||||
|
},
|
||||||
|
md5 => { fn },
|
||||||
|
sha1 => { fn },
|
||||||
|
sha256 => { fn },
|
||||||
|
sha512 => { fn }
|
||||||
|
},
|
||||||
|
duration => {
|
||||||
|
days => { fn },
|
||||||
|
hours => { fn },
|
||||||
|
micros => { fn },
|
||||||
|
millis => { fn },
|
||||||
|
mins => { fn },
|
||||||
|
nanos => { fn },
|
||||||
|
secs => { fn },
|
||||||
|
weeks => { fn },
|
||||||
|
years => { fn },
|
||||||
|
from => {
|
||||||
|
days => { fn },
|
||||||
|
hours => { fn },
|
||||||
|
micros => { fn },
|
||||||
|
millis => { fn },
|
||||||
|
mins => { fn },
|
||||||
|
nanos => { fn },
|
||||||
|
secs => { fn },
|
||||||
|
weeks => { fn },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
encoding => {
|
||||||
|
base64 => {
|
||||||
|
decode => { fn },
|
||||||
|
encode => { fn },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
geo => {
|
||||||
|
area => { fn },
|
||||||
|
bearing => { fn },
|
||||||
|
centroid => { fn },
|
||||||
|
distance => { fn },
|
||||||
|
hash => {
|
||||||
|
decode => { fn },
|
||||||
|
encode => { fn },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
http => {
|
||||||
|
head => { fn },
|
||||||
|
get => { fn },
|
||||||
|
put => { fn },
|
||||||
|
post => { fn },
|
||||||
|
patch => { fn },
|
||||||
|
delete => { fn },
|
||||||
|
},
|
||||||
|
math => {
|
||||||
|
abs => { fn },
|
||||||
|
bottom => { fn },
|
||||||
|
ceil => { fn },
|
||||||
|
fixed => { fn },
|
||||||
|
floor => { fn },
|
||||||
|
interquartile => { fn },
|
||||||
|
max => { fn },
|
||||||
|
mean => { fn },
|
||||||
|
median => { fn },
|
||||||
|
midhinge => { fn },
|
||||||
|
min => { fn },
|
||||||
|
mode => { fn },
|
||||||
|
nearestrank => { fn },
|
||||||
|
percentile => { fn },
|
||||||
|
pow => { fn },
|
||||||
|
product => { fn },
|
||||||
|
round => { fn },
|
||||||
|
spread => { fn },
|
||||||
|
sqrt => { fn },
|
||||||
|
stddev => { fn },
|
||||||
|
sum => { fn },
|
||||||
|
top => { fn },
|
||||||
|
trimean => { fn },
|
||||||
|
variance => { fn },
|
||||||
|
E => { const = constant::Constant::MathE },
|
||||||
|
FRAC_1_PI => { const = constant::Constant::MathFrac1Pi },
|
||||||
|
FRAC_1_SQRT_2 => { const = constant::Constant::MathFrac1Sqrt2 },
|
||||||
|
FRAC_2_PI => { const = constant::Constant::MathFrac2Pi },
|
||||||
|
FRAC_2_SQRT_PI => { const = constant::Constant::MathFrac2SqrtPi },
|
||||||
|
FRAC_PI_2 => { const = constant::Constant::MathFracPi2 },
|
||||||
|
FRAC_PI_3 => { const = constant::Constant::MathFracPi3 },
|
||||||
|
FRAC_PI_4 => { const = constant::Constant::MathFracPi4 },
|
||||||
|
FRAC_PI_6 => { const = constant::Constant::MathFracPi6 },
|
||||||
|
FRAC_PI_8 => { const = constant::Constant::MathFracPi8 },
|
||||||
|
INF => { const = constant::Constant::MathInf },
|
||||||
|
LN_10 => { const = constant::Constant::MathLn10 },
|
||||||
|
LN_2 => { const = constant::Constant::MathLn2 },
|
||||||
|
LOG10_2 => { const = constant::Constant::MathLog102 },
|
||||||
|
LOG10_E => { const = constant::Constant::MathLog10E },
|
||||||
|
LOG2_10 => { const = constant::Constant::MathLog210 },
|
||||||
|
LOG2_E => { const = constant::Constant::MathLog2E },
|
||||||
|
PI => { const = constant::Constant::MathPi },
|
||||||
|
SQRT_2 => { const = constant::Constant::MathSqrt2 },
|
||||||
|
TAU => { const = constant::Constant::MathTau },
|
||||||
|
},
|
||||||
|
meta => {
|
||||||
|
id => { fn },
|
||||||
|
table => { fn },
|
||||||
|
tb => { fn },
|
||||||
|
},
|
||||||
|
parse => {
|
||||||
|
email => {
|
||||||
|
host => { fn },
|
||||||
|
user => { fn },
|
||||||
|
},
|
||||||
|
url => {
|
||||||
|
domain => { fn },
|
||||||
|
fragment => { fn },
|
||||||
|
host => { fn },
|
||||||
|
path => { fn },
|
||||||
|
port => { fn },
|
||||||
|
query => { fn },
|
||||||
|
scheme => { fn },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rand(func) => {
|
||||||
|
r#bool = "bool" => { fn },
|
||||||
|
r#enum = "enum" => { fn },
|
||||||
|
float => { fn },
|
||||||
|
guid => { fn },
|
||||||
|
int => { fn },
|
||||||
|
string => { fn },
|
||||||
|
time => { fn },
|
||||||
|
ulid => { fn },
|
||||||
|
uuid(func) => {
|
||||||
|
v4 => { fn },
|
||||||
|
v7 => { fn },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
search => {
|
||||||
|
score => { fn },
|
||||||
|
highlight => { fn },
|
||||||
|
offsets => { fn },
|
||||||
|
},
|
||||||
|
session => {
|
||||||
|
db => { fn },
|
||||||
|
id => { fn },
|
||||||
|
ip => { fn },
|
||||||
|
ns => { fn },
|
||||||
|
origin => { fn },
|
||||||
|
sc => { fn },
|
||||||
|
sd => { fn },
|
||||||
|
token => { fn },
|
||||||
|
},
|
||||||
|
string => {
|
||||||
|
concat => { fn },
|
||||||
|
contains => { fn },
|
||||||
|
ends_with = "endsWith" => { fn },
|
||||||
|
join => { fn },
|
||||||
|
len => { fn },
|
||||||
|
lowercase => { fn },
|
||||||
|
repeat => { fn },
|
||||||
|
replace => { fn },
|
||||||
|
reverse => { fn },
|
||||||
|
slice => { fn },
|
||||||
|
slug => { fn },
|
||||||
|
split => { fn },
|
||||||
|
starts_with = "startsWith" => { fn },
|
||||||
|
trim => { fn },
|
||||||
|
uppercase => { fn },
|
||||||
|
words => { fn },
|
||||||
|
distance => {
|
||||||
|
hamming => { fn },
|
||||||
|
levenshtein => { fn },
|
||||||
|
},
|
||||||
|
similarity => {
|
||||||
|
fuzzy => { fn },
|
||||||
|
jaro => { fn },
|
||||||
|
smithwaterman => { fn },
|
||||||
|
},
|
||||||
|
is => {
|
||||||
|
alphanum => { fn },
|
||||||
|
alpha => { fn },
|
||||||
|
ascii => { fn },
|
||||||
|
datetime => { fn },
|
||||||
|
domain => { fn },
|
||||||
|
email => { fn },
|
||||||
|
hexadecimal => { fn },
|
||||||
|
latitude => { fn },
|
||||||
|
longitude => { fn },
|
||||||
|
numeric => { fn },
|
||||||
|
semver => { fn },
|
||||||
|
url => { fn },
|
||||||
|
uuid => { fn },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
time => {
|
||||||
|
ceil => { fn },
|
||||||
|
day => { fn },
|
||||||
|
floor => { fn },
|
||||||
|
format => { fn },
|
||||||
|
group => { fn },
|
||||||
|
hour => { fn },
|
||||||
|
minute => { fn },
|
||||||
|
max => { fn },
|
||||||
|
min => { fn },
|
||||||
|
month => { fn },
|
||||||
|
nano => { fn },
|
||||||
|
now => { fn },
|
||||||
|
round => { fn },
|
||||||
|
second => { fn },
|
||||||
|
timezone => { fn },
|
||||||
|
unix => { fn },
|
||||||
|
wday => { fn },
|
||||||
|
week => { fn },
|
||||||
|
yday => { fn },
|
||||||
|
year => { fn },
|
||||||
|
from => {
|
||||||
|
micros => {fn},
|
||||||
|
millis => {fn},
|
||||||
|
unix => {fn},
|
||||||
|
secs => {fn},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
r#type = "type" => {
|
||||||
|
r#bool = "bool" => { fn },
|
||||||
|
datetime => { fn },
|
||||||
|
decimal => { fn },
|
||||||
|
duration => { fn },
|
||||||
|
fields => { fn },
|
||||||
|
field => { fn },
|
||||||
|
float => { fn },
|
||||||
|
int => { fn },
|
||||||
|
number => { fn },
|
||||||
|
point => { fn },
|
||||||
|
string => { fn },
|
||||||
|
table => { fn },
|
||||||
|
thing => { fn },
|
||||||
|
is => {
|
||||||
|
array => { fn },
|
||||||
|
r#bool = "bool" => { fn },
|
||||||
|
bytes => { fn },
|
||||||
|
collection => { fn },
|
||||||
|
datetime => { fn },
|
||||||
|
decimal => { fn },
|
||||||
|
duration => { fn },
|
||||||
|
float => { fn },
|
||||||
|
geometry => { fn },
|
||||||
|
int => { fn },
|
||||||
|
line => { fn },
|
||||||
|
null => { fn },
|
||||||
|
multiline => { fn },
|
||||||
|
multipoint => { fn },
|
||||||
|
multipolygon => { fn },
|
||||||
|
number => { fn },
|
||||||
|
object => { fn },
|
||||||
|
point => { fn },
|
||||||
|
polygon => { fn },
|
||||||
|
record => { fn },
|
||||||
|
string => { fn },
|
||||||
|
uuid => { fn },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vector => {
|
||||||
|
add => { fn },
|
||||||
|
angle => { fn },
|
||||||
|
divide => { fn },
|
||||||
|
cross => { fn },
|
||||||
|
dot => { fn },
|
||||||
|
magnitude => { fn },
|
||||||
|
multiply => { fn },
|
||||||
|
normalize => { fn },
|
||||||
|
project => { fn },
|
||||||
|
subtract => { fn },
|
||||||
|
distance => {
|
||||||
|
chebyshev => { fn },
|
||||||
|
euclidean => { fn },
|
||||||
|
hamming => { fn },
|
||||||
|
mahalanobis => { fn },
|
||||||
|
manhattan => { fn },
|
||||||
|
minkowski => { fn },
|
||||||
|
},
|
||||||
|
similarity => {
|
||||||
|
cosine => {fn },
|
||||||
|
jaccard => {fn },
|
||||||
|
pearson => {fn },
|
||||||
|
spearman => {fn },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count => { fn },
|
||||||
|
not => { fn },
|
||||||
|
sleep => { fn },
|
||||||
|
}
|
||||||
|
_parse_builtin_name(i)
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::error::Error::Parser;
|
use crate::sql::error::{IResult, ParseError};
|
||||||
use crate::sql::error::IResult;
|
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::take_while;
|
use nom::bytes::complete::take_while;
|
||||||
use nom::bytes::complete::take_while_m_n;
|
use nom::bytes::complete::take_while_m_n;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::character::is_alphanumeric;
|
use nom::character::is_alphanumeric;
|
||||||
|
use nom::combinator::map_res;
|
||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
use nom::Err::Error;
|
use nom::Err;
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
pub fn colons(i: &str) -> IResult<&str, ()> {
|
pub fn colons(i: &str) -> IResult<&str, ()> {
|
||||||
|
@ -36,52 +36,52 @@ pub fn commasorspace(i: &str) -> IResult<&str, ()> {
|
||||||
alt((commas, shouldbespace))(i)
|
alt((commas, shouldbespace))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openparentheses(i: &str) -> IResult<&str, ()> {
|
pub fn openparentheses(s: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = char('(')(i)?;
|
let (i, _) = char('(')(s)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn closeparentheses(i: &str) -> IResult<&str, ()> {
|
pub fn closeparentheses(i: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (s, _) = mightbespace(i)?;
|
||||||
let (i, _) = char(')')(i)?;
|
let (i, _) = char(')')(s)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openbraces(i: &str) -> IResult<&str, ()> {
|
pub fn openbraces(s: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = char('{')(i)?;
|
let (i, _) = char('{')(s)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn closebraces(i: &str) -> IResult<&str, ()> {
|
pub fn closebraces(i: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (s, _) = mightbespace(i)?;
|
||||||
let (i, _) = char('}')(i)?;
|
let (i, _) = char('}')(s)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openbracket(i: &str) -> IResult<&str, ()> {
|
pub fn openbracket(s: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = char('[')(i)?;
|
let (i, _) = char('[')(s)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn closebracket(i: &str) -> IResult<&str, ()> {
|
pub fn closebracket(i: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (s, _) = mightbespace(i)?;
|
||||||
let (i, _) = char(']')(i)?;
|
let (i, _) = char(']')(s)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openchevron(i: &str) -> IResult<&str, ()> {
|
pub fn openchevron(s: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, _) = char('<')(s)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn closechevron(i: &str) -> IResult<&str, ()> {
|
pub fn closechevron(i: &str) -> IResult<&str, &str> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (s, _) = mightbespace(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = char('>')(s)?;
|
||||||
Ok((i, ()))
|
Ok((i, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -105,33 +105,34 @@ pub fn val_char(chr: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_u64(i: &str) -> IResult<&str, u64> {
|
pub fn take_u64(i: &str) -> IResult<&str, u64> {
|
||||||
let (i, v) = take_while(is_digit)(i)?;
|
map_res(take_while(is_digit), |s: &str| s.parse::<u64>())(i)
|
||||||
match v.parse::<u64>() {
|
|
||||||
Ok(v) => Ok((i, v)),
|
|
||||||
_ => Err(Error(Parser(i))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_u32_len(i: &str) -> IResult<&str, (u32, usize)> {
|
pub fn take_u32_len(i: &str) -> IResult<&str, (u32, usize)> {
|
||||||
let (i, v) = take_while(is_digit)(i)?;
|
map_res(take_while(is_digit), |s: &str| s.parse::<u32>().map(|x| (x, s.len())))(i)
|
||||||
match v.parse::<u32>() {
|
|
||||||
Ok(n) => Ok((i, (n, v.len()))),
|
|
||||||
_ => Err(Error(Parser(i))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_digits(i: &str, n: usize) -> IResult<&str, u32> {
|
pub fn take_digits(i: &str, n: usize) -> IResult<&str, u32> {
|
||||||
let (i, v) = take_while_m_n(n, n, is_digit)(i)?;
|
map_res(take_while_m_n(n, n, is_digit), |s: &str| s.parse::<u32>())(i)
|
||||||
match v.parse::<u32>() {
|
|
||||||
Ok(v) => Ok((i, v)),
|
|
||||||
_ => Err(Error(Parser(i))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_digits_range(i: &str, n: usize, range: impl RangeBounds<u32>) -> IResult<&str, u32> {
|
pub fn take_digits_range(i: &str, n: usize, range: impl RangeBounds<u32>) -> IResult<&str, u32> {
|
||||||
let (i, v) = take_while_m_n(n, n, is_digit)(i)?;
|
let (i, v) = take_while_m_n(n, n, is_digit)(i)?;
|
||||||
match v.parse::<u32>() {
|
match v.parse::<u32>() {
|
||||||
Ok(v) if range.contains(&v) => Ok((i, v)),
|
Ok(v) => {
|
||||||
_ => Err(Error(Parser(i))),
|
if range.contains(&v) {
|
||||||
|
Ok((i, v))
|
||||||
|
} else {
|
||||||
|
Result::Err(Err::Error(ParseError::RangeError {
|
||||||
|
tried: i,
|
||||||
|
lower: range.start_bound().cloned(),
|
||||||
|
upper: range.end_bound().cloned(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => Result::Err(Err::Error(ParseError::ParseInt {
|
||||||
|
tried: v,
|
||||||
|
error,
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,11 @@ use crate::ctx::Context;
|
||||||
use crate::dbs::{Options, Transaction};
|
use crate::dbs::{Options, Transaction};
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::error::IResult;
|
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use crate::sql::Datetime;
|
use crate::sql::Datetime;
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
|
||||||
use nom::bytes::complete::tag_no_case;
|
|
||||||
use nom::combinator::value;
|
|
||||||
use nom::sequence::preceded;
|
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -122,71 +117,37 @@ impl fmt::Display for Constant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constant(i: &str) -> IResult<&str, Constant> {
|
|
||||||
alt((constant_math, constant_time))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_math(i: &str) -> IResult<&str, Constant> {
|
|
||||||
preceded(
|
|
||||||
tag_no_case("math::"),
|
|
||||||
alt((
|
|
||||||
value(Constant::MathE, tag_no_case("E")),
|
|
||||||
value(Constant::MathFrac1Pi, tag_no_case("FRAC_1_PI")),
|
|
||||||
value(Constant::MathFrac1Sqrt2, tag_no_case("FRAC_1_SQRT_2")),
|
|
||||||
value(Constant::MathFrac2Pi, tag_no_case("FRAC_2_PI")),
|
|
||||||
value(Constant::MathFrac2SqrtPi, tag_no_case("FRAC_2_SQRT_PI")),
|
|
||||||
value(Constant::MathFracPi2, tag_no_case("FRAC_PI_2")),
|
|
||||||
value(Constant::MathFracPi3, tag_no_case("FRAC_PI_3")),
|
|
||||||
value(Constant::MathFracPi4, tag_no_case("FRAC_PI_4")),
|
|
||||||
value(Constant::MathFracPi6, tag_no_case("FRAC_PI_6")),
|
|
||||||
value(Constant::MathFracPi8, tag_no_case("FRAC_PI_8")),
|
|
||||||
value(Constant::MathInf, tag_no_case("INF")),
|
|
||||||
value(Constant::MathLn10, tag_no_case("LN_10")),
|
|
||||||
value(Constant::MathLn2, tag_no_case("LN_2")),
|
|
||||||
value(Constant::MathLog102, tag_no_case("LOG10_2")),
|
|
||||||
value(Constant::MathLog10E, tag_no_case("LOG10_E")),
|
|
||||||
value(Constant::MathLog210, tag_no_case("LOG2_10")),
|
|
||||||
value(Constant::MathLog2E, tag_no_case("LOG2_E")),
|
|
||||||
value(Constant::MathPi, tag_no_case("PI")),
|
|
||||||
value(Constant::MathSqrt2, tag_no_case("SQRT_2")),
|
|
||||||
value(Constant::MathTau, tag_no_case("TAU")),
|
|
||||||
)),
|
|
||||||
)(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn constant_time(i: &str) -> IResult<&str, Constant> {
|
|
||||||
preceded(tag_no_case("time::"), alt((value(Constant::TimeEpoch, tag_no_case("EPOCH")),)))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
use crate::sql::builtin::{builtin_name, BuiltinName};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constant_lowercase() {
|
fn constant_lowercase() {
|
||||||
let sql = "math::pi";
|
let sql = "math::pi";
|
||||||
let res = constant(sql);
|
let res = builtin_name(sql);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(out, Constant::MathPi);
|
assert_eq!(out, BuiltinName::Constant(Constant::MathPi));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constant_uppercase() {
|
fn constant_uppercase() {
|
||||||
let sql = "MATH::PI";
|
let sql = "MATH::PI";
|
||||||
let res = constant(sql);
|
let res = builtin_name(sql);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(out, Constant::MathPi);
|
assert_eq!(out, BuiltinName::Constant(Constant::MathPi));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constant_mixedcase() {
|
fn constant_mixedcase() {
|
||||||
let sql = "math::PI";
|
let sql = "math::PI";
|
||||||
let res = constant(sql);
|
let res = builtin_name(sql);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(out, Constant::MathPi);
|
assert_eq!(out, BuiltinName::Constant(Constant::MathPi));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ use std::ops::Deref;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Datetime";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Datetime";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
|
||||||
|
@ -108,7 +110,7 @@ impl ops::Sub<Self> for Datetime {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn datetime(i: &str) -> IResult<&str, Datetime> {
|
pub fn datetime(i: &str) -> IResult<&str, Datetime> {
|
||||||
alt((datetime_single, datetime_double))(i)
|
expected("a datetime", alt((datetime_single, datetime_double)))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn datetime_single(i: &str) -> IResult<&str, Datetime> {
|
fn datetime_single(i: &str) -> IResult<&str, Datetime> {
|
||||||
|
|
|
@ -15,6 +15,8 @@ use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
static SECONDS_PER_YEAR: u64 = 365 * SECONDS_PER_DAY;
|
static SECONDS_PER_YEAR: u64 = 365 * SECONDS_PER_DAY;
|
||||||
static SECONDS_PER_WEEK: u64 = 7 * SECONDS_PER_DAY;
|
static SECONDS_PER_WEEK: u64 = 7 * SECONDS_PER_DAY;
|
||||||
static SECONDS_PER_DAY: u64 = 24 * SECONDS_PER_HOUR;
|
static SECONDS_PER_DAY: u64 = 24 * SECONDS_PER_HOUR;
|
||||||
|
@ -296,9 +298,11 @@ impl<'a> Sum<&'a Self> for Duration {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duration(i: &str) -> IResult<&str, Duration> {
|
pub fn duration(i: &str) -> IResult<&str, Duration> {
|
||||||
let (i, v) = many1(duration_raw)(i)?;
|
expected("a duration", |i| {
|
||||||
let (i, _) = ending(i)?;
|
let (i, v) = many1(duration_raw)(i)?;
|
||||||
Ok((i, v.iter().sum::<Duration>()))
|
let (i, _) = ending(i)?;
|
||||||
|
Ok((i, v.iter().sum::<Duration>()))
|
||||||
|
})(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration_raw(i: &str) -> IResult<&str, Duration> {
|
fn duration_raw(i: &str) -> IResult<&str, Duration> {
|
||||||
|
@ -319,7 +323,7 @@ fn duration_raw(i: &str) -> IResult<&str, Duration> {
|
||||||
_ => unreachable!("shouldn't have parsed {u} as duration unit"),
|
_ => unreachable!("shouldn't have parsed {u} as duration unit"),
|
||||||
};
|
};
|
||||||
|
|
||||||
std_duration.map(|d| (i, Duration(d))).ok_or(nom::Err::Error(crate::sql::Error::Parser(i)))
|
std_duration.map(|d| (i, Duration(d))).ok_or(nom::Err::Error(crate::sql::ParseError::Base(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part(i: &str) -> IResult<&str, u64> {
|
fn part(i: &str) -> IResult<&str, u64> {
|
||||||
|
|
|
@ -87,27 +87,39 @@ pub fn field(i: &str) -> IResult<&str, ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subquery(i: &str) -> IResult<&str, ()> {
|
pub fn subquery(i: &str) -> IResult<&str, ()> {
|
||||||
alt((
|
peek(alt((
|
||||||
|
value((), preceded(shouldbespace, tag_no_case("THEN"))),
|
||||||
|
value((), preceded(shouldbespace, tag_no_case("ELSE"))),
|
||||||
|
value((), preceded(shouldbespace, tag_no_case("END"))),
|
||||||
|i| {
|
|i| {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = char(';')(i)?;
|
alt((
|
||||||
let (i, _) = peek(alt((
|
value((), eof),
|
||||||
preceded(shouldbespace, tag_no_case("THEN")),
|
value((), char(';')),
|
||||||
preceded(shouldbespace, tag_no_case("ELSE")),
|
value((), char(',')),
|
||||||
preceded(shouldbespace, tag_no_case("END")),
|
value((), char('}')),
|
||||||
)))(i)?;
|
value((), char(')')),
|
||||||
Ok((i, ()))
|
value((), char(']')),
|
||||||
|
))(i)
|
||||||
},
|
},
|
||||||
peek(alt((
|
)))(i)
|
||||||
value((), preceded(shouldbespace, tag_no_case("THEN"))),
|
}
|
||||||
value((), preceded(shouldbespace, tag_no_case("ELSE"))),
|
|
||||||
value((), preceded(shouldbespace, tag_no_case("END"))),
|
pub fn query(i: &str) -> IResult<&str, ()> {
|
||||||
value((), comment),
|
peek(alt((
|
||||||
value((), char(']')),
|
value((), preceded(shouldbespace, tag_no_case("THEN"))),
|
||||||
value((), char('}')),
|
value((), preceded(shouldbespace, tag_no_case("ELSE"))),
|
||||||
value((), char(';')),
|
value((), preceded(shouldbespace, tag_no_case("END"))),
|
||||||
value((), char(',')),
|
|i| {
|
||||||
value((), eof),
|
let (i, _) = mightbespace(i)?;
|
||||||
))),
|
alt((
|
||||||
))(i)
|
value((), eof),
|
||||||
|
value((), char(';')),
|
||||||
|
value((), char(',')),
|
||||||
|
value((), char('}')),
|
||||||
|
value((), char(')')),
|
||||||
|
value((), char(']')),
|
||||||
|
))(i)
|
||||||
|
},
|
||||||
|
)))(i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
use nom::error::ErrorKind;
|
|
||||||
use nom::error::ParseError;
|
|
||||||
use nom::Err;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub enum Error<I> {
|
|
||||||
Parser(I),
|
|
||||||
ExcessiveDepth,
|
|
||||||
Field(I, String),
|
|
||||||
Split(I, String),
|
|
||||||
Order(I, String),
|
|
||||||
Group(I, String),
|
|
||||||
Role(I, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type IResult<I, O, E = Error<I>> = Result<(I, O), Err<E>>;
|
|
||||||
|
|
||||||
impl<I> ParseError<I> for Error<I> {
|
|
||||||
fn from_error_kind(input: I, _: ErrorKind) -> Self {
|
|
||||||
Self::Parser(input)
|
|
||||||
}
|
|
||||||
fn append(_: I, _: ErrorKind, other: Self) -> Self {
|
|
||||||
other
|
|
||||||
}
|
|
||||||
}
|
|
478
lib/src/sql/error/mod.rs
Normal file
478
lib/src/sql/error/mod.rs
Normal file
|
@ -0,0 +1,478 @@
|
||||||
|
use nom::error::ErrorKind;
|
||||||
|
use nom::error::FromExternalError;
|
||||||
|
use nom::error::ParseError as NomParseError;
|
||||||
|
use nom::Err;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::num::ParseFloatError;
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
use std::ops::Bound;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
pub use utils::*;
|
||||||
|
mod render;
|
||||||
|
pub use render::*;
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone)]
|
||||||
|
pub enum ParseError<I> {
|
||||||
|
Base(I),
|
||||||
|
Expected {
|
||||||
|
tried: I,
|
||||||
|
expected: &'static str,
|
||||||
|
},
|
||||||
|
Explained {
|
||||||
|
tried: I,
|
||||||
|
explained: &'static str,
|
||||||
|
},
|
||||||
|
ExplainedExpected {
|
||||||
|
tried: I,
|
||||||
|
explained: &'static str,
|
||||||
|
expected: &'static str,
|
||||||
|
},
|
||||||
|
MissingDelimiter {
|
||||||
|
opened: I,
|
||||||
|
tried: I,
|
||||||
|
},
|
||||||
|
ExcessiveDepth(I),
|
||||||
|
Field(I, String),
|
||||||
|
Split(I, String),
|
||||||
|
Order(I, String),
|
||||||
|
Group(I, String),
|
||||||
|
Role(I, String),
|
||||||
|
ParseInt {
|
||||||
|
tried: I,
|
||||||
|
error: ParseIntError,
|
||||||
|
},
|
||||||
|
ParseFloat {
|
||||||
|
tried: I,
|
||||||
|
error: ParseFloatError,
|
||||||
|
},
|
||||||
|
ParseDecimal {
|
||||||
|
tried: I,
|
||||||
|
error: rust_decimal::Error,
|
||||||
|
},
|
||||||
|
ParseRegex {
|
||||||
|
tried: I,
|
||||||
|
error: regex::Error,
|
||||||
|
},
|
||||||
|
RangeError {
|
||||||
|
tried: I,
|
||||||
|
lower: Bound<u32>,
|
||||||
|
upper: Bound<u32>,
|
||||||
|
},
|
||||||
|
InvalidUnicode {
|
||||||
|
tried: I,
|
||||||
|
},
|
||||||
|
InvalidPath {
|
||||||
|
tried: I,
|
||||||
|
parent: I,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Clone> ParseError<I> {
|
||||||
|
/// returns the input value where the parser failed.
|
||||||
|
pub fn tried(&self) -> I {
|
||||||
|
let (Self::Base(ref tried)
|
||||||
|
| Self::Expected {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::Expected {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::Explained {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::ExplainedExpected {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::ExcessiveDepth(ref tried)
|
||||||
|
| Self::MissingDelimiter {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::Field(ref tried, _)
|
||||||
|
| Self::Split(ref tried, _)
|
||||||
|
| Self::Order(ref tried, _)
|
||||||
|
| Self::Group(ref tried, _)
|
||||||
|
| Self::Role(ref tried, _)
|
||||||
|
| Self::ParseInt {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::ParseFloat {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::ParseDecimal {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::ParseRegex {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::RangeError {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::InvalidUnicode {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Self::InvalidPath {
|
||||||
|
ref tried,
|
||||||
|
..
|
||||||
|
}) = self;
|
||||||
|
tried.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A location inside a string.
|
||||||
|
///
|
||||||
|
/// Locations are 1 indexed, the first character on the first line being on line 1 column 1.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Location {
|
||||||
|
pub line: usize,
|
||||||
|
pub column: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Location {
|
||||||
|
/// Returns the location of a substring in the larger string.
|
||||||
|
pub fn of_in(substr: &str, s: &str) -> Self {
|
||||||
|
let offset = s
|
||||||
|
.len()
|
||||||
|
.checked_sub(substr.len())
|
||||||
|
.expect("tried to find location of substring in unrelated string");
|
||||||
|
let lines = s.split('\n').enumerate();
|
||||||
|
let mut total = 0;
|
||||||
|
for (idx, line) in lines {
|
||||||
|
// +1 for the '\n'
|
||||||
|
let new_total = total + line.len() + 1;
|
||||||
|
if new_total > offset {
|
||||||
|
// found line.
|
||||||
|
let line_offset = offset - total;
|
||||||
|
let column = line[..line_offset].chars().count();
|
||||||
|
// +1 because line and column are 1 index.
|
||||||
|
return Self {
|
||||||
|
line: idx + 1,
|
||||||
|
column: column + 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
total = new_total;
|
||||||
|
}
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseError<&str> {
|
||||||
|
/// Returns the error represented as a pretty printed string formatted on the original source
|
||||||
|
/// text.
|
||||||
|
pub fn render_on(&self, input: &str) -> RenderedError {
|
||||||
|
match self {
|
||||||
|
ParseError::Base(i) => {
|
||||||
|
let location = Location::of_in(i, input);
|
||||||
|
let text = format!(
|
||||||
|
"Failed to parse query at line {} column {}",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
expected,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!(
|
||||||
|
"Failed to parse query at line {} column {} expected {}",
|
||||||
|
location.line, location.column, expected
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Explained {
|
||||||
|
tried,
|
||||||
|
explained,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!(
|
||||||
|
"Failed to parse query at line {} column {}",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, Some(*explained));
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::ExplainedExpected {
|
||||||
|
tried,
|
||||||
|
expected,
|
||||||
|
explained,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!(
|
||||||
|
"Failed to parse query at line {} column {} expected {}",
|
||||||
|
location.line, location.column, expected
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, Some(*explained));
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::InvalidPath {
|
||||||
|
tried,
|
||||||
|
parent,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!(
|
||||||
|
"Path is not a member of {parent} at line {} column {}",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::MissingDelimiter {
|
||||||
|
tried,
|
||||||
|
opened,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = format!(
|
||||||
|
"Missing closing delimiter at line {} column {}",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let error_snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
let location = Location::of_in(opened, input);
|
||||||
|
let open_snippet = Snippet::from_source_location(
|
||||||
|
input,
|
||||||
|
location,
|
||||||
|
Some("expected this delimiter to be closed"),
|
||||||
|
);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![error_snippet, open_snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::ExcessiveDepth(tried) => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!(
|
||||||
|
"Exceeded maximum parse depth at line {} column {}",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Field(tried, f) => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = format!(
|
||||||
|
"Found '{f}' in SELECT clause at line {} column {}, but field is not an aggregate function, and is not present in GROUP BY expression",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Split(tried, f) => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = format!(
|
||||||
|
"Found '{f}' in SPLIT ON clause at line {} column {}, but field is is not present in SELECT expression",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Order(tried, f) => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = format!(
|
||||||
|
"Found '{f}' in ORDER BY clause at line {} column {}, but field is is not present in SELECT expression",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Group(tried, f) => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = format!(
|
||||||
|
"Found '{f}' in GROUP BY clause at line {} column {}, but field is is not present in SELECT expression",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Role(tried, r) => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = format!(
|
||||||
|
"Invalid role '{r}' at line {} column {}.",
|
||||||
|
location.line, location.column
|
||||||
|
);
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::ParseInt {
|
||||||
|
tried,
|
||||||
|
error,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!("Failed to parse '{tried}' as an integer: {error}.");
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::ParseFloat {
|
||||||
|
tried,
|
||||||
|
error,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!("Failed to parse '{tried}' as a float: {error}.");
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::ParseDecimal {
|
||||||
|
tried,
|
||||||
|
error,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!("Failed to parse '{tried}' as decimal: {error}.");
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::ParseRegex {
|
||||||
|
tried,
|
||||||
|
error,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
let text = format!("Failed to parse '{tried}' as a regex: {error}.");
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseError::RangeError {
|
||||||
|
tried,
|
||||||
|
lower,
|
||||||
|
upper,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
|
||||||
|
let mut text =
|
||||||
|
format!("Failed to parse '{tried}' as a bounded integer with bounds");
|
||||||
|
// Writing to a string can't return an error.
|
||||||
|
match lower {
|
||||||
|
Bound::Included(x) => write!(&mut text, "[{}", x).unwrap(),
|
||||||
|
Bound::Excluded(x) => write!(&mut text, "({}", x).unwrap(),
|
||||||
|
Bound::Unbounded => {}
|
||||||
|
}
|
||||||
|
write!(&mut text, "...").unwrap();
|
||||||
|
match upper {
|
||||||
|
Bound::Included(x) => write!(&mut text, "{}]", x).unwrap(),
|
||||||
|
Bound::Excluded(x) => write!(&mut text, "{})", x).unwrap(),
|
||||||
|
Bound::Unbounded => {}
|
||||||
|
}
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::InvalidUnicode {
|
||||||
|
tried,
|
||||||
|
} => {
|
||||||
|
let location = Location::of_in(tried, input);
|
||||||
|
let text = "Invalid unicode escape code.".to_string();
|
||||||
|
let snippet = Snippet::from_source_location(input, location, None);
|
||||||
|
RenderedError {
|
||||||
|
text,
|
||||||
|
snippets: vec![snippet],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type IResult<I, O, E = ParseError<I>> = Result<(I, O), Err<E>>;
|
||||||
|
|
||||||
|
impl<I> FromExternalError<I, ParseIntError> for ParseError<I> {
|
||||||
|
fn from_external_error(input: I, _kind: ErrorKind, e: ParseIntError) -> Self {
|
||||||
|
ParseError::ParseInt {
|
||||||
|
error: e,
|
||||||
|
tried: input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> FromExternalError<I, ParseFloatError> for ParseError<I> {
|
||||||
|
fn from_external_error(input: I, _kind: ErrorKind, e: ParseFloatError) -> Self {
|
||||||
|
ParseError::ParseFloat {
|
||||||
|
error: e,
|
||||||
|
tried: input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> FromExternalError<I, regex::Error> for ParseError<I> {
|
||||||
|
fn from_external_error(input: I, _kind: ErrorKind, e: regex::Error) -> Self {
|
||||||
|
ParseError::ParseRegex {
|
||||||
|
error: e,
|
||||||
|
tried: input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> NomParseError<I> for ParseError<I> {
|
||||||
|
fn from_error_kind(input: I, _: ErrorKind) -> Self {
|
||||||
|
Self::Base(input)
|
||||||
|
}
|
||||||
|
fn append(_: I, _: ErrorKind, other: Self) -> Self {
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
144
lib/src/sql/error/render.rs
Normal file
144
lib/src/sql/error/render.rs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::Location;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RenderedError {
|
||||||
|
pub text: String,
|
||||||
|
pub snippets: Vec<Snippet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RenderedError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
writeln!(f, "{}", self.text)?;
|
||||||
|
for s in self.snippets.iter() {
|
||||||
|
writeln!(f, "{}", s)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the snippet was truncated.
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||||
|
pub enum Truncation {
|
||||||
|
/// The snippet wasn't truncated
|
||||||
|
None,
|
||||||
|
/// The snippet was truncated at the start
|
||||||
|
Start,
|
||||||
|
/// The snippet was truncated at the end
|
||||||
|
End,
|
||||||
|
/// Both sided of the snippet where truncated.
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A piece of the source code with a location and an optional explenation.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Snippet {
|
||||||
|
/// The part of the orignal source code,
|
||||||
|
source: String,
|
||||||
|
/// Wether part of the source line was truncated.
|
||||||
|
truncation: Truncation,
|
||||||
|
/// The location of the snippet in the orignal source code.
|
||||||
|
location: Location,
|
||||||
|
/// The offset into the snippet where the location is.
|
||||||
|
offset: usize,
|
||||||
|
/// A possible explanation for this snippet.
|
||||||
|
explain: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snippet {
|
||||||
|
/// How long with the source line have to be before it gets truncated.
|
||||||
|
const MAX_SOURCE_DISPLAY_LEN: usize = 80;
|
||||||
|
/// How far the will have to be in the source line before everything before it gets truncated.
|
||||||
|
const MAX_ERROR_LINE_OFFSET: usize = 50;
|
||||||
|
|
||||||
|
pub fn from_source_location(
|
||||||
|
source: &str,
|
||||||
|
location: Location,
|
||||||
|
explain: Option<&'static str>,
|
||||||
|
) -> Self {
|
||||||
|
let line = source.split('\n').nth(location.line - 1).unwrap();
|
||||||
|
let (line, truncation, offset) = Self::truncate_line(line, location.column);
|
||||||
|
|
||||||
|
Snippet {
|
||||||
|
source: line.to_owned(),
|
||||||
|
truncation,
|
||||||
|
location,
|
||||||
|
offset,
|
||||||
|
explain: explain.map(|x| x.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trims whitespace of an line and additionally truncates a string if it is too long.
|
||||||
|
fn truncate_line(mut line: &str, around_offset: usize) -> (&str, Truncation, usize) {
|
||||||
|
let full_line_length = line.len();
|
||||||
|
line = line.trim_start();
|
||||||
|
let mut offset = around_offset - (full_line_length - line.len());
|
||||||
|
line = line.trim_end();
|
||||||
|
let mut truncation = Truncation::None;
|
||||||
|
|
||||||
|
if around_offset > Self::MAX_ERROR_LINE_OFFSET {
|
||||||
|
// Actual error is to far to the right, just truncated everything to the left.
|
||||||
|
// show some prefix for some extra context.
|
||||||
|
let extra_offset = around_offset - 10;
|
||||||
|
let mut chars = line.chars();
|
||||||
|
for _ in 0..extra_offset {
|
||||||
|
chars.next();
|
||||||
|
}
|
||||||
|
offset -= extra_offset;
|
||||||
|
line = chars.as_str();
|
||||||
|
truncation = Truncation::Start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.chars().count() > Self::MAX_SOURCE_DISPLAY_LEN {
|
||||||
|
// Line is too long, truncate to source
|
||||||
|
let mut size = Self::MAX_SOURCE_DISPLAY_LEN - 3;
|
||||||
|
if truncation == Truncation::Start {
|
||||||
|
truncation = Truncation::Both;
|
||||||
|
size -= 3;
|
||||||
|
} else {
|
||||||
|
truncation = Truncation::End
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap because we just checked if the line length is longer then this.
|
||||||
|
let truncate_index = line.char_indices().nth(size).unwrap().0;
|
||||||
|
line = &line[..truncate_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
(line, truncation, offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Snippet {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
// extra spacing for the line number
|
||||||
|
let spacing = self.location.line.ilog10() as usize + 1;
|
||||||
|
writeln!(f, "{:>spacing$} |", "")?;
|
||||||
|
write!(f, "{:>spacing$} | ", self.location.line)?;
|
||||||
|
match self.truncation {
|
||||||
|
Truncation::None => {
|
||||||
|
writeln!(f, "{}", self.source)?;
|
||||||
|
}
|
||||||
|
Truncation::Start => {
|
||||||
|
writeln!(f, "...{}", self.source)?;
|
||||||
|
}
|
||||||
|
Truncation::End => {
|
||||||
|
writeln!(f, "{}...", self.source)?;
|
||||||
|
}
|
||||||
|
Truncation::Both => {
|
||||||
|
writeln!(f, "...{}...", self.source)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let error_offset = self.offset
|
||||||
|
+ if matches!(self.truncation, Truncation::Start | Truncation::Both) {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
write!(f, "{:>spacing$} | {:>error_offset$} ", "", "^",)?;
|
||||||
|
if let Some(ref explain) = self.explain {
|
||||||
|
write!(f, "{explain}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
134
lib/src/sql/error/utils.rs
Normal file
134
lib/src/sql/error/utils.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
use super::{IResult, ParseError};
|
||||||
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::Err;
|
||||||
|
use nom::Parser;
|
||||||
|
|
||||||
|
pub fn expected<I, O, P>(expect: &'static str, mut parser: P) -> impl FnMut(I) -> IResult<I, O>
|
||||||
|
where
|
||||||
|
P: Parser<I, O, ParseError<I>>,
|
||||||
|
{
|
||||||
|
move |input: I| match parser.parse(input) {
|
||||||
|
Err(Err::Error(err)) => match err {
|
||||||
|
ParseError::Base(tried) => Err(Err::Error(ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
expected: expect,
|
||||||
|
})),
|
||||||
|
ParseError::Explained {
|
||||||
|
tried,
|
||||||
|
explained,
|
||||||
|
} => Err(Err::Error(ParseError::ExplainedExpected {
|
||||||
|
tried,
|
||||||
|
expected: expect,
|
||||||
|
explained,
|
||||||
|
})),
|
||||||
|
ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
..
|
||||||
|
} => Err(Err::Error(ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
expected: expect,
|
||||||
|
})),
|
||||||
|
x => Err(Err::Error(x)),
|
||||||
|
},
|
||||||
|
Err(Err::Failure(err)) => match err {
|
||||||
|
ParseError::Base(tried) => Err(Err::Failure(ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
expected: expect,
|
||||||
|
})),
|
||||||
|
ParseError::Explained {
|
||||||
|
tried,
|
||||||
|
explained,
|
||||||
|
} => Err(Err::Failure(ParseError::ExplainedExpected {
|
||||||
|
tried,
|
||||||
|
expected: expect,
|
||||||
|
explained,
|
||||||
|
})),
|
||||||
|
ParseError::Expected {
|
||||||
|
tried: input,
|
||||||
|
..
|
||||||
|
} => Err(Err::Failure(ParseError::Expected {
|
||||||
|
tried: input,
|
||||||
|
expected: expect,
|
||||||
|
})),
|
||||||
|
x => Err(Err::Failure(x)),
|
||||||
|
},
|
||||||
|
rest => rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExplainResultExt<I, O> {
|
||||||
|
/// A function which adds a explaination to an error if the parser fails at a place which can
|
||||||
|
/// be parsed with the given parser.
|
||||||
|
fn explain<P, O1>(self, explain: &'static str, condition: P) -> Self
|
||||||
|
where
|
||||||
|
P: Parser<I, O1, ParseError<I>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Clone, O> ExplainResultExt<I, O> for IResult<I, O> {
|
||||||
|
fn explain<P, O1>(self, explain: &'static str, mut condition: P) -> Self
|
||||||
|
where
|
||||||
|
P: Parser<I, O1, ParseError<I>>,
|
||||||
|
{
|
||||||
|
let error = match self {
|
||||||
|
Ok(x) => return Ok(x),
|
||||||
|
Err(e) => e,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut was_failure = false;
|
||||||
|
let error = match error {
|
||||||
|
Err::Error(e) => e,
|
||||||
|
Err::Failure(e) => {
|
||||||
|
was_failure = true;
|
||||||
|
e
|
||||||
|
}
|
||||||
|
Err::Incomplete(e) => return Err(Err::Incomplete(e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_error = match error {
|
||||||
|
ParseError::Base(tried) => {
|
||||||
|
if condition.parse(tried.clone()).is_ok() {
|
||||||
|
ParseError::Explained {
|
||||||
|
tried,
|
||||||
|
explained: explain,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ParseError::Base(tried)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
expected,
|
||||||
|
} => {
|
||||||
|
if condition.parse(tried.clone()).is_ok() {
|
||||||
|
ParseError::ExplainedExpected {
|
||||||
|
tried,
|
||||||
|
expected,
|
||||||
|
explained: explain,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ParseError::Expected {
|
||||||
|
tried,
|
||||||
|
expected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e => e,
|
||||||
|
};
|
||||||
|
|
||||||
|
if was_failure {
|
||||||
|
Err(Err::Failure(new_error))
|
||||||
|
} else {
|
||||||
|
Err(Err::Error(new_error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_tag_no_case(tag: &'static str) -> impl FnMut(&str) -> IResult<&str, &str> {
|
||||||
|
move |input: &str| match tag_no_case(tag).parse(input) {
|
||||||
|
Result::Err(_) => Err(Err::Failure(ParseError::Expected {
|
||||||
|
tried: input,
|
||||||
|
expected: tag,
|
||||||
|
})),
|
||||||
|
rest => rest,
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ use crate::fnc;
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::operator::{self, Operator};
|
use crate::sql::operator::{self, Operator};
|
||||||
use crate::sql::value::{single, value, Value};
|
use crate::sql::value::{single, Value};
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -225,12 +225,13 @@ pub fn unary(i: &str) -> IResult<&str, Expression> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn binary(i: &str) -> IResult<&str, Expression> {
|
pub fn binary(i: &str) -> IResult<&str, Expression> {
|
||||||
let (i, l) = single(i)?;
|
let (i, l) = single(i)?;
|
||||||
let (i, o) = operator::binary(i)?;
|
let (i, o) = operator::binary(i)?;
|
||||||
// Make sure to dive if the query is a right-deep binary tree.
|
// Make sure to dive if the query is a right-deep binary tree.
|
||||||
let _diving = crate::sql::parser::depth::dive()?;
|
let _diving = crate::sql::parser::depth::dive(i)?;
|
||||||
let (i, r) = value(i)?;
|
let (i, r) = crate::sql::value::value(i)?;
|
||||||
let v = match r {
|
let v = match r {
|
||||||
Value::Expression(r) => r.augment(l, o),
|
Value::Expression(r) => r.augment(l, o),
|
||||||
_ => Expression::new(l, o, r),
|
_ => Expression::new(l, o, r),
|
||||||
|
|
|
@ -21,12 +21,13 @@ use nom::bytes::complete::take_while1;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::combinator::{cut, recognize};
|
use nom::combinator::{cut, recognize};
|
||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use nom::sequence::{preceded, terminated};
|
use nom::sequence::terminated;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
use super::util::delimited_list0;
|
use super::util::delimited_list0;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Function";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Function";
|
||||||
|
@ -253,17 +254,16 @@ impl fmt::Display for Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(i: &str) -> IResult<&str, Function> {
|
pub fn defined_function(i: &str) -> IResult<&str, Function> {
|
||||||
alt((normal, custom, script))(i)
|
alt((custom, script))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normal(i: &str) -> IResult<&str, Function> {
|
pub fn builtin_function<'a>(name: &'a str, i: &'a str) -> IResult<&'a str, Function> {
|
||||||
let (i, s) = function_names(i)?;
|
let (i, a) = expected(
|
||||||
let (i, a) =
|
"function arguments",
|
||||||
delimited_list0(openparentheses, commas, terminated(cut(value), mightbespace), char(')'))(
|
delimited_list0(openparentheses, commas, terminated(cut(value), mightbespace), char(')')),
|
||||||
i,
|
)(i)?;
|
||||||
)?;
|
Ok((i, Function::Normal(name.to_string(), a)))
|
||||||
Ok((i, Function::Normal(s.to_string(), a)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn custom(i: &str) -> IResult<&str, Function> {
|
pub fn custom(i: &str) -> IResult<&str, Function> {
|
||||||
|
@ -271,11 +271,14 @@ pub fn custom(i: &str) -> IResult<&str, Function> {
|
||||||
cut(|i| {
|
cut(|i| {
|
||||||
let (i, s) = recognize(separated_list1(tag("::"), take_while1(val_char)))(i)?;
|
let (i, s) = recognize(separated_list1(tag("::"), take_while1(val_char)))(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, a) = delimited_list0(
|
let (i, a) = expected(
|
||||||
openparentheses,
|
"function arguments",
|
||||||
commas,
|
delimited_list0(
|
||||||
terminated(cut(value), mightbespace),
|
cut(openparentheses),
|
||||||
char(')'),
|
commas,
|
||||||
|
terminated(cut(value), mightbespace),
|
||||||
|
char(')'),
|
||||||
|
),
|
||||||
)(i)?;
|
)(i)?;
|
||||||
Ok((i, Function::Custom(s.to_string(), a)))
|
Ok((i, Function::Custom(s.to_string(), a)))
|
||||||
})(i)
|
})(i)
|
||||||
|
@ -299,389 +302,23 @@ fn script(i: &str) -> IResult<&str, Function> {
|
||||||
})(i)
|
})(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn function_names(i: &str) -> IResult<&str, &str> {
|
|
||||||
recognize(alt((
|
|
||||||
alt((
|
|
||||||
preceded(tag("array::"), cut(function_array)),
|
|
||||||
preceded(tag("bytes::"), cut(function_bytes)),
|
|
||||||
preceded(tag("crypto::"), cut(function_crypto)),
|
|
||||||
preceded(tag("duration::"), cut(function_duration)),
|
|
||||||
preceded(tag("encoding::"), cut(function_encoding)),
|
|
||||||
preceded(tag("geo::"), cut(function_geo)),
|
|
||||||
preceded(tag("http::"), cut(function_http)),
|
|
||||||
// Don't cut in time and math for now since there are also constant's with the same
|
|
||||||
// prefix.
|
|
||||||
preceded(tag("math::"), function_math),
|
|
||||||
preceded(tag("meta::"), cut(function_meta)),
|
|
||||||
preceded(tag("parse::"), cut(function_parse)),
|
|
||||||
preceded(tag("rand::"), cut(function_rand)),
|
|
||||||
preceded(tag("search::"), cut(function_search)),
|
|
||||||
preceded(tag("session::"), cut(function_session)),
|
|
||||||
preceded(tag("string::"), cut(function_string)),
|
|
||||||
// Don't cut in time and math for now since there are also constant's with the same
|
|
||||||
// prefix.
|
|
||||||
preceded(tag("time::"), function_time),
|
|
||||||
preceded(tag("type::"), cut(function_type)),
|
|
||||||
preceded(tag("vector::"), cut(function_vector)),
|
|
||||||
)),
|
|
||||||
alt((tag("count"), tag("not"), tag("rand"), tag("sleep"))),
|
|
||||||
)))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_array(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
alt((
|
|
||||||
tag("add"),
|
|
||||||
tag("all"),
|
|
||||||
tag("any"),
|
|
||||||
tag("append"),
|
|
||||||
tag("at"),
|
|
||||||
tag("boolean_and"),
|
|
||||||
tag("boolean_not"),
|
|
||||||
tag("boolean_or"),
|
|
||||||
tag("boolean_xor"),
|
|
||||||
tag("clump"),
|
|
||||||
tag("combine"),
|
|
||||||
tag("complement"),
|
|
||||||
tag("concat"),
|
|
||||||
tag("difference"),
|
|
||||||
tag("distinct"),
|
|
||||||
tag("filter_index"),
|
|
||||||
tag("find_index"),
|
|
||||||
tag("first"),
|
|
||||||
tag("flatten"),
|
|
||||||
tag("group"),
|
|
||||||
tag("insert"),
|
|
||||||
)),
|
|
||||||
alt((
|
|
||||||
tag("intersect"),
|
|
||||||
tag("join"),
|
|
||||||
tag("last"),
|
|
||||||
tag("len"),
|
|
||||||
tag("logical_and"),
|
|
||||||
tag("logical_or"),
|
|
||||||
tag("logical_xor"),
|
|
||||||
tag("matches"),
|
|
||||||
tag("max"),
|
|
||||||
tag("min"),
|
|
||||||
tag("pop"),
|
|
||||||
tag("prepend"),
|
|
||||||
tag("push"),
|
|
||||||
)),
|
|
||||||
alt((
|
|
||||||
tag("remove"),
|
|
||||||
tag("reverse"),
|
|
||||||
tag("slice"),
|
|
||||||
tag("sort::asc"),
|
|
||||||
tag("sort::desc"),
|
|
||||||
tag("sort"),
|
|
||||||
tag("transpose"),
|
|
||||||
tag("union"),
|
|
||||||
)),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_bytes(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((tag("len"),))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_crypto(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
preceded(tag("argon2::"), alt((tag("compare"), tag("generate")))),
|
|
||||||
preceded(tag("bcrypt::"), alt((tag("compare"), tag("generate")))),
|
|
||||||
preceded(tag("pbkdf2::"), alt((tag("compare"), tag("generate")))),
|
|
||||||
preceded(tag("scrypt::"), alt((tag("compare"), tag("generate")))),
|
|
||||||
tag("md5"),
|
|
||||||
tag("sha1"),
|
|
||||||
tag("sha256"),
|
|
||||||
tag("sha512"),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_duration(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("days"),
|
|
||||||
tag("hours"),
|
|
||||||
tag("micros"),
|
|
||||||
tag("millis"),
|
|
||||||
tag("mins"),
|
|
||||||
tag("nanos"),
|
|
||||||
tag("secs"),
|
|
||||||
tag("weeks"),
|
|
||||||
tag("years"),
|
|
||||||
preceded(
|
|
||||||
tag("from::"),
|
|
||||||
alt((
|
|
||||||
tag("days"),
|
|
||||||
tag("hours"),
|
|
||||||
tag("micros"),
|
|
||||||
tag("millis"),
|
|
||||||
tag("mins"),
|
|
||||||
tag("nanos"),
|
|
||||||
tag("secs"),
|
|
||||||
tag("weeks"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_encoding(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((preceded(tag("base64::"), alt((tag("decode"), tag("encode")))),))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_geo(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("area"),
|
|
||||||
tag("bearing"),
|
|
||||||
tag("centroid"),
|
|
||||||
tag("distance"),
|
|
||||||
preceded(tag("hash::"), alt((tag("decode"), tag("encode")))),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_http(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((tag("head"), tag("get"), tag("put"), tag("post"), tag("patch"), tag("delete")))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_math(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
alt((
|
|
||||||
tag("abs"),
|
|
||||||
tag("bottom"),
|
|
||||||
tag("ceil"),
|
|
||||||
tag("fixed"),
|
|
||||||
tag("floor"),
|
|
||||||
tag("interquartile"),
|
|
||||||
tag("max"),
|
|
||||||
tag("mean"),
|
|
||||||
tag("median"),
|
|
||||||
tag("midhinge"),
|
|
||||||
tag("min"),
|
|
||||||
tag("mode"),
|
|
||||||
)),
|
|
||||||
alt((
|
|
||||||
tag("nearestrank"),
|
|
||||||
tag("percentile"),
|
|
||||||
tag("pow"),
|
|
||||||
tag("product"),
|
|
||||||
tag("round"),
|
|
||||||
tag("spread"),
|
|
||||||
tag("sqrt"),
|
|
||||||
tag("stddev"),
|
|
||||||
tag("sum"),
|
|
||||||
tag("top"),
|
|
||||||
tag("trimean"),
|
|
||||||
tag("variance"),
|
|
||||||
)),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_meta(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((tag("id"), tag("table"), tag("tb")))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_parse(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
preceded(tag("email::"), alt((tag("host"), tag("user")))),
|
|
||||||
preceded(
|
|
||||||
tag("url::"),
|
|
||||||
alt((
|
|
||||||
tag("domain"),
|
|
||||||
tag("fragment"),
|
|
||||||
tag("host"),
|
|
||||||
tag("path"),
|
|
||||||
tag("port"),
|
|
||||||
tag("query"),
|
|
||||||
tag("scheme"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_rand(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("bool"),
|
|
||||||
tag("enum"),
|
|
||||||
tag("float"),
|
|
||||||
tag("guid"),
|
|
||||||
tag("int"),
|
|
||||||
tag("string"),
|
|
||||||
tag("time"),
|
|
||||||
tag("ulid"),
|
|
||||||
tag("uuid::v4"),
|
|
||||||
tag("uuid::v7"),
|
|
||||||
tag("uuid"),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_search(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((tag("score"), tag("highlight"), tag("offsets")))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_session(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("db"),
|
|
||||||
tag("id"),
|
|
||||||
tag("ip"),
|
|
||||||
tag("ns"),
|
|
||||||
tag("origin"),
|
|
||||||
tag("sc"),
|
|
||||||
tag("sd"),
|
|
||||||
tag("token"),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_string(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("concat"),
|
|
||||||
tag("contains"),
|
|
||||||
tag("endsWith"),
|
|
||||||
tag("join"),
|
|
||||||
tag("len"),
|
|
||||||
tag("lowercase"),
|
|
||||||
tag("repeat"),
|
|
||||||
tag("replace"),
|
|
||||||
tag("reverse"),
|
|
||||||
tag("slice"),
|
|
||||||
tag("slug"),
|
|
||||||
tag("split"),
|
|
||||||
tag("startsWith"),
|
|
||||||
tag("trim"),
|
|
||||||
tag("uppercase"),
|
|
||||||
tag("words"),
|
|
||||||
preceded(tag("distance::"), alt((tag("hamming"), tag("levenshtein")))),
|
|
||||||
preceded(
|
|
||||||
tag("is::"),
|
|
||||||
alt((
|
|
||||||
tag("alphanum"),
|
|
||||||
tag("alpha"),
|
|
||||||
tag("ascii"),
|
|
||||||
tag("datetime"),
|
|
||||||
tag("domain"),
|
|
||||||
tag("email"),
|
|
||||||
tag("hexadecimal"),
|
|
||||||
tag("latitude"),
|
|
||||||
tag("longitude"),
|
|
||||||
tag("numeric"),
|
|
||||||
tag("semver"),
|
|
||||||
tag("url"),
|
|
||||||
tag("uuid"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
preceded(tag("similarity::"), alt((tag("fuzzy"), tag("jaro"), tag("smithwaterman")))),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_time(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("ceil"),
|
|
||||||
tag("day"),
|
|
||||||
tag("floor"),
|
|
||||||
tag("format"),
|
|
||||||
tag("group"),
|
|
||||||
tag("hour"),
|
|
||||||
tag("minute"),
|
|
||||||
tag("max"),
|
|
||||||
tag("min"),
|
|
||||||
tag("month"),
|
|
||||||
tag("nano"),
|
|
||||||
tag("now"),
|
|
||||||
tag("round"),
|
|
||||||
tag("second"),
|
|
||||||
tag("timezone"),
|
|
||||||
tag("unix"),
|
|
||||||
tag("wday"),
|
|
||||||
tag("week"),
|
|
||||||
tag("yday"),
|
|
||||||
tag("year"),
|
|
||||||
preceded(tag("from::"), alt((tag("micros"), tag("millis"), tag("secs"), tag("unix")))),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_type(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("bool"),
|
|
||||||
tag("datetime"),
|
|
||||||
tag("decimal"),
|
|
||||||
tag("duration"),
|
|
||||||
tag("fields"),
|
|
||||||
tag("field"),
|
|
||||||
tag("float"),
|
|
||||||
tag("int"),
|
|
||||||
tag("number"),
|
|
||||||
tag("point"),
|
|
||||||
tag("string"),
|
|
||||||
tag("table"),
|
|
||||||
tag("thing"),
|
|
||||||
preceded(
|
|
||||||
tag("is::"),
|
|
||||||
alt((
|
|
||||||
alt((
|
|
||||||
tag("array"),
|
|
||||||
tag("bool"),
|
|
||||||
tag("bytes"),
|
|
||||||
tag("collection"),
|
|
||||||
tag("datetime"),
|
|
||||||
tag("decimal"),
|
|
||||||
tag("duration"),
|
|
||||||
tag("float"),
|
|
||||||
tag("geometry"),
|
|
||||||
tag("int"),
|
|
||||||
tag("line"),
|
|
||||||
)),
|
|
||||||
alt((
|
|
||||||
tag("null"),
|
|
||||||
tag("multiline"),
|
|
||||||
tag("multipoint"),
|
|
||||||
tag("multipolygon"),
|
|
||||||
tag("number"),
|
|
||||||
tag("object"),
|
|
||||||
tag("point"),
|
|
||||||
tag("polygon"),
|
|
||||||
tag("record"),
|
|
||||||
tag("string"),
|
|
||||||
tag("uuid"),
|
|
||||||
)),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn function_vector(i: &str) -> IResult<&str, &str> {
|
|
||||||
alt((
|
|
||||||
tag("add"),
|
|
||||||
tag("angle"),
|
|
||||||
tag("divide"),
|
|
||||||
tag("cross"),
|
|
||||||
tag("dot"),
|
|
||||||
tag("magnitude"),
|
|
||||||
tag("multiply"),
|
|
||||||
tag("normalize"),
|
|
||||||
tag("project"),
|
|
||||||
tag("subtract"),
|
|
||||||
preceded(
|
|
||||||
tag("distance::"),
|
|
||||||
alt((
|
|
||||||
tag("chebyshev"),
|
|
||||||
tag("euclidean"),
|
|
||||||
tag("hamming"),
|
|
||||||
tag("mahalanobis"),
|
|
||||||
tag("manhattan"),
|
|
||||||
tag("minkowski"),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
preceded(
|
|
||||||
tag("similarity::"),
|
|
||||||
alt((tag("cosine"), tag("jaccard"), tag("pearson"), tag("spearman"))),
|
|
||||||
),
|
|
||||||
))(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::sql::test::Parse;
|
use crate::sql::{
|
||||||
|
builtin::{builtin_name, BuiltinName},
|
||||||
|
test::Parse,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn function(i: &str) -> IResult<&str, Function> {
|
||||||
|
alt((defined_function, |i| {
|
||||||
|
let (i, name) = builtin_name(i)?;
|
||||||
|
let BuiltinName::Function(x) = name else {
|
||||||
|
panic!("not a function")
|
||||||
|
};
|
||||||
|
builtin_function(x, i)
|
||||||
|
}))(i)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_single() {
|
fn function_single() {
|
||||||
|
|
|
@ -13,6 +13,8 @@ use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::util::expect_delimited;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Future";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Future";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
|
@ -50,9 +52,7 @@ impl fmt::Display for Future {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn future(i: &str) -> IResult<&str, Future> {
|
pub fn future(i: &str) -> IResult<&str, Future> {
|
||||||
let (i, _) = openchevron(i)?;
|
let (i, _) = expect_delimited(openchevron, tag("future"), closechevron)(i)?;
|
||||||
let (i, _) = tag("future")(i)?;
|
|
||||||
let (i, _) = closechevron(i)?;
|
|
||||||
cut(|i| {
|
cut(|i| {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, v) = block(i)?;
|
let (i, v) = block(i)?;
|
||||||
|
|
|
@ -627,7 +627,7 @@ impl hash::Hash for Geometry {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn geometry(i: &str) -> IResult<&str, Geometry> {
|
pub fn geometry(i: &str) -> IResult<&str, Geometry> {
|
||||||
let _diving = crate::sql::parser::depth::dive()?;
|
let _diving = crate::sql::parser::depth::dive(i)?;
|
||||||
alt((simple, normal))(i)
|
alt((simple, normal))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter, Write};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
|
|
||||||
|
use super::util::expect_delimited;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
pub struct Graph {
|
pub struct Graph {
|
||||||
|
@ -107,22 +109,26 @@ fn simple(i: &str) -> IResult<&str, (Tables, Option<Cond>, Option<Idiom>)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom(i: &str) -> IResult<&str, (Tables, Option<Cond>, Option<Idiom>)> {
|
fn custom(i: &str) -> IResult<&str, (Tables, Option<Cond>, Option<Idiom>)> {
|
||||||
let (i, _) = openparentheses(i)?;
|
expect_delimited(
|
||||||
let (i, w) = alt((any, tables))(i)?;
|
openparentheses,
|
||||||
let (i, c) = opt(|i| {
|
|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, w) = alt((any, tables))(i)?;
|
||||||
let (i, v) = cond(i)?;
|
let (i, c) = opt(|i| {
|
||||||
Ok((i, v))
|
let (i, _) = shouldbespace(i)?;
|
||||||
})(i)?;
|
let (i, v) = cond(i)?;
|
||||||
let (i, a) = opt(|i| {
|
Ok((i, v))
|
||||||
let (i, _) = shouldbespace(i)?;
|
})(i)?;
|
||||||
let (i, _) = tag_no_case("AS")(i)?;
|
let (i, a) = opt(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = idiom(i)?;
|
let (i, _) = tag_no_case("AS")(i)?;
|
||||||
Ok((i, v))
|
let (i, _) = shouldbespace(i)?;
|
||||||
})(i)?;
|
let (i, v) = idiom(i)?;
|
||||||
let (i, _) = closeparentheses(i)?;
|
Ok((i, v))
|
||||||
Ok((i, (w, c, a)))
|
})(i)?;
|
||||||
|
Ok((i, (w, c, a)))
|
||||||
|
},
|
||||||
|
closeparentheses,
|
||||||
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn one(i: &str) -> IResult<&str, Tables> {
|
fn one(i: &str) -> IResult<&str, Tables> {
|
||||||
|
|
|
@ -18,6 +18,8 @@ use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
const BRACKET_L: char = '⟨';
|
const BRACKET_L: char = '⟨';
|
||||||
const BRACKET_R: char = '⟩';
|
const BRACKET_R: char = '⟩';
|
||||||
const BRACKET_END_NUL: &str = "⟩\0";
|
const BRACKET_END_NUL: &str = "⟩\0";
|
||||||
|
@ -78,7 +80,7 @@ impl Display for Ident {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ident(i: &str) -> IResult<&str, Ident> {
|
pub fn ident(i: &str) -> IResult<&str, Ident> {
|
||||||
let (i, v) = ident_raw(i)?;
|
let (i, v) = expected("an identifier", ident_raw)(i)?;
|
||||||
Ok((i, Ident::from(v)))
|
Ok((i, Ident::from(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use super::dir::dir;
|
||||||
|
use super::error::{expected, ExplainResultExt};
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Idiom";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Idiom";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
|
@ -191,31 +194,37 @@ impl Display for Idiom {
|
||||||
|
|
||||||
/// Used in DEFINE FIELD and DEFINE INDEX clauses
|
/// Used in DEFINE FIELD and DEFINE INDEX clauses
|
||||||
pub fn local(i: &str) -> IResult<&str, Idiom> {
|
pub fn local(i: &str) -> IResult<&str, Idiom> {
|
||||||
let (i, p) = first(i)?;
|
expected("a local idiom", |i| {
|
||||||
let (i, mut v) = many0(local_part)(i)?;
|
let (i, p) = first(i).explain("graphs are not allowed in a local idioms.", dir)?;
|
||||||
// Flatten is only allowed at the end
|
let (i, mut v) = many0(local_part)(i)?;
|
||||||
let (i, flat) = opt(flatten)(i)?;
|
// Flatten is only allowed at the end
|
||||||
if let Some(p) = flat {
|
let (i, flat) = opt(flatten)(i)?;
|
||||||
v.push(p);
|
if let Some(p) = flat {
|
||||||
}
|
v.push(p);
|
||||||
v.insert(0, p);
|
}
|
||||||
Ok((i, Idiom::from(v)))
|
v.insert(0, p);
|
||||||
|
Ok((i, Idiom::from(v)))
|
||||||
|
})(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in a SPLIT, ORDER, and GROUP clauses
|
/// Used in a SPLIT, ORDER, and GROUP clauses
|
||||||
pub fn basic(i: &str) -> IResult<&str, Idiom> {
|
pub fn basic(i: &str) -> IResult<&str, Idiom> {
|
||||||
let (i, p) = first(i)?;
|
expected("a basic idiom", |i| {
|
||||||
let (i, mut v) = many0(basic_part)(i)?;
|
let (i, p) = first(i).explain("graphs are not allowed in a basic idioms.", dir)?;
|
||||||
v.insert(0, p);
|
let (i, mut v) = many0(basic_part)(i)?;
|
||||||
Ok((i, Idiom::from(v)))
|
v.insert(0, p);
|
||||||
|
Ok((i, Idiom::from(v)))
|
||||||
|
})(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple idiom with one or more parts
|
/// A simple idiom with one or more parts
|
||||||
pub fn plain(i: &str) -> IResult<&str, Idiom> {
|
pub fn plain(i: &str) -> IResult<&str, Idiom> {
|
||||||
let (i, p) = alt((first, graph))(i)?;
|
expected("a idiom", |i| {
|
||||||
let (i, mut v) = many0(part)(i)?;
|
let (i, p) = alt((first, graph))(i)?;
|
||||||
v.insert(0, p);
|
let (i, mut v) = many0(part)(i)?;
|
||||||
Ok((i, Idiom::from(v)))
|
v.insert(0, p);
|
||||||
|
Ok((i, Idiom::from(v)))
|
||||||
|
})(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reparse a value which might part of an idiom.
|
/// Reparse a value which might part of an idiom.
|
||||||
|
|
|
@ -15,7 +15,7 @@ use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use super::util::delimited_list1;
|
use super::util::{delimited_list1, expect_terminator};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
|
@ -140,9 +140,9 @@ fn option(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, _) = tag("option")(i)?;
|
let (i, _) = tag("option")(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
cut(|i| {
|
cut(|i| {
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, s) = tag("<")(i)?;
|
||||||
let (i, v) = map(alt((either, simple, geometry, record, array, set)), Box::new)(i)?;
|
let (i, v) = map(alt((either, simple, geometry, record, array, set)), Box::new)(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = expect_terminator(s, char('>'))(i)?;
|
||||||
Ok((i, Kind::Option(v)))
|
Ok((i, Kind::Option(v)))
|
||||||
})(i)
|
})(i)
|
||||||
}
|
}
|
||||||
|
@ -152,9 +152,9 @@ fn record(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, v) =
|
let (i, v) =
|
||||||
opt(alt((delimited_list1(openparentheses, commas, cut(table), closeparentheses), |i| {
|
opt(alt((delimited_list1(openparentheses, commas, cut(table), closeparentheses), |i| {
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, s) = tag("<")(i)?;
|
||||||
let (i, v) = separated_list1(verbar, table)(i)?;
|
let (i, v) = separated_list1(verbar, table)(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = expect_terminator(s, char('>'))(i)?;
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
})))(i)?;
|
})))(i)?;
|
||||||
Ok((i, Kind::Record(v.unwrap_or_default())))
|
Ok((i, Kind::Record(v.unwrap_or_default())))
|
||||||
|
@ -165,9 +165,9 @@ fn geometry(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, v) =
|
let (i, v) =
|
||||||
opt(alt((delimited_list1(openparentheses, commas, cut(geo), closeparentheses), |i| {
|
opt(alt((delimited_list1(openparentheses, commas, cut(geo), closeparentheses), |i| {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, s) = tag("<")(i)?;
|
||||||
let (i, v) = separated_list1(verbar, cut(geo))(i)?;
|
let (i, v) = separated_list1(verbar, cut(geo))(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = expect_terminator(s, char('>'))(i)?;
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
})))(i)?;
|
})))(i)?;
|
||||||
Ok((i, Kind::Geometry(v.unwrap_or_default())))
|
Ok((i, Kind::Geometry(v.unwrap_or_default())))
|
||||||
|
@ -176,7 +176,7 @@ fn geometry(i: &str) -> IResult<&str, Kind> {
|
||||||
fn array(i: &str) -> IResult<&str, Kind> {
|
fn array(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, _) = tag("array")(i)?;
|
let (i, _) = tag("array")(i)?;
|
||||||
let (i, v) = opt(|i| {
|
let (i, v) = opt(|i| {
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, s) = tag("<")(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, k) = kind(i)?;
|
let (i, k) = kind(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
|
@ -187,7 +187,7 @@ fn array(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, l))
|
Ok((i, l))
|
||||||
})(i)?;
|
})(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = expect_terminator(s, char('>'))(i)?;
|
||||||
Ok((i, (k, l)))
|
Ok((i, (k, l)))
|
||||||
})(i)?;
|
})(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
|
@ -202,7 +202,7 @@ fn array(i: &str) -> IResult<&str, Kind> {
|
||||||
fn set(i: &str) -> IResult<&str, Kind> {
|
fn set(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, _) = tag("set")(i)?;
|
let (i, _) = tag("set")(i)?;
|
||||||
let (i, v) = opt(|i| {
|
let (i, v) = opt(|i| {
|
||||||
let (i, _) = char('<')(i)?;
|
let (i, s) = tag("<")(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, k) = kind(i)?;
|
let (i, k) = kind(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
|
@ -213,7 +213,7 @@ fn set(i: &str) -> IResult<&str, Kind> {
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, l))
|
Ok((i, l))
|
||||||
})(i)?;
|
})(i)?;
|
||||||
let (i, _) = char('>')(i)?;
|
let (i, _) = expect_terminator(s, char('>'))(i)?;
|
||||||
Ok((i, (k, l)))
|
Ok((i, (k, l)))
|
||||||
})(i)?;
|
})(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
|
|
|
@ -4,6 +4,7 @@ pub(crate) mod algorithm;
|
||||||
pub(crate) mod array;
|
pub(crate) mod array;
|
||||||
pub(crate) mod base;
|
pub(crate) mod base;
|
||||||
pub(crate) mod block;
|
pub(crate) mod block;
|
||||||
|
pub(crate) mod builtin;
|
||||||
pub(crate) mod bytes;
|
pub(crate) mod bytes;
|
||||||
pub(crate) mod cast;
|
pub(crate) mod cast;
|
||||||
pub(crate) mod changefeed;
|
pub(crate) mod changefeed;
|
||||||
|
@ -93,7 +94,7 @@ pub use self::datetime::Datetime;
|
||||||
pub use self::dir::Dir;
|
pub use self::dir::Dir;
|
||||||
pub use self::duration::Duration;
|
pub use self::duration::Duration;
|
||||||
pub use self::edges::Edges;
|
pub use self::edges::Edges;
|
||||||
pub use self::error::Error;
|
pub use self::error::ParseError;
|
||||||
pub use self::explain::Explain;
|
pub use self::explain::Explain;
|
||||||
pub use self::expression::Expression;
|
pub use self::expression::Expression;
|
||||||
pub use self::fetch::Fetch;
|
pub use self::fetch::Fetch;
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
use super::value::{TryAdd, TryDiv, TryMul, TryNeg, TryPow, TrySub};
|
use super::value::{TryAdd, TryDiv, TryMul, TryNeg, TryPow, TrySub};
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::ending::number as ending;
|
use crate::sql::ending::number as ending;
|
||||||
use crate::sql::error::Error::Parser;
|
use crate::sql::error::{IResult, ParseError};
|
||||||
use crate::sql::error::IResult;
|
|
||||||
use crate::sql::strand::Strand;
|
use crate::sql::strand::Strand;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::i64;
|
use nom::character::complete::i64;
|
||||||
use nom::combinator::{opt, value};
|
use nom::combinator::{opt, value};
|
||||||
use nom::number::complete::recognize_float;
|
use nom::number::complete::recognize_float;
|
||||||
use nom::Err::Failure;
|
use nom::Err;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use rust_decimal::prelude::*;
|
use rust_decimal::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -747,9 +746,43 @@ fn not_nan(i: &str) -> IResult<&str, Number> {
|
||||||
let (i, suffix) = suffix(i)?;
|
let (i, suffix) = suffix(i)?;
|
||||||
let (i, _) = ending(i)?;
|
let (i, _) = ending(i)?;
|
||||||
let number = match suffix {
|
let number = match suffix {
|
||||||
Suffix::None => Number::try_from(v).map_err(|_| Failure(Parser(i)))?,
|
Suffix::None => {
|
||||||
Suffix::Float => Number::from(f64::from_str(v).map_err(|_| Failure(Parser(i)))?),
|
// Manually check for int or float for better parsing errors
|
||||||
Suffix::Decimal => Number::from(Decimal::from_str(v).map_err(|_| Failure(Parser(i)))?),
|
if v.contains(['e', 'E', '.']) {
|
||||||
|
let float = f64::from_str(v)
|
||||||
|
.map_err(|e| ParseError::ParseFloat {
|
||||||
|
tried: v,
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
.map_err(Err::Failure)?;
|
||||||
|
Number::from(float)
|
||||||
|
} else {
|
||||||
|
let int = i64::from_str(v)
|
||||||
|
.map_err(|e| ParseError::ParseInt {
|
||||||
|
tried: v,
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
.map_err(Err::Failure)?;
|
||||||
|
Number::from(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Suffix::Float => {
|
||||||
|
let float = f64::from_str(v)
|
||||||
|
.map_err(|e| ParseError::ParseFloat {
|
||||||
|
tried: v,
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
.map_err(Err::Failure)?;
|
||||||
|
Number::from(float)
|
||||||
|
}
|
||||||
|
Suffix::Decimal => Number::from(
|
||||||
|
Decimal::from_str(v)
|
||||||
|
.map_err(|e| ParseError::ParseDecimal {
|
||||||
|
tried: v,
|
||||||
|
error: e,
|
||||||
|
})
|
||||||
|
.map_err(Err::Failure)?,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
Ok((i, number))
|
Ok((i, number))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,22 @@ use crate::dbs::{Options, Transaction};
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::common::openbraces;
|
use crate::sql::common::{closebraces, openbraces};
|
||||||
use crate::sql::common::{commas, val_char};
|
use crate::sql::common::{commas, val_char};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::{expected, IResult};
|
||||||
use crate::sql::escape::escape_key;
|
use crate::sql::escape::escape_key;
|
||||||
use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty};
|
use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty};
|
||||||
use crate::sql::operation::Operation;
|
use crate::sql::operation::Operation;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::util::delimited_list0;
|
use crate::sql::util::expect_terminator;
|
||||||
use crate::sql::value::{value, Value};
|
use crate::sql::value::{value, Value};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::take_while1;
|
use nom::bytes::complete::take_while1;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::combinator::cut;
|
use nom::combinator::{cut, opt};
|
||||||
use nom::sequence::{delimited, terminated};
|
use nom::sequence::delimited;
|
||||||
|
use nom::Err;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -325,15 +326,38 @@ pub fn object(i: &str) -> IResult<&str, Object> {
|
||||||
fn entry(i: &str) -> IResult<&str, (String, Value)> {
|
fn entry(i: &str) -> IResult<&str, (String, Value)> {
|
||||||
let (i, k) = key(i)?;
|
let (i, k) = key(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = char(':')(i)?;
|
let (i, _) = expected("`:`", char(':'))(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, v) = cut(value)(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, (String::from(k), v)))
|
Ok((i, (String::from(k), v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
let (i, v) =
|
let start = i;
|
||||||
delimited_list0(openbraces, commas, terminated(entry, mightbespace), char('}'))(i)?;
|
let (i, _) = openbraces(i)?;
|
||||||
Ok((i, Object(v.into_iter().collect())))
|
let (i, first) = match entry(i) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(Err::Error(_)) => {
|
||||||
|
let (i, _) = closebraces(i)?;
|
||||||
|
return Ok((i, Object(BTreeMap::new())));
|
||||||
|
}
|
||||||
|
Err(Err::Failure(x)) => return Err(Err::Failure(x)),
|
||||||
|
Err(Err::Incomplete(x)) => return Err(Err::Incomplete(x)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tree = BTreeMap::new();
|
||||||
|
tree.insert(first.0, first.1);
|
||||||
|
|
||||||
|
let mut input = i;
|
||||||
|
while let (i, Some(_)) = opt(commas)(input)? {
|
||||||
|
if let (i, Some(_)) = opt(closebraces)(i)? {
|
||||||
|
return Ok((i, Object(tree)));
|
||||||
|
}
|
||||||
|
let (i, v) = cut(entry)(i)?;
|
||||||
|
tree.insert(v.0, v.1);
|
||||||
|
input = i
|
||||||
|
}
|
||||||
|
let (i, _) = expect_terminator(start, closebraces)(input)?;
|
||||||
|
Ok((i, Object(tree)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key(i: &str) -> IResult<&str, &str> {
|
pub fn key(i: &str) -> IResult<&str, &str> {
|
||||||
|
|
|
@ -2,11 +2,12 @@ use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::idiom::{locals as idioms, Idioms};
|
use crate::sql::idiom::{locals as idioms, Idioms};
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
|
|
||||||
pub fn omit(i: &str) -> IResult<&str, Idioms> {
|
pub fn omit(i: &str) -> IResult<&str, Idioms> {
|
||||||
let (i, _) = tag_no_case("OMIT")(i)?;
|
let (i, _) = tag_no_case("OMIT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = idioms(i)?;
|
let (i, v) = cut(idioms)(i)?;
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::iam::Error as IamError;
|
|
||||||
use crate::sql::error::Error::{ExcessiveDepth, Field, Group, Order, Parser, Role, Split};
|
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::idiom::Idiom;
|
use crate::sql::idiom::Idiom;
|
||||||
use crate::sql::query::{query, Query};
|
use crate::sql::query::{query, Query};
|
||||||
use crate::sql::subquery::Subquery;
|
use crate::sql::subquery::Subquery;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use nom::Err;
|
use nom::Finish;
|
||||||
use std::str;
|
use std::str;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
|
@ -65,82 +63,20 @@ fn parse_impl<O>(input: &str, parser: impl Fn(&str) -> IResult<&str, O>) -> Resu
|
||||||
// The input query was empty
|
// The input query was empty
|
||||||
0 => Err(Error::QueryEmpty),
|
0 => Err(Error::QueryEmpty),
|
||||||
// Continue parsing the query
|
// Continue parsing the query
|
||||||
_ => match parser(input) {
|
_ => match parser(input).finish() {
|
||||||
// The query was parsed successfully
|
// The query was parsed successfully
|
||||||
Ok((v, parsed)) if v.is_empty() => Ok(parsed),
|
Ok((v, parsed)) if v.is_empty() => Ok(parsed),
|
||||||
// There was unparsed SQL remaining
|
// There was unparsed SQL remaining
|
||||||
Ok((_, _)) => Err(Error::QueryRemaining),
|
Ok((_, _)) => Err(Error::QueryRemaining),
|
||||||
// There was an error when parsing the query
|
// There was an error when parsing the query
|
||||||
Err(Err::Error(e)) | Err(Err::Failure(e)) => Err(match e {
|
Err(e) => Err(Error::InvalidQuery(e.render_on(input))),
|
||||||
// There was a parsing error
|
|
||||||
Parser(e) => {
|
|
||||||
// Locate the parser position
|
|
||||||
let (s, l, c) = locate(input, e);
|
|
||||||
// Return the parser error
|
|
||||||
Error::InvalidQuery {
|
|
||||||
line: l,
|
|
||||||
char: c,
|
|
||||||
sql: s.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// There was a parsing error
|
|
||||||
ExcessiveDepth => Error::ComputationDepthExceeded,
|
|
||||||
// There was a SPLIT ON error
|
|
||||||
Field(e, f) => Error::InvalidField {
|
|
||||||
line: locate(input, e).1,
|
|
||||||
field: f,
|
|
||||||
},
|
|
||||||
// There was a SPLIT ON error
|
|
||||||
Split(e, f) => Error::InvalidSplit {
|
|
||||||
line: locate(input, e).1,
|
|
||||||
field: f,
|
|
||||||
},
|
|
||||||
// There was a ORDER BY error
|
|
||||||
Order(e, f) => Error::InvalidOrder {
|
|
||||||
line: locate(input, e).1,
|
|
||||||
field: f,
|
|
||||||
},
|
|
||||||
// There was a GROUP BY error
|
|
||||||
Group(e, f) => Error::InvalidGroup {
|
|
||||||
line: locate(input, e).1,
|
|
||||||
field: f,
|
|
||||||
},
|
|
||||||
// There was an error parsing the ROLE
|
|
||||||
Role(_, role) => Error::IamError(IamError::InvalidRole(role)),
|
|
||||||
}),
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn truncate(s: &str, l: usize) -> &str {
|
|
||||||
// TODO: use s.floor_char_boundary once https://github.com/rust-lang/rust/issues/93743 lands
|
|
||||||
match s.char_indices().nth(l) {
|
|
||||||
None => s,
|
|
||||||
Some((i, _)) => &s[..i],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn locate<'a>(input: &str, tried: &'a str) -> (&'a str, usize, usize) {
|
|
||||||
let index = input.len() - tried.len();
|
|
||||||
let tried = truncate(tried, 100);
|
|
||||||
let lines = input.split('\n').map(|l| l.len()).enumerate();
|
|
||||||
let (mut total, mut chars) = (0, 0);
|
|
||||||
for (line, size) in lines {
|
|
||||||
total += size + 1;
|
|
||||||
if index < total {
|
|
||||||
let line_num = line + 1;
|
|
||||||
let char_num = index - chars;
|
|
||||||
return (tried, line_num, char_num);
|
|
||||||
}
|
|
||||||
chars += size + 1;
|
|
||||||
}
|
|
||||||
(tried, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod depth {
|
pub(crate) mod depth {
|
||||||
use crate::cnf::MAX_COMPUTATION_DEPTH;
|
use crate::cnf::MAX_COMPUTATION_DEPTH;
|
||||||
use crate::sql::Error::ExcessiveDepth;
|
use crate::sql::ParseError;
|
||||||
use nom::Err;
|
use nom::Err;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::thread::panicking;
|
use std::thread::panicking;
|
||||||
|
@ -170,14 +106,14 @@ pub(crate) mod depth {
|
||||||
/// Call at least once in recursive parsing code paths to limit recursion depth.
|
/// Call at least once in recursive parsing code paths to limit recursion depth.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[must_use = "must store and implicitly drop when returning"]
|
#[must_use = "must store and implicitly drop when returning"]
|
||||||
pub(crate) fn dive() -> Result<Diving, Err<crate::sql::Error<&'static str>>> {
|
pub(crate) fn dive<I>(position: I) -> Result<Diving, Err<crate::sql::ParseError<I>>> {
|
||||||
DEPTH.with(|cell| {
|
DEPTH.with(|cell| {
|
||||||
let depth = cell.get().saturating_add(DEPTH_PER_DIVE);
|
let depth = cell.get().saturating_add(DEPTH_PER_DIVE);
|
||||||
if depth <= *MAX_COMPUTATION_DEPTH {
|
if depth <= *MAX_COMPUTATION_DEPTH {
|
||||||
cell.replace(depth);
|
cell.replace(depth);
|
||||||
Ok(Diving)
|
Ok(Diving)
|
||||||
} else {
|
} else {
|
||||||
Err(Err::Failure(ExcessiveDepth))
|
Err(Err::Failure(ParseError::ExcessiveDepth(position)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -422,6 +358,8 @@ mod tests {
|
||||||
n: usize,
|
n: usize,
|
||||||
excessive: bool,
|
excessive: bool,
|
||||||
) {
|
) {
|
||||||
|
use crate::sql::error::ParseError;
|
||||||
|
|
||||||
let mut sql = String::from(prefix);
|
let mut sql = String::from(prefix);
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
sql.push_str(recursive_start);
|
sql.push_str(recursive_start);
|
||||||
|
@ -431,11 +369,11 @@ mod tests {
|
||||||
sql.push_str(recursive_end);
|
sql.push_str(recursive_end);
|
||||||
}
|
}
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let res = parse(&sql);
|
let res = query(&sql).finish();
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
if excessive {
|
if excessive {
|
||||||
assert!(
|
assert!(
|
||||||
matches!(res, Err(Error::ComputationDepthExceeded)),
|
matches!(res, Err(ParseError::ExcessiveDepth(_))),
|
||||||
"expected computation depth exceeded, got {:?}",
|
"expected computation depth exceeded, got {:?}",
|
||||||
res
|
res
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::common::closebracket;
|
|
||||||
use crate::sql::ending::ident as ending;
|
use crate::sql::ending::ident as ending;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::Fmt;
|
use crate::sql::fmt::Fmt;
|
||||||
|
@ -13,7 +12,6 @@ use crate::sql::value::{self, Value};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::char;
|
|
||||||
use nom::combinator::{self, cut, map, not, peek};
|
use nom::combinator::{self, cut, map, not, peek};
|
||||||
use nom::sequence::{preceded, terminated};
|
use nom::sequence::{preceded, terminated};
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
|
@ -22,6 +20,9 @@ use std::fmt;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use super::comment::mightbespace;
|
use super::comment::mightbespace;
|
||||||
|
use super::common::{closebracket, openbracket};
|
||||||
|
use super::error::{expected, ExplainResultExt};
|
||||||
|
use super::util::expect_delimited;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
|
@ -149,7 +150,7 @@ pub fn part(i: &str) -> IResult<&str, Part> {
|
||||||
alt((
|
alt((
|
||||||
flatten,
|
flatten,
|
||||||
preceded(tag("."), cut(dot_part)),
|
preceded(tag("."), cut(dot_part)),
|
||||||
preceded(char('['), cut(bracketed_part)),
|
expect_delimited(openbracket, cut(bracketed_part), closebracket),
|
||||||
graph,
|
graph,
|
||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
@ -164,11 +165,30 @@ pub fn flatten(i: &str) -> IResult<&str, Part> {
|
||||||
|
|
||||||
pub fn local_part(i: &str) -> IResult<&str, Part> {
|
pub fn local_part(i: &str) -> IResult<&str, Part> {
|
||||||
// Cant cut dot part since it might be part of the flatten at the end.
|
// Cant cut dot part since it might be part of the flatten at the end.
|
||||||
alt((preceded(tag("."), dot_part), preceded(tag("["), cut(local_bracketed_part))))(i)
|
alt((
|
||||||
|
preceded(tag("."), dot_part),
|
||||||
|
expect_delimited(openbracket, cut(local_bracketed_part), closebracket),
|
||||||
|
// TODO explain
|
||||||
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn basic_part(i: &str) -> IResult<&str, Part> {
|
pub fn basic_part(i: &str) -> IResult<&str, Part> {
|
||||||
alt((preceded(tag("."), cut(dot_part)), preceded(tag("["), cut(basic_bracketed_part))))(i)
|
alt((
|
||||||
|
preceded(
|
||||||
|
tag("."),
|
||||||
|
cut(|i| dot_part(i).explain("flattening is not allowed with a basic idiom", tag(".."))),
|
||||||
|
),
|
||||||
|
|s| {
|
||||||
|
let (i, _) = openbracket(s)?;
|
||||||
|
let (i, v) = expected(
|
||||||
|
"$, * or a number",
|
||||||
|
cut(terminated(basic_bracketed_part, closebracket)),
|
||||||
|
)(i)
|
||||||
|
.explain("basic idioms don't allow computed values", bracketed_value)
|
||||||
|
.explain("basic idioms don't allow where selectors", bracketed_where)?;
|
||||||
|
Ok((i, v))
|
||||||
|
},
|
||||||
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dot_part(i: &str) -> IResult<&str, Part> {
|
fn dot_part(i: &str) -> IResult<&str, Part> {
|
||||||
|
@ -180,26 +200,22 @@ fn dot_part(i: &str) -> IResult<&str, Part> {
|
||||||
|
|
||||||
fn basic_bracketed_part(i: &str) -> IResult<&str, Part> {
|
fn basic_bracketed_part(i: &str) -> IResult<&str, Part> {
|
||||||
alt((
|
alt((
|
||||||
combinator::value(Part::All, terminated(tag("*"), cut(closebracket))),
|
combinator::value(Part::All, tag("*")),
|
||||||
// Can cut here since it can't be a parameter.
|
combinator::value(Part::Last, tag("$")),
|
||||||
combinator::value(Part::All, terminated(tag("$"), cut(closebracket))),
|
map(number, Part::Index),
|
||||||
map(terminated(number, cut(closebracket)), Part::Index),
|
|
||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn local_bracketed_part(i: &str) -> IResult<&str, Part> {
|
fn local_bracketed_part(i: &str) -> IResult<&str, Part> {
|
||||||
alt((
|
alt((combinator::value(Part::All, tag("*")), map(number, Part::Index)))(i)
|
||||||
combinator::value(Part::All, terminated(tag("*"), cut(closebracket))),
|
.explain("using `[$]` in a local idiom is not allowed", tag("$"))
|
||||||
map(terminated(number, cut(closebracket)), Part::Index),
|
|
||||||
))(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bracketed_part(i: &str) -> IResult<&str, Part> {
|
fn bracketed_part(i: &str) -> IResult<&str, Part> {
|
||||||
alt((
|
alt((
|
||||||
combinator::value(Part::All, terminated(tag("*"), cut(closebracket))),
|
combinator::value(Part::All, tag("*")),
|
||||||
// Don't cut here, the '$' could be part of a param.
|
combinator::value(Part::Last, terminated(tag("$"), peek(closebracket))),
|
||||||
combinator::value(Part::Last, terminated(tag("$"), closebracket)),
|
map(number, Part::Index),
|
||||||
map(terminated(number, cut(closebracket)), Part::Index),
|
|
||||||
bracketed_where,
|
bracketed_where,
|
||||||
bracketed_value,
|
bracketed_value,
|
||||||
))(i)
|
))(i)
|
||||||
|
@ -219,8 +235,6 @@ pub fn bracketed_where(i: &str) -> IResult<&str, Part> {
|
||||||
))(i)?;
|
))(i)?;
|
||||||
|
|
||||||
let (i, v) = value::value(i)?;
|
let (i, v) = value::value(i)?;
|
||||||
|
|
||||||
let (i, _) = cut(closebracket)(i)?;
|
|
||||||
Ok((i, Part::Where(v)))
|
Ok((i, Part::Where(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +244,6 @@ pub fn bracketed_value(i: &str) -> IResult<&str, Part> {
|
||||||
map(param::param, Value::Param),
|
map(param::param, Value::Param),
|
||||||
map(idiom::basic, Value::Idiom),
|
map(idiom::basic, Value::Idiom),
|
||||||
))(i)?;
|
))(i)?;
|
||||||
let (i, _) = cut(closebracket)(i)?;
|
|
||||||
Ok((i, Part::Value(v)))
|
Ok((i, Part::Value(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ use std::fmt::Write;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
pub struct Permissions {
|
pub struct Permissions {
|
||||||
|
@ -236,13 +238,16 @@ impl Display for Permission {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn permission(i: &str) -> IResult<&str, Permission> {
|
pub fn permission(i: &str) -> IResult<&str, Permission> {
|
||||||
alt((
|
expected(
|
||||||
combinator::value(Permission::None, tag_no_case("NONE")),
|
"a permission",
|
||||||
combinator::value(Permission::Full, tag_no_case("FULL")),
|
alt((
|
||||||
map(tuple((tag_no_case("WHERE"), shouldbespace, value)), |(_, _, v)| {
|
combinator::value(Permission::None, tag_no_case("NONE")),
|
||||||
Permission::Specific(v)
|
combinator::value(Permission::Full, tag_no_case("FULL")),
|
||||||
}),
|
map(tuple((tag_no_case("WHERE"), shouldbespace, value)), |(_, _, v)| {
|
||||||
))(i)
|
Permission::Specific(v)
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rule(i: &str) -> IResult<&str, Vec<(PermissionKind, Permission)>> {
|
fn rule(i: &str) -> IResult<&str, Vec<(PermissionKind, Permission)>> {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::{IResult, ParseError};
|
||||||
use crate::sql::fmt::Pretty;
|
use crate::sql::fmt::Pretty;
|
||||||
use crate::sql::statement::{statements, Statement, Statements};
|
use crate::sql::statement::{statements, Statement, Statements};
|
||||||
use crate::sql::Value;
|
use crate::sql::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::combinator::all_consuming;
|
use nom::Err;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
@ -46,7 +46,14 @@ impl Display for Query {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query(i: &str) -> IResult<&str, Query> {
|
pub fn query(i: &str) -> IResult<&str, Query> {
|
||||||
let (i, v) = all_consuming(statements)(i)?;
|
let (i, v) = statements(i)?;
|
||||||
|
if !i.is_empty() {
|
||||||
|
return Err(Err::Failure(ParseError::ExplainedExpected {
|
||||||
|
tried: i,
|
||||||
|
expected: "query to end",
|
||||||
|
explained: "perhaps missing a semicolon on the previous statement?",
|
||||||
|
}));
|
||||||
|
}
|
||||||
Ok((i, Query(v)))
|
Ok((i, Query(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ pub fn regex(i: &str) -> IResult<&str, Regex> {
|
||||||
let (i, _) = char('/')(i)?;
|
let (i, _) = char('/')(i)?;
|
||||||
let (i, v) = escaped(is_not("\\/"), '\\', anychar)(i)?;
|
let (i, v) = escaped(is_not("\\/"), '\\', anychar)(i)?;
|
||||||
let (i, _) = char('/')(i)?;
|
let (i, _) = char('/')(i)?;
|
||||||
let regex = v.parse().map_err(|_| nom::Err::Error(crate::sql::Error::Parser(v)))?;
|
let regex = v.parse().map_err(|_| nom::Err::Error(crate::sql::ParseError::Base(v)))?;
|
||||||
Ok((i, regex))
|
Ok((i, regex))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use nom::{error::ParseError, Err, Parser};
|
|
||||||
|
|
||||||
use super::error::IResult;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error<I> {
|
|
||||||
/// An error because a keyword
|
|
||||||
Keyword{
|
|
||||||
input: I,
|
|
||||||
kind: nom::error::ErrorKind,
|
|
||||||
},
|
|
||||||
Base {
|
|
||||||
kind: nom::error::ErrorKind,
|
|
||||||
input: I,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cut_keyword<I, K, P, O, O1>(
|
|
||||||
mut keyword: K,
|
|
||||||
mut parser: P,
|
|
||||||
) -> impl FnMut(I) -> IResult<I, O, Error<I>>
|
|
||||||
where
|
|
||||||
K: Parser<I, I, Error<I>>,
|
|
||||||
P: Parser<I, O, Error<I>>,
|
|
||||||
I: Clone,
|
|
||||||
{
|
|
||||||
move |input: I| {
|
|
||||||
let (input, keyword) = keyword.parse(input)?;
|
|
||||||
|
|
||||||
match parser.parse(input) {
|
|
||||||
Err(Err::Error(Error::Base {
|
|
||||||
kind,
|
|
||||||
input,
|
|
||||||
})) => Err(Err::Failure(Error::KeywordError {
|
|
||||||
keyword,
|
|
||||||
input,
|
|
||||||
kind,
|
|
||||||
})),
|
|
||||||
x => x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recover_keyword<P, W, I, O>(
|
|
||||||
mut parser: P,
|
|
||||||
mut with: W,
|
|
||||||
) -> impl FnMut(I) -> IResult<I, O, Error<I>>
|
|
||||||
where
|
|
||||||
I: Clone,
|
|
||||||
P: Parser<I, O, Error<I>>,
|
|
||||||
W: Parser<I, O, Error<I>>,
|
|
||||||
{
|
|
||||||
move |input: I| match parser.parse(input.clone()) {
|
|
||||||
Err(Err::Failure(Error::KeywordError {
|
|
||||||
keyword,
|
|
||||||
failure_input,
|
|
||||||
kind,
|
|
||||||
})) => {
|
|
||||||
match parser.parse()
|
|
||||||
}
|
|
||||||
x => x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: fmt::Display> fmt::Display for Error<I> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
writeln!(f, "todo")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> ParseError<I> for Error<I> {
|
|
||||||
fn from_error_kind(input: I, kind: nom::error::ErrorKind) -> Self {
|
|
||||||
Error::Base {
|
|
||||||
input,
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append(input: I, kind: nom::error::ErrorKind, other: Self) -> Self {
|
|
||||||
other
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,16 @@
|
||||||
use crate::sql::common::{closeparentheses, commas, openparentheses};
|
use crate::sql::common::{closeparentheses, commas, openparentheses};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::Error::Parser;
|
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::combinator::{cut, value};
|
use nom::combinator::{cut, map_res, value};
|
||||||
use nom::number::complete::recognize_float;
|
use nom::number::complete::recognize_float;
|
||||||
use nom::Err::Failure;
|
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use super::util::expect_delimited;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialOrd, Serialize, Deserialize)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
pub enum Scoring {
|
pub enum Scoring {
|
||||||
|
@ -83,22 +83,22 @@ pub fn scoring(i: &str) -> IResult<&str, Scoring> {
|
||||||
value(Scoring::Vs, tag_no_case("VS")),
|
value(Scoring::Vs, tag_no_case("VS")),
|
||||||
|i| {
|
|i| {
|
||||||
let (i, _) = tag_no_case("BM25")(i)?;
|
let (i, _) = tag_no_case("BM25")(i)?;
|
||||||
let (i, _) = openparentheses(i)?;
|
expect_delimited(
|
||||||
cut(|i| {
|
openparentheses,
|
||||||
let (i, k1): (&str, &str) = recognize_float(i)?;
|
|i| {
|
||||||
let k1 = k1.parse::<f32>().map_err(|_| Failure(Parser(i)))?;
|
let (i, k1) = cut(map_res(recognize_float, |x: &str| x.parse::<f32>()))(i)?;
|
||||||
let (i, _) = commas(i)?;
|
let (i, _) = cut(commas)(i)?;
|
||||||
let (i, b) = recognize_float(i)?;
|
let (i, b) = cut(map_res(recognize_float, |x: &str| x.parse::<f32>()))(i)?;
|
||||||
let b = b.parse::<f32>().map_err(|_| Failure(Parser(i)))?;
|
Ok((
|
||||||
let (i, _) = closeparentheses(i)?;
|
i,
|
||||||
Ok((
|
Scoring::Bm {
|
||||||
i,
|
k1,
|
||||||
Scoring::Bm {
|
b,
|
||||||
k1,
|
},
|
||||||
b,
|
))
|
||||||
},
|
},
|
||||||
))
|
closeparentheses,
|
||||||
})(i)
|
)(i)
|
||||||
},
|
},
|
||||||
value(Scoring::bm25(), tag_no_case("BM25")),
|
value(Scoring::bm25(), tag_no_case("BM25")),
|
||||||
))(i)
|
))(i)
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub fn script(i: &str) -> IResult<&str, Script> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn script_raw(i: &str) -> IResult<&str, &str> {
|
fn script_raw(i: &str) -> IResult<&str, &str> {
|
||||||
let _diving = crate::sql::parser::depth::dive()?;
|
let _diving = crate::sql::parser::depth::dive(i)?;
|
||||||
recognize(many0(alt((
|
recognize(many0(alt((
|
||||||
script_comment,
|
script_comment,
|
||||||
script_object,
|
script_object,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::sql::error::Error;
|
use crate::sql::error::ParseError;
|
||||||
use crate::sql::field::{Field, Fields};
|
use crate::sql::field::{Field, Fields};
|
||||||
use crate::sql::group::Groups;
|
use crate::sql::group::Groups;
|
||||||
use crate::sql::order::Orders;
|
use crate::sql::order::Orders;
|
||||||
|
@ -40,14 +40,14 @@ pub fn check_split_on_fields<'a>(
|
||||||
i: &'a str,
|
i: &'a str,
|
||||||
fields: &Fields,
|
fields: &Fields,
|
||||||
splits: &Option<Splits>,
|
splits: &Option<Splits>,
|
||||||
) -> Result<(), Err<Error<&'a str>>> {
|
) -> Result<(), Err<ParseError<&'a str>>> {
|
||||||
// Check to see if a SPLIT ON clause has been defined
|
// Check to see if a SPLIT ON clause has been defined
|
||||||
if let Some(splits) = splits {
|
if let Some(splits) = splits {
|
||||||
// Loop over each of the expressions in the SPLIT ON clause
|
// Loop over each of the expressions in the SPLIT ON clause
|
||||||
for split in splits.iter() {
|
for split in splits.iter() {
|
||||||
if !contains_idiom(fields, &split.0) {
|
if !contains_idiom(fields, &split.0) {
|
||||||
// If the expression isn't specified in the SELECT clause, then error
|
// If the expression isn't specified in the SELECT clause, then error
|
||||||
return Err(Failure(Error::Split(i, split.to_string())));
|
return Err(Failure(ParseError::Split(i, split.to_string())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,14 +59,14 @@ pub fn check_order_by_fields<'a>(
|
||||||
i: &'a str,
|
i: &'a str,
|
||||||
fields: &Fields,
|
fields: &Fields,
|
||||||
orders: &Option<Orders>,
|
orders: &Option<Orders>,
|
||||||
) -> Result<(), Err<Error<&'a str>>> {
|
) -> Result<(), Err<ParseError<&'a str>>> {
|
||||||
// Check to see if a ORDER BY clause has been defined
|
// Check to see if a ORDER BY clause has been defined
|
||||||
if let Some(orders) = orders {
|
if let Some(orders) = orders {
|
||||||
// Loop over each of the expressions in the ORDER BY clause
|
// Loop over each of the expressions in the ORDER BY clause
|
||||||
for order in orders.iter() {
|
for order in orders.iter() {
|
||||||
if !contains_idiom(fields, order) {
|
if !contains_idiom(fields, order) {
|
||||||
// If the expression isn't specified in the SELECT clause, then error
|
// If the expression isn't specified in the SELECT clause, then error
|
||||||
return Err(Failure(Error::Order(i, order.to_string())));
|
return Err(Failure(ParseError::Order(i, order.to_string())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,14 +78,14 @@ pub fn check_group_by_fields<'a>(
|
||||||
i: &'a str,
|
i: &'a str,
|
||||||
fields: &Fields,
|
fields: &Fields,
|
||||||
groups: &Option<Groups>,
|
groups: &Option<Groups>,
|
||||||
) -> Result<(), Err<Error<&'a str>>> {
|
) -> Result<(), Err<ParseError<&'a str>>> {
|
||||||
// Check to see if a GROUP BY clause has been defined
|
// Check to see if a GROUP BY clause has been defined
|
||||||
if let Some(groups) = groups {
|
if let Some(groups) = groups {
|
||||||
// Loop over each of the expressions in the GROUP BY clause
|
// Loop over each of the expressions in the GROUP BY clause
|
||||||
for group in groups.iter() {
|
for group in groups.iter() {
|
||||||
if !contains_idiom(fields, &group.0) {
|
if !contains_idiom(fields, &group.0) {
|
||||||
// If the expression isn't specified in the SELECT clause, then error
|
// If the expression isn't specified in the SELECT clause, then error
|
||||||
return Err(Failure(Error::Group(i, group.to_string())));
|
return Err(Failure(ParseError::Group(i, group.to_string())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if this is a GROUP ALL clause or a GROUP BY clause
|
// Check if this is a GROUP ALL clause or a GROUP BY clause
|
||||||
|
@ -120,7 +120,7 @@ pub fn check_group_by_fields<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the expression isn't an aggregate function and isn't specified in the GROUP BY clause, then error
|
// If the expression isn't an aggregate function and isn't specified in the GROUP BY clause, then error
|
||||||
return Err(Failure(Error::Field(i, field.to_string())));
|
return Err(Failure(ParseError::Field(i, field.to_string())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,11 +57,9 @@ impl Display for Split {
|
||||||
pub fn split(i: &str) -> IResult<&str, Splits> {
|
pub fn split(i: &str) -> IResult<&str, Splits> {
|
||||||
let (i, _) = tag_no_case("SPLIT")(i)?;
|
let (i, _) = tag_no_case("SPLIT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
cut(|i| {
|
let (i, _) = opt(terminated(tag_no_case("ON"), shouldbespace))(i)?;
|
||||||
let (i, _) = opt(terminated(tag_no_case("ON"), shouldbespace))(i)?;
|
let (i, v) = cut(separated_list1(commas, split_raw))(i)?;
|
||||||
let (i, v) = separated_list1(commas, split_raw)(i)?;
|
Ok((i, Splits(v)))
|
||||||
Ok((i, Splits(v)))
|
|
||||||
})(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_raw(i: &str) -> IResult<&str, Split> {
|
fn split_raw(i: &str) -> IResult<&str, Split> {
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl CreateStatement {
|
||||||
// This is a single record result
|
// This is a single record result
|
||||||
Value::Array(mut a) if self.only => match a.len() {
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
// There was exactly one result
|
// There was exactly one result
|
||||||
v if v == 1 => Ok(a.remove(0)),
|
1 => Ok(a.remove(0)),
|
||||||
// There were no results
|
// There were no results
|
||||||
_ => Err(Error::SingleOnlyOutput),
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::filter::{filters, Filter};
|
use crate::sql::filter::{filters, Filter};
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
|
@ -16,6 +18,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -75,12 +78,11 @@ impl Display for DefineAnalyzerStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> {
|
pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("ANALYZER")(i)?;
|
let (i, _) = tag_no_case("ANALYZER")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, opts) = many0(analyzer_opts)(i)?;
|
let (i, opts) = many0(analyzer_opts)(i)?;
|
||||||
|
let (i, _) = expected("one of FILTERS, TOKENIZERS, or COMMENT", ending::query)(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineAnalyzerStatement {
|
let mut res = DefineAnalyzerStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -118,7 +120,7 @@ fn analyzer_comment(i: &str) -> IResult<&str, DefineAnalyzerOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineAnalyzerOption::Comment(v)))
|
Ok((i, DefineAnalyzerOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +128,7 @@ fn analyzer_filters(i: &str) -> IResult<&str, DefineAnalyzerOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("FILTERS")(i)?;
|
let (i, _) = tag_no_case("FILTERS")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = filters(i)?;
|
let (i, v) = cut(filters)(i)?;
|
||||||
Ok((i, DefineAnalyzerOption::Filters(v)))
|
Ok((i, DefineAnalyzerOption::Filters(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +136,6 @@ fn analyzer_tokenizers(i: &str) -> IResult<&str, DefineAnalyzerOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("TOKENIZERS")(i)?;
|
let (i, _) = tag_no_case("TOKENIZERS")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = tokenizers(i)?;
|
let (i, v) = cut(tokenizers)(i)?;
|
||||||
Ok((i, DefineAnalyzerOption::Tokenizers(v)))
|
Ok((i, DefineAnalyzerOption::Tokenizers(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::changefeed::{changefeed, ChangeFeed};
|
use crate::sql::changefeed::{changefeed, ChangeFeed};
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::strand::{strand, Strand};
|
use crate::sql::strand::{strand, Strand};
|
||||||
|
@ -15,6 +17,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -76,12 +79,12 @@ impl Display for DefineDatabaseStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
|
pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
|
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, opts) = many0(database_opts)(i)?;
|
let (i, opts) = many0(database_opts)(i)?;
|
||||||
|
let (i, _) = expected("COMMENT or CHANGEFEED", ending::query)(i)?;
|
||||||
|
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineDatabaseStatement {
|
let mut res = DefineDatabaseStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -115,7 +118,7 @@ fn database_comment(i: &str) -> IResult<&str, DefineDatabaseOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineDatabaseOption::Comment(v)))
|
Ok((i, DefineDatabaseOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,10 +135,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn define_database_with_changefeed() {
|
fn define_database_with_changefeed() {
|
||||||
let sql = "DEFINE DATABASE mydatabase CHANGEFEED 1h";
|
let sql = "DATABASE mydatabase CHANGEFEED 1h";
|
||||||
let res = database(sql);
|
let res = database(sql);
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(sql, format!("{}", out));
|
assert_eq!(format!("DEFINE {sql}"), format!("{}", out));
|
||||||
|
|
||||||
let serialized: Vec<u8> = (&out).try_into().unwrap();
|
let serialized: Vec<u8> = (&out).try_into().unwrap();
|
||||||
let deserialized = DefineDatabaseStatement::try_from(&serialized).unwrap();
|
let deserialized = DefineDatabaseStatement::try_from(&serialized).unwrap();
|
||||||
|
|
|
@ -7,6 +7,9 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::strand::{strand, Strand};
|
use crate::sql::strand::{strand, Strand};
|
||||||
|
@ -14,6 +17,7 @@ use crate::sql::value::{value, values, Value, Values};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
@ -75,17 +79,19 @@ impl Display for DefineEventStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn event(i: &str) -> IResult<&str, DefineEventStatement> {
|
pub fn event(i: &str) -> IResult<&str, DefineEventStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("EVENT")(i)?;
|
let (i, _) = tag_no_case("EVENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, (name, what, opts)) = cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, name) = ident(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||||
let (i, what) = ident(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, opts) = many0(event_opts)(i)?;
|
let (i, what) = ident(i)?;
|
||||||
|
let (i, opts) = many0(event_opts)(i)?;
|
||||||
|
let (i, _) = expected("WHEN, THEN, or COMMENT", ending::query)(i)?;
|
||||||
|
Ok((i, (name, what, opts)))
|
||||||
|
})(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineEventStatement {
|
let mut res = DefineEventStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -129,7 +135,7 @@ fn event_when(i: &str) -> IResult<&str, DefineEventOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("WHEN")(i)?;
|
let (i, _) = tag_no_case("WHEN")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineEventOption::When(v)))
|
Ok((i, DefineEventOption::When(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +143,7 @@ fn event_then(i: &str) -> IResult<&str, DefineEventOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("THEN")(i)?;
|
let (i, _) = tag_no_case("THEN")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = values(i)?;
|
let (i, v) = cut(values)(i)?;
|
||||||
Ok((i, DefineEventOption::Then(v)))
|
Ok((i, DefineEventOption::Then(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +151,6 @@ fn event_comment(i: &str) -> IResult<&str, DefineEventOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineEventOption::Comment(v)))
|
Ok((i, DefineEventOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::is_pretty;
|
use crate::sql::fmt::is_pretty;
|
||||||
use crate::sql::fmt::pretty_indent;
|
use crate::sql::fmt::pretty_indent;
|
||||||
|
@ -20,6 +23,7 @@ use crate::sql::value::{value, Value};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
@ -103,17 +107,22 @@ impl Display for DefineFieldStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
|
pub fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("FIELD")(i)?;
|
let (i, _) = tag_no_case("FIELD")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = idiom::local(i)?;
|
let (i, (name, what, opts)) = cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, name) = idiom::local(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||||
let (i, what) = ident(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, opts) = many0(field_opts)(i)?;
|
let (i, what) = ident(i)?;
|
||||||
|
let (i, opts) = many0(field_opts)(i)?;
|
||||||
|
let (i, _) = expected(
|
||||||
|
"one of FLEX(IBLE), TYPE, VALUE, ASSERT, DEFAULT, or COMMENT",
|
||||||
|
cut(ending::query),
|
||||||
|
)(i)?;
|
||||||
|
Ok((i, (name, what, opts)))
|
||||||
|
})(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineFieldStatement {
|
let mut res = DefineFieldStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -182,7 +191,7 @@ fn field_kind(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("TYPE")(i)?;
|
let (i, _) = tag_no_case("TYPE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = kind(i)?;
|
let (i, v) = cut(kind)(i)?;
|
||||||
Ok((i, DefineFieldOption::Kind(v)))
|
Ok((i, DefineFieldOption::Kind(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +199,7 @@ fn field_value(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("VALUE")(i)?;
|
let (i, _) = tag_no_case("VALUE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineFieldOption::Value(v)))
|
Ok((i, DefineFieldOption::Value(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +207,7 @@ fn field_assert(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ASSERT")(i)?;
|
let (i, _) = tag_no_case("ASSERT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineFieldOption::Assert(v)))
|
Ok((i, DefineFieldOption::Assert(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +215,7 @@ fn field_default(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("DEFAULT")(i)?;
|
let (i, _) = tag_no_case("DEFAULT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineFieldOption::Default(v)))
|
Ok((i, DefineFieldOption::Default(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,11 @@ use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::block::{block, Block};
|
use crate::sql::block::{block, Block};
|
||||||
use crate::sql::comment::{mightbespace, shouldbespace};
|
use crate::sql::comment::{mightbespace, shouldbespace};
|
||||||
|
use crate::sql::common::closeparentheses;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
|
use crate::sql::common::openparentheses;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::is_pretty;
|
use crate::sql::fmt::is_pretty;
|
||||||
use crate::sql::fmt::pretty_indent;
|
use crate::sql::fmt::pretty_indent;
|
||||||
|
@ -17,14 +21,15 @@ use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::kind::{kind, Kind};
|
use crate::sql::kind::{kind, Kind};
|
||||||
use crate::sql::permission::{permission, Permission};
|
use crate::sql::permission::{permission, Permission};
|
||||||
use crate::sql::strand::{strand, Strand};
|
use crate::sql::strand::{strand, Strand};
|
||||||
|
use crate::sql::util::delimited_list0;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::multi::separated_list0;
|
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Write};
|
use std::fmt::{self, Display, Write};
|
||||||
|
@ -92,29 +97,29 @@ impl fmt::Display for DefineFunctionStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(i: &str) -> IResult<&str, DefineFunctionStatement> {
|
pub fn function(i: &str) -> IResult<&str, DefineFunctionStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("FUNCTION")(i)?;
|
let (i, _) = tag_no_case("FUNCTION")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag("fn::")(i)?;
|
let (i, _) = tag("fn::")(i)?;
|
||||||
let (i, name) = ident::multi(i)?;
|
let (i, name) = ident::multi(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = char('(')(i)?;
|
let (i, args) = delimited_list0(
|
||||||
let (i, _) = mightbespace(i)?;
|
openparentheses,
|
||||||
let (i, args) = separated_list0(commas, |i| {
|
commas,
|
||||||
let (i, _) = char('$')(i)?;
|
|i| {
|
||||||
let (i, name) = ident(i)?;
|
let (i, _) = char('$')(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, name) = ident(i)?;
|
||||||
let (i, _) = char(':')(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = char(':')(i)?;
|
||||||
let (i, kind) = kind(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, (name, kind)))
|
let (i, kind) = kind(i)?;
|
||||||
})(i)?;
|
Ok((i, (name, kind)))
|
||||||
let (i, _) = mightbespace(i)?;
|
},
|
||||||
let (i, _) = char(')')(i)?;
|
closeparentheses,
|
||||||
|
)(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, block) = block(i)?;
|
let (i, block) = block(i)?;
|
||||||
let (i, opts) = many0(function_opts)(i)?;
|
let (i, opts) = many0(function_opts)(i)?;
|
||||||
|
let (i, _) = expected("PERMISSIONS or COMMENT", ending::query)(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineFunctionStatement {
|
let mut res = DefineFunctionStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -150,7 +155,7 @@ fn function_comment(i: &str) -> IResult<&str, DefineFunctionOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineFunctionOption::Comment(v)))
|
Ok((i, DefineFunctionOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +163,6 @@ fn function_permissions(i: &str) -> IResult<&str, DefineFunctionOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("PERMISSIONS")(i)?;
|
let (i, _) = tag_no_case("PERMISSIONS")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = permission(i)?;
|
let (i, v) = cut(permission)(i)?;
|
||||||
Ok((i, DefineFunctionOption::Permissions(v)))
|
Ok((i, DefineFunctionOption::Permissions(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::idiom;
|
use crate::sql::idiom;
|
||||||
|
@ -19,6 +21,7 @@ use crate::sql::value::{Value, Values};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
@ -98,17 +101,19 @@ impl Display for DefineIndexStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
|
pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("INDEX")(i)?;
|
let (i, _) = tag_no_case("INDEX")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, (name, what, opts)) = cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, name) = ident(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||||
let (i, what) = ident(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, opts) = many0(index_opts)(i)?;
|
let (i, what) = ident(i)?;
|
||||||
|
let (i, opts) = many0(index_opts)(i)?;
|
||||||
|
let (i, _) = ending::query(i)?;
|
||||||
|
Ok((i, (name, what, opts)))
|
||||||
|
})(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineIndexStatement {
|
let mut res = DefineIndexStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -183,7 +188,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_create_non_unique_index() {
|
fn check_create_non_unique_index() {
|
||||||
let sql = "DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col";
|
let sql = "INDEX my_index ON TABLE my_table COLUMNS my_col";
|
||||||
let (_, idx) = index(sql).unwrap();
|
let (_, idx) = index(sql).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
idx,
|
idx,
|
||||||
|
@ -200,7 +205,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_create_unique_index() {
|
fn check_create_unique_index() {
|
||||||
let sql = "DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col UNIQUE";
|
let sql = "INDEX my_index ON TABLE my_table COLUMNS my_col UNIQUE";
|
||||||
let (_, idx) = index(sql).unwrap();
|
let (_, idx) = index(sql).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
idx,
|
idx,
|
||||||
|
@ -217,7 +222,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_create_search_index_with_highlights() {
|
fn check_create_search_index_with_highlights() {
|
||||||
let sql = "DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col SEARCH ANALYZER my_analyzer BM25(1.2,0.75) DOC_IDS_ORDER 1000 DOC_LENGTHS_ORDER 1000 POSTINGS_ORDER 1000 TERMS_ORDER 1000 HIGHLIGHTS";
|
let sql = "INDEX my_index ON TABLE my_table COLUMNS my_col SEARCH ANALYZER my_analyzer BM25(1.2,0.75) DOC_IDS_ORDER 1000 DOC_LENGTHS_ORDER 1000 POSTINGS_ORDER 1000 TERMS_ORDER 1000 HIGHLIGHTS";
|
||||||
let (_, idx) = index(sql).unwrap();
|
let (_, idx) = index(sql).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
idx,
|
idx,
|
||||||
|
@ -245,8 +250,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_create_search_index() {
|
fn check_create_search_index() {
|
||||||
let sql =
|
let sql = "INDEX my_index ON TABLE my_table COLUMNS my_col SEARCH ANALYZER my_analyzer VS";
|
||||||
"DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col SEARCH ANALYZER my_analyzer VS";
|
|
||||||
let (_, idx) = index(sql).unwrap();
|
let (_, idx) = index(sql).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
idx,
|
idx,
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub use field::{field, DefineFieldStatement};
|
||||||
pub use function::{function, DefineFunctionStatement};
|
pub use function::{function, DefineFunctionStatement};
|
||||||
pub use index::{index, DefineIndexStatement};
|
pub use index::{index, DefineIndexStatement};
|
||||||
pub use namespace::{namespace, DefineNamespaceStatement};
|
pub use namespace::{namespace, DefineNamespaceStatement};
|
||||||
|
use nom::bytes::complete::tag_no_case;
|
||||||
pub use param::{param, DefineParamStatement};
|
pub use param::{param, DefineParamStatement};
|
||||||
pub use scope::{scope, DefineScopeStatement};
|
pub use scope::{scope, DefineScopeStatement};
|
||||||
pub use table::{table, DefineTableStatement};
|
pub use table::{table, DefineTableStatement};
|
||||||
|
@ -29,6 +30,7 @@ use crate::dbs::Options;
|
||||||
use crate::dbs::Transaction;
|
use crate::dbs::Transaction;
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
|
@ -105,6 +107,8 @@ impl Display for DefineStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define(i: &str) -> IResult<&str, DefineStatement> {
|
pub fn define(i: &str) -> IResult<&str, DefineStatement> {
|
||||||
|
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||||
|
let (i, _) = shouldbespace(i)?;
|
||||||
alt((
|
alt((
|
||||||
map(namespace, DefineStatement::Namespace),
|
map(namespace, DefineStatement::Namespace),
|
||||||
map(database, DefineStatement::Database),
|
map(database, DefineStatement::Database),
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::strand::{strand, Strand};
|
use crate::sql::strand::{strand, Strand};
|
||||||
|
@ -14,6 +16,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -68,12 +71,11 @@ impl Display for DefineNamespaceStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
|
pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
|
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, opts) = many0(namespace_opts)(i)?;
|
let (i, opts) = many0(namespace_opts)(i)?;
|
||||||
|
let (i, _) = expected("COMMENT", ending::query)(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineNamespaceStatement {
|
let mut res = DefineNamespaceStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -103,6 +105,6 @@ fn namespace_comment(i: &str) -> IResult<&str, DefineNamespaceOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineNamespaceOption::Comment(v)))
|
Ok((i, DefineNamespaceOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::is_pretty;
|
use crate::sql::fmt::is_pretty;
|
||||||
use crate::sql::fmt::pretty_indent;
|
use crate::sql::fmt::pretty_indent;
|
||||||
|
@ -18,6 +20,7 @@ use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -77,13 +80,12 @@ impl Display for DefineParamStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn param(i: &str) -> IResult<&str, DefineParamStatement> {
|
pub fn param(i: &str) -> IResult<&str, DefineParamStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("PARAM")(i)?;
|
let (i, _) = tag_no_case("PARAM")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = char('$')(i)?;
|
let (i, _) = cut(char('$'))(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, opts) = many0(param_opts)(i)?;
|
let (i, opts) = many0(param_opts)(i)?;
|
||||||
|
let (i, _) = expected("VALUE, PERMISSIONS, or COMMENT", ending::query)(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineParamStatement {
|
let mut res = DefineParamStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -125,7 +127,7 @@ fn param_value(i: &str) -> IResult<&str, DefineParamOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("VALUE")(i)?;
|
let (i, _) = tag_no_case("VALUE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineParamOption::Value(v)))
|
Ok((i, DefineParamOption::Value(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ fn param_comment(i: &str) -> IResult<&str, DefineParamOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineParamOption::Comment(v)))
|
Ok((i, DefineParamOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +143,6 @@ fn param_permissions(i: &str) -> IResult<&str, DefineParamOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("PERMISSIONS")(i)?;
|
let (i, _) = tag_no_case("PERMISSIONS")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = permission(i)?;
|
let (i, v) = cut(permission)(i)?;
|
||||||
Ok((i, DefineParamOption::Permissions(v)))
|
Ok((i, DefineParamOption::Permissions(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::duration::{duration, Duration};
|
use crate::sql::duration::{duration, Duration};
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::strand::{strand, Strand};
|
use crate::sql::strand::{strand, Strand};
|
||||||
|
@ -15,6 +17,7 @@ use crate::sql::value::{value, Value};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -78,12 +81,11 @@ impl Display for DefineScopeStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
|
pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("SCOPE")(i)?;
|
let (i, _) = tag_no_case("SCOPE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, opts) = many0(scope_opts)(i)?;
|
let (i, opts) = many0(scope_opts)(i)?;
|
||||||
|
let (i, _) = expected("SESSION, SIGNUP, SIGNIN, or COMMENT", ending::query)(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineScopeStatement {
|
let mut res = DefineScopeStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -130,7 +132,7 @@ fn scope_session(i: &str) -> IResult<&str, DefineScopeOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("SESSION")(i)?;
|
let (i, _) = tag_no_case("SESSION")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = duration(i)?;
|
let (i, v) = cut(duration)(i)?;
|
||||||
Ok((i, DefineScopeOption::Session(v)))
|
Ok((i, DefineScopeOption::Session(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +140,7 @@ fn scope_signup(i: &str) -> IResult<&str, DefineScopeOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("SIGNUP")(i)?;
|
let (i, _) = tag_no_case("SIGNUP")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineScopeOption::Signup(v)))
|
Ok((i, DefineScopeOption::Signup(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ fn scope_signin(i: &str) -> IResult<&str, DefineScopeOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("SIGNIN")(i)?;
|
let (i, _) = tag_no_case("SIGNIN")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = value(i)?;
|
let (i, v) = cut(value)(i)?;
|
||||||
Ok((i, DefineScopeOption::Signin(v)))
|
Ok((i, DefineScopeOption::Signin(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +156,6 @@ fn scope_comment(i: &str) -> IResult<&str, DefineScopeOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineScopeOption::Comment(v)))
|
Ok((i, DefineScopeOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::changefeed::{changefeed, ChangeFeed};
|
use crate::sql::changefeed::{changefeed, ChangeFeed};
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::is_pretty;
|
use crate::sql::fmt::is_pretty;
|
||||||
use crate::sql::fmt::pretty_indent;
|
use crate::sql::fmt::pretty_indent;
|
||||||
|
@ -20,6 +22,7 @@ use crate::sql::view::{view, View};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -136,12 +139,14 @@ impl Display for DefineTableStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table(i: &str) -> IResult<&str, DefineTableStatement> {
|
pub fn table(i: &str) -> IResult<&str, DefineTableStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("TABLE")(i)?;
|
let (i, _) = tag_no_case("TABLE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, opts) = many0(table_opts)(i)?;
|
let (i, opts) = many0(table_opts)(i)?;
|
||||||
|
let (i, _) = expected(
|
||||||
|
"DROP, SCHEMALESS, SCHEMAFUL(L), VIEW, CHANGEFEED, PERMISSIONS, or COMMENT",
|
||||||
|
ending::query,
|
||||||
|
)(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineTableStatement {
|
let mut res = DefineTableStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -250,10 +255,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn define_table_with_changefeed() {
|
fn define_table_with_changefeed() {
|
||||||
let sql = "DEFINE TABLE mytable SCHEMALESS CHANGEFEED 1h";
|
let sql = "TABLE mytable SCHEMALESS CHANGEFEED 1h";
|
||||||
let res = table(sql);
|
let res = table(sql);
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(sql, format!("{}", out));
|
assert_eq!(format!("DEFINE {sql}"), format!("{}", out));
|
||||||
|
|
||||||
let serialized: Vec<u8> = (&out).try_into().unwrap();
|
let serialized: Vec<u8> = (&out).try_into().unwrap();
|
||||||
let deserialized = DefineTableStatement::try_from(&serialized).unwrap();
|
let deserialized = DefineTableStatement::try_from(&serialized).unwrap();
|
||||||
|
|
|
@ -8,6 +8,9 @@ use crate::iam::ResourceKind;
|
||||||
use crate::sql::algorithm::{algorithm, Algorithm};
|
use crate::sql::algorithm::{algorithm, Algorithm};
|
||||||
use crate::sql::base::{base_or_scope, Base};
|
use crate::sql::base::{base_or_scope, Base};
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::escape::quote_str;
|
use crate::sql::escape::quote_str;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
|
@ -16,6 +19,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -106,16 +110,18 @@ impl Display for DefineTokenStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
|
pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("TOKEN")(i)?;
|
let (i, _) = tag_no_case("TOKEN")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, (name, base, opts)) = cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, name) = ident(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, base) = base_or_scope(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, opts) = many0(token_opts)(i)?;
|
let (i, base) = base_or_scope(i)?;
|
||||||
|
let (i, opts) = many0(token_opts)(i)?;
|
||||||
|
let (i, _) = expected("TYPE, VALUE, or COMMENT", ending::query)(i)?;
|
||||||
|
Ok((i, (name, base, opts)))
|
||||||
|
})(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineTokenStatement {
|
let mut res = DefineTokenStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -158,7 +164,7 @@ fn token_type(i: &str) -> IResult<&str, DefineTokenOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("TYPE")(i)?;
|
let (i, _) = tag_no_case("TYPE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = algorithm(i)?;
|
let (i, v) = cut(algorithm)(i)?;
|
||||||
Ok((i, DefineTokenOption::Type(v)))
|
Ok((i, DefineTokenOption::Type(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +172,7 @@ fn token_value(i: &str) -> IResult<&str, DefineTokenOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("VALUE")(i)?;
|
let (i, _) = tag_no_case("VALUE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand_raw(i)?;
|
let (i, v) = cut(strand_raw)(i)?;
|
||||||
Ok((i, DefineTokenOption::Value(v)))
|
Ok((i, DefineTokenOption::Value(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +180,6 @@ fn token_comment(i: &str) -> IResult<&str, DefineTokenOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineTokenOption::Comment(v)))
|
Ok((i, DefineTokenOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,11 @@ use crate::iam::Role;
|
||||||
use crate::sql::base::{base, Base};
|
use crate::sql::base::{base, Base};
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
use crate::sql::error::Error as SqlError;
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
|
use crate::sql::error::ParseError as SqlError;
|
||||||
use crate::sql::escape::quote_str;
|
use crate::sql::escape::quote_str;
|
||||||
use crate::sql::fmt::Fmt;
|
use crate::sql::fmt::Fmt;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
|
@ -21,8 +24,9 @@ use argon2::Argon2;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::multi::separated_list0;
|
use nom::multi::separated_list1;
|
||||||
use nom::Err::Failure;
|
use nom::Err::Failure;
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
@ -137,16 +141,18 @@ impl Display for DefineUserStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
|
pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
|
||||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("USER")(i)?;
|
let (i, _) = tag_no_case("USER")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, (name, base, opts)) = cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, name) = ident(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, base) = base(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, opts) = user_opts(i)?;
|
let (i, base) = base(i)?;
|
||||||
|
let (i, opts) = user_opts(i)?;
|
||||||
|
let (i, _) = expected("PASSWORD, PASSHASH, ROLES, or COMMENT", ending::query)(i)?;
|
||||||
|
Ok((i, (name, base, opts)))
|
||||||
|
})(i)?;
|
||||||
// Create the base statement
|
// Create the base statement
|
||||||
let mut res = DefineUserStatement {
|
let mut res = DefineUserStatement {
|
||||||
name,
|
name,
|
||||||
|
@ -191,14 +197,14 @@ enum DefineUserOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_opts(i: &str) -> IResult<&str, Vec<DefineUserOption>> {
|
fn user_opts(i: &str) -> IResult<&str, Vec<DefineUserOption>> {
|
||||||
many0(alt((alt((user_pass, user_hash)), user_roles, user_comment)))(i)
|
many0(alt((user_pass, user_hash, user_roles, user_comment)))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_pass(i: &str) -> IResult<&str, DefineUserOption> {
|
fn user_pass(i: &str) -> IResult<&str, DefineUserOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("PASSWORD")(i)?;
|
let (i, _) = tag_no_case("PASSWORD")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand_raw(i)?;
|
let (i, v) = cut(strand_raw)(i)?;
|
||||||
Ok((i, DefineUserOption::Password(v)))
|
Ok((i, DefineUserOption::Password(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +212,7 @@ fn user_hash(i: &str) -> IResult<&str, DefineUserOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("PASSHASH")(i)?;
|
let (i, _) = tag_no_case("PASSHASH")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand_raw(i)?;
|
let (i, v) = cut(strand_raw)(i)?;
|
||||||
Ok((i, DefineUserOption::Passhash(v)))
|
Ok((i, DefineUserOption::Passhash(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +220,7 @@ fn user_comment(i: &str) -> IResult<&str, DefineUserOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("COMMENT")(i)?;
|
let (i, _) = tag_no_case("COMMENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, v) = strand(i)?;
|
let (i, v) = cut(strand)(i)?;
|
||||||
Ok((i, DefineUserOption::Comment(v)))
|
Ok((i, DefineUserOption::Comment(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,8 +228,8 @@ fn user_roles(i: &str) -> IResult<&str, DefineUserOption> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ROLES")(i)?;
|
let (i, _) = tag_no_case("ROLES")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, roles) = separated_list0(commas, |i| {
|
let (i, roles) = separated_list1(commas, |i| {
|
||||||
let (i, v) = ident(i)?;
|
let (i, v) = cut(ident)(i)?;
|
||||||
// Verify the role is valid
|
// Verify the role is valid
|
||||||
Role::try_from(v.as_str()).map_err(|_| Failure(SqlError::Role(i, v.to_string())))?;
|
Role::try_from(v.as_str()).map_err(|_| Failure(SqlError::Role(i, v.to_string())))?;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ use crate::sql::timeout::{timeout, Timeout};
|
||||||
use crate::sql::value::{whats, Value, Values};
|
use crate::sql::value::{whats, Value, Values};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::combinator::cut;
|
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
|
@ -70,7 +69,7 @@ impl DeleteStatement {
|
||||||
// This is a single record result
|
// This is a single record result
|
||||||
Value::Array(mut a) if self.only => match a.len() {
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
// There was exactly one result
|
// There was exactly one result
|
||||||
v if v == 1 => Ok(a.remove(0)),
|
1 => Ok(a.remove(0)),
|
||||||
// There were no results
|
// There were no results
|
||||||
_ => Err(Error::SingleOnlyOutput),
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
},
|
},
|
||||||
|
@ -109,13 +108,10 @@ pub fn delete(i: &str) -> IResult<&str, DeleteStatement> {
|
||||||
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = whats(i)?;
|
let (i, what) = whats(i)?;
|
||||||
let (i, (cond, output, timeout, parallel)) = cut(|i| {
|
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
||||||
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
||||||
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
||||||
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
let (i, parallel) = opt(preceded(shouldbespace, tag_no_case("PARALLEL")))(i)?;
|
||||||
let (i, parallel) = opt(preceded(shouldbespace, tag_no_case("PARALLEL")))(i)?;
|
|
||||||
Ok((i, (cond, output, timeout, parallel)))
|
|
||||||
})(i)?;
|
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
DeleteStatement {
|
DeleteStatement {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::block::{block, Block, Entry};
|
use crate::sql::block::{block, Block, Entry};
|
||||||
use crate::sql::comment::{mightbespace, shouldbespace};
|
use crate::sql::comment::{mightbespace, shouldbespace};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::{expect_tag_no_case, IResult};
|
||||||
use crate::sql::param::{param, Param};
|
use crate::sql::param::{param, Param};
|
||||||
use crate::sql::value::{value, Value};
|
use crate::sql::value::{value, Value};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
|
@ -105,7 +105,7 @@ pub fn foreach(i: &str) -> IResult<&str, ForeachStatement> {
|
||||||
let (i, param) = param(i)?;
|
let (i, param) = param(i)?;
|
||||||
let (i, (range, block)) = cut(|i| {
|
let (i, (range, block)) = cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("IN")(i)?;
|
let (i, _) = expect_tag_no_case("IN")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, range) = value(i)?;
|
let (i, range) = value(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::iam::Action;
|
||||||
use crate::iam::ResourceKind;
|
use crate::iam::ResourceKind;
|
||||||
use crate::sql::base::base;
|
use crate::sql::base::base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expected;
|
||||||
|
use crate::sql::error::ExplainResultExt;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::object::Object;
|
use crate::sql::object::Object;
|
||||||
|
@ -236,10 +238,11 @@ pub fn info(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
let (i, _) = tag_no_case("INFO")(i)?;
|
let (i, _) = tag_no_case("INFO")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("FOR")(i)?;
|
let (i, _) = tag_no_case("FOR")(i)?;
|
||||||
cut(|i| {
|
let (i, _) = cut(shouldbespace)(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
expected(
|
||||||
alt((root, ns, db, sc, tb, user))(i)
|
"ROOT, NAMESPACE, DATABASE, SCOPE, TABLE or USER",
|
||||||
})(i)
|
cut(alt((root, ns, db, sc, tb, user))),
|
||||||
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root(i: &str) -> IResult<&str, InfoStatement> {
|
fn root(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
|
@ -260,19 +263,15 @@ fn db(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
fn sc(i: &str) -> IResult<&str, InfoStatement> {
|
fn sc(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
let (i, _) = alt((tag_no_case("SCOPE"), tag_no_case("SC")))(i)?;
|
let (i, _) = alt((tag_no_case("SCOPE"), tag_no_case("SC")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
cut(|i| {
|
let (i, scope) = cut(ident)(i)?;
|
||||||
let (i, scope) = ident(i)?;
|
Ok((i, InfoStatement::Sc(scope)))
|
||||||
Ok((i, InfoStatement::Sc(scope)))
|
|
||||||
})(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tb(i: &str) -> IResult<&str, InfoStatement> {
|
fn tb(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
let (i, _) = alt((tag_no_case("TABLE"), tag_no_case("TB")))(i)?;
|
let (i, _) = alt((tag_no_case("TABLE"), tag_no_case("TB")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
cut(|i| {
|
let (i, table) = cut(ident)(i)?;
|
||||||
let (i, table) = ident(i)?;
|
Ok((i, InfoStatement::Tb(table)))
|
||||||
Ok((i, InfoStatement::Tb(table)))
|
|
||||||
})(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user(i: &str) -> IResult<&str, InfoStatement> {
|
fn user(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
|
@ -285,7 +284,8 @@ fn user(i: &str) -> IResult<&str, InfoStatement> {
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = tag_no_case("ON")(i)?;
|
||||||
cut(|i| {
|
cut(|i| {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, base) = base(i)?;
|
let (i, base) =
|
||||||
|
base(i).explain("scopes are not allowed here", tag_no_case("SCOPE"))?;
|
||||||
Ok((i, base))
|
Ok((i, base))
|
||||||
})(i)
|
})(i)
|
||||||
})(i)?;
|
})(i)?;
|
||||||
|
|
|
@ -7,11 +7,14 @@ use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::data::{single, update, values, Data};
|
use crate::sql::data::{single, update, values, Data};
|
||||||
|
use crate::sql::error::expected;
|
||||||
|
use crate::sql::error::ExplainResultExt;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::output::{output, Output};
|
use crate::sql::output::{output, Output};
|
||||||
use crate::sql::param::param;
|
use crate::sql::param::param;
|
||||||
use crate::sql::table::table;
|
use crate::sql::table::table;
|
||||||
use crate::sql::timeout::{timeout, Timeout};
|
use crate::sql::timeout::{timeout, Timeout};
|
||||||
|
use crate::sql::value::value;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
@ -143,8 +146,14 @@ pub fn insert(i: &str) -> IResult<&str, InsertStatement> {
|
||||||
let (i, ignore) = opt(terminated(tag_no_case("IGNORE"), shouldbespace))(i)?;
|
let (i, ignore) = opt(terminated(tag_no_case("IGNORE"), shouldbespace))(i)?;
|
||||||
let (i, _) = tag_no_case("INTO")(i)?;
|
let (i, _) = tag_no_case("INTO")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, into) = cut(alt((map(table, Value::Table), map(param, Value::Param))))(i)?;
|
let (i, into) = expected(
|
||||||
let (i, _) = cut(shouldbespace)(i)?;
|
"a parameter or a table name",
|
||||||
|
cut(alt((
|
||||||
|
map(terminated(table, shouldbespace), Value::Table),
|
||||||
|
map(terminated(param, shouldbespace), Value::Param),
|
||||||
|
))),
|
||||||
|
)(i)
|
||||||
|
.explain("expressions aren't allowed here.", value)?;
|
||||||
let (i, data) = cut(alt((values, single)))(i)?;
|
let (i, data) = cut(alt((values, single)))(i)?;
|
||||||
let (i, update) = opt(preceded(shouldbespace, update))(i)?;
|
let (i, update) = opt(preceded(shouldbespace, update))(i)?;
|
||||||
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::err::Error;
|
||||||
use crate::iam::Auth;
|
use crate::iam::Auth;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::cond::{cond, Cond};
|
use crate::sql::cond::{cond, Cond};
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::fetch::{fetch, Fetchs};
|
use crate::sql::fetch::{fetch, Fetchs};
|
||||||
use crate::sql::field::{fields, Fields};
|
use crate::sql::field::{fields, Fields};
|
||||||
|
@ -125,7 +126,7 @@ pub fn live(i: &str) -> IResult<&str, LiveStatement> {
|
||||||
cut(|i| {
|
cut(|i| {
|
||||||
let (i, expr) = alt((map(tag_no_case("DIFF"), |_| Fields::default()), fields))(i)?;
|
let (i, expr) = alt((map(tag_no_case("DIFF"), |_| Fields::default()), fields))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("FROM")(i)?;
|
let (i, _) = expect_tag_no_case("FROM")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = alt((into(param), into(table)))(i)?;
|
let (i, what) = alt((into(param), into(table)))(i)?;
|
||||||
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
|
@ -35,10 +36,13 @@ pub fn option(i: &str) -> IResult<&str, OptionStatement> {
|
||||||
let (i, _) = tag_no_case("OPTION")(i)?;
|
let (i, _) = tag_no_case("OPTION")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, n) = ident(i)?;
|
let (i, n) = ident(i)?;
|
||||||
let (i, v) = cut(opt(alt((
|
let (i, v) = expected(
|
||||||
value(true, tuple((mightbespace, char('='), mightbespace, tag_no_case("TRUE")))),
|
"'=' follwed by a value for the option",
|
||||||
value(false, tuple((mightbespace, char('='), mightbespace, tag_no_case("FALSE")))),
|
cut(opt(alt((
|
||||||
))))(i)?;
|
value(true, tuple((mightbespace, char('='), mightbespace, tag_no_case("TRUE")))),
|
||||||
|
value(false, tuple((mightbespace, char('='), mightbespace, tag_no_case("FALSE")))),
|
||||||
|
)))),
|
||||||
|
)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
OptionStatement {
|
OptionStatement {
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::sql::array::array;
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::data::{data, Data};
|
use crate::sql::data::{data, Data};
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::output::{output, Output};
|
use crate::sql::output::{output, Output};
|
||||||
use crate::sql::param::param;
|
use crate::sql::param::param;
|
||||||
|
@ -179,7 +180,7 @@ impl RelateStatement {
|
||||||
// This is a single record result
|
// This is a single record result
|
||||||
Value::Array(mut a) if self.only => match a.len() {
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
// There was exactly one result
|
// There was exactly one result
|
||||||
v if v == 1 => Ok(a.remove(0)),
|
1 => Ok(a.remove(0)),
|
||||||
// There were no results
|
// There were no results
|
||||||
_ => Err(Error::SingleOnlyOutput),
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
},
|
},
|
||||||
|
@ -244,7 +245,8 @@ pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
|
||||||
fn relate_oi(i: &str) -> IResult<&str, (Value, Value, Value)> {
|
fn relate_oi(i: &str) -> IResult<&str, (Value, Value, Value)> {
|
||||||
let (i, prefix) = alt((into(subquery), into(array), into(param), into(thing)))(i)?;
|
let (i, prefix) = alt((into(subquery), into(array), into(param), into(thing)))(i)?;
|
||||||
let (i, _) = mightbespace(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
let (i, is_o) = cut(alt((value(true, tag("->")), value(false, tag("<-")))))(i)?;
|
let (i, is_o) =
|
||||||
|
expected("`->` or `<-`", cut(alt((value(true, tag("->")), value(false, tag("<-"))))))(i)?;
|
||||||
|
|
||||||
if is_o {
|
if is_o {
|
||||||
let (i, (kind, with)) = cut(relate_o)(i)?;
|
let (i, (kind, with)) = cut(relate_o)(i)?;
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -49,11 +50,9 @@ impl Display for RemoveAnalyzerStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyzer(i: &str) -> IResult<&str, RemoveAnalyzerStatement> {
|
pub fn analyzer(i: &str) -> IResult<&str, RemoveAnalyzerStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("ANALYZER")(i)?;
|
let (i, _) = tag_no_case("ANALYZER")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveAnalyzerStatement {
|
RemoveAnalyzerStatement {
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -53,11 +54,9 @@ impl Display for RemoveDatabaseStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
|
pub fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
|
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveDatabaseStatement {
|
RemoveDatabaseStatement {
|
||||||
|
|
|
@ -5,11 +5,13 @@ use crate::err::Error;
|
||||||
use crate::iam::{Action, ResourceKind};
|
use crate::iam::{Action, ResourceKind};
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
|
@ -55,16 +57,14 @@ impl Display for RemoveEventStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
|
pub fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("EVENT")(i)?;
|
let (i, _) = tag_no_case("EVENT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = ident(i)?;
|
let (i, what) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveEventStatement {
|
RemoveEventStatement {
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::err::Error;
|
||||||
use crate::iam::{Action, ResourceKind};
|
use crate::iam::{Action, ResourceKind};
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::idiom;
|
use crate::sql::idiom;
|
||||||
|
@ -12,6 +13,7 @@ use crate::sql::idiom::Idiom;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
|
@ -58,16 +60,14 @@ impl Display for RemoveFieldStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
|
pub fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("FIELD")(i)?;
|
let (i, _) = tag_no_case("FIELD")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = idiom::local(i)?;
|
let (i, name) = cut(idiom::local)(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = ident(i)?;
|
let (i, what) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveFieldStatement {
|
RemoveFieldStatement {
|
||||||
|
|
|
@ -53,8 +53,6 @@ impl Display for RemoveFunctionStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(i: &str) -> IResult<&str, RemoveFunctionStatement> {
|
pub fn function(i: &str) -> IResult<&str, RemoveFunctionStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("FUNCTION")(i)?;
|
let (i, _) = tag_no_case("FUNCTION")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag("fn::")(i)?;
|
let (i, _) = tag("fn::")(i)?;
|
||||||
|
|
|
@ -5,11 +5,13 @@ use crate::err::Error;
|
||||||
use crate::iam::{Action, ResourceKind};
|
use crate::iam::{Action, ResourceKind};
|
||||||
use crate::sql::base::Base;
|
use crate::sql::base::Base;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
|
@ -58,16 +60,14 @@ impl Display for RemoveIndexStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
|
pub fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("INDEX")(i)?;
|
let (i, _) = tag_no_case("INDEX")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = ident(i)?;
|
let (i, what) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveIndexStatement {
|
RemoveIndexStatement {
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub use field::{field, RemoveFieldStatement};
|
||||||
pub use function::{function, RemoveFunctionStatement};
|
pub use function::{function, RemoveFunctionStatement};
|
||||||
pub use index::{index, RemoveIndexStatement};
|
pub use index::{index, RemoveIndexStatement};
|
||||||
pub use namespace::{namespace, RemoveNamespaceStatement};
|
pub use namespace::{namespace, RemoveNamespaceStatement};
|
||||||
|
use nom::bytes::complete::tag_no_case;
|
||||||
pub use param::{param, RemoveParamStatement};
|
pub use param::{param, RemoveParamStatement};
|
||||||
pub use scope::{scope, RemoveScopeStatement};
|
pub use scope::{scope, RemoveScopeStatement};
|
||||||
pub use table::{table, RemoveTableStatement};
|
pub use table::{table, RemoveTableStatement};
|
||||||
|
@ -29,6 +30,7 @@ use crate::dbs::Options;
|
||||||
use crate::dbs::Transaction;
|
use crate::dbs::Transaction;
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
|
@ -105,6 +107,8 @@ impl Display for RemoveStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(i: &str) -> IResult<&str, RemoveStatement> {
|
pub fn remove(i: &str) -> IResult<&str, RemoveStatement> {
|
||||||
|
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||||
|
let (i, _) = shouldbespace(i)?;
|
||||||
alt((
|
alt((
|
||||||
map(namespace, RemoveStatement::Namespace),
|
map(namespace, RemoveStatement::Namespace),
|
||||||
map(database, RemoveStatement::Database),
|
map(database, RemoveStatement::Database),
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -53,11 +54,9 @@ impl Display for RemoveNamespaceStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
|
pub fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
|
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveNamespaceStatement {
|
RemoveNamespaceStatement {
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -50,12 +51,10 @@ impl Display for RemoveParamStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn param(i: &str) -> IResult<&str, RemoveParamStatement> {
|
pub fn param(i: &str) -> IResult<&str, RemoveParamStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("PARAM")(i)?;
|
let (i, _) = tag_no_case("PARAM")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = char('$')(i)?;
|
let (i, _) = cut(char('$'))(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveParamStatement {
|
RemoveParamStatement {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -52,11 +53,9 @@ impl Display for RemoveScopeStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
|
pub fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("SCOPE")(i)?;
|
let (i, _) = tag_no_case("SCOPE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveScopeStatement {
|
RemoveScopeStatement {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -63,11 +64,9 @@ impl Display for RemoveTableStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
|
pub fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("TABLE")(i)?;
|
let (i, _) = tag_no_case("TABLE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveTableStatement {
|
RemoveTableStatement {
|
||||||
|
|
|
@ -5,11 +5,13 @@ use crate::err::Error;
|
||||||
use crate::iam::{Action, ResourceKind};
|
use crate::iam::{Action, ResourceKind};
|
||||||
use crate::sql::base::{base_or_scope, Base};
|
use crate::sql::base::{base_or_scope, Base};
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -78,15 +80,13 @@ impl Display for RemoveTokenStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
|
pub fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("TOKEN")(i)?;
|
let (i, _) = tag_no_case("TOKEN")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, base) = base_or_scope(i)?;
|
let (i, base) = cut(base_or_scope)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveTokenStatement {
|
RemoveTokenStatement {
|
||||||
|
|
|
@ -5,11 +5,13 @@ use crate::err::Error;
|
||||||
use crate::iam::{Action, ResourceKind};
|
use crate::iam::{Action, ResourceKind};
|
||||||
use crate::sql::base::{base, Base};
|
use crate::sql::base::{base, Base};
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::ident::{ident, Ident};
|
use crate::sql::ident::{ident, Ident};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::combinator::cut;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -78,15 +80,13 @@ impl Display for RemoveUserStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user(i: &str) -> IResult<&str, RemoveUserStatement> {
|
pub fn user(i: &str) -> IResult<&str, RemoveUserStatement> {
|
||||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
|
||||||
let (i, _) = shouldbespace(i)?;
|
|
||||||
let (i, _) = tag_no_case("USER")(i)?;
|
let (i, _) = tag_no_case("USER")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, name) = ident(i)?;
|
let (i, name) = cut(ident)(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("ON")(i)?;
|
let (i, _) = expect_tag_no_case("ON")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, base) = base(i)?;
|
let (i, base) = cut(base)(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RemoveUserStatement {
|
RemoveUserStatement {
|
||||||
|
|
|
@ -8,6 +8,9 @@ use crate::err::Error;
|
||||||
use crate::idx::planner::QueryPlanner;
|
use crate::idx::planner::QueryPlanner;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::cond::{cond, Cond};
|
use crate::sql::cond::{cond, Cond};
|
||||||
|
use crate::sql::ending;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::explain::{explain, Explain};
|
use crate::sql::explain::{explain, Explain};
|
||||||
use crate::sql::fetch::{fetch, Fetchs};
|
use crate::sql::fetch::{fetch, Fetchs};
|
||||||
|
@ -30,6 +33,7 @@ use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::combinator::cut;
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
|
use nom::combinator::peek;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -74,6 +78,7 @@ impl SelectStatement {
|
||||||
}
|
}
|
||||||
self.cond.as_ref().map_or(false, |v| v.writeable())
|
self.cond.as_ref().map_or(false, |v| v.writeable())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
@ -138,7 +143,7 @@ impl SelectStatement {
|
||||||
// This is a single record result
|
// This is a single record result
|
||||||
Value::Array(mut a) if self.only => match a.len() {
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
// There was exactly one result
|
// There was exactly one result
|
||||||
v if v == 1 => Ok(a.remove(0)),
|
1 => Ok(a.remove(0)),
|
||||||
// There were no results
|
// There were no results
|
||||||
_ => Err(Error::SingleOnlyOutput),
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
},
|
},
|
||||||
|
@ -205,7 +210,7 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
||||||
let (i, expr) = fields(i)?;
|
let (i, expr) = fields(i)?;
|
||||||
let (i, omit) = opt(preceded(shouldbespace, omit))(i)?;
|
let (i, omit) = opt(preceded(shouldbespace, omit))(i)?;
|
||||||
let (i, _) = cut(shouldbespace)(i)?;
|
let (i, _) = cut(shouldbespace)(i)?;
|
||||||
let (i, _) = cut(tag_no_case("FROM"))(i)?;
|
let (i, _) = expect_tag_no_case("FROM")(i)?;
|
||||||
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = cut(shouldbespace)(i)?;
|
let (i, _) = cut(shouldbespace)(i)?;
|
||||||
let (i, what) = cut(selects)(i)?;
|
let (i, what) = cut(selects)(i)?;
|
||||||
|
@ -224,6 +229,11 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
||||||
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
||||||
let (i, parallel) = opt(preceded(shouldbespace, tag_no_case("PARALLEL")))(i)?;
|
let (i, parallel) = opt(preceded(shouldbespace, tag_no_case("PARALLEL")))(i)?;
|
||||||
let (i, explain) = opt(preceded(shouldbespace, explain))(i)?;
|
let (i, explain) = opt(preceded(shouldbespace, explain))(i)?;
|
||||||
|
let (i, _) = expected(
|
||||||
|
"one of WITH, WHERE, SPLIT, GROUP, ORDER, LIMIT, START, FETCH, VERSION, TIMEOUT, PARELLEL, or EXPLAIN",
|
||||||
|
cut(peek(ending::query))
|
||||||
|
)(i)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
SelectStatement {
|
SelectStatement {
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::iam::ResourceKind;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::common::take_u64;
|
use crate::sql::common::take_u64;
|
||||||
use crate::sql::datetime::datetime;
|
use crate::sql::datetime::datetime;
|
||||||
|
use crate::sql::error::expect_tag_no_case;
|
||||||
|
use crate::sql::error::expected;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::table::{table, Table};
|
use crate::sql::table::{table, Table};
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
@ -99,15 +101,18 @@ impl fmt::Display for ShowStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table_or_database(i: &str) -> IResult<&str, Option<Table>> {
|
pub fn table_or_database(i: &str) -> IResult<&str, Option<Table>> {
|
||||||
let (i, v) = alt((
|
let (i, v) = expected(
|
||||||
map(preceded(tag_no_case("table"), preceded(shouldbespace, table)), Some),
|
"one of TABLE, DATABASE",
|
||||||
value(None, tag_no_case("database")),
|
alt((
|
||||||
))(i)?;
|
map(preceded(tag_no_case("TABLE"), preceded(shouldbespace, cut(table))), Some),
|
||||||
|
value(None, tag_no_case("DATABASE")),
|
||||||
|
)),
|
||||||
|
)(i)?;
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn since(i: &str) -> IResult<&str, ShowSince> {
|
pub fn since(i: &str) -> IResult<&str, ShowSince> {
|
||||||
let (i, _) = tag_no_case("SINCE")(i)?;
|
let (i, _) = expect_tag_no_case("SINCE")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
|
|
||||||
cut(alt((map(take_u64, ShowSince::Versionstamp), map(datetime, ShowSince::Timestamp))))(i)
|
cut(alt((map(take_u64, ShowSince::Versionstamp), map(datetime, ShowSince::Timestamp))))(i)
|
||||||
|
@ -116,7 +121,6 @@ pub fn since(i: &str) -> IResult<&str, ShowSince> {
|
||||||
pub fn limit(i: &str) -> IResult<&str, u32> {
|
pub fn limit(i: &str) -> IResult<&str, u32> {
|
||||||
let (i, _) = tag_no_case("LIMIT")(i)?;
|
let (i, _) = tag_no_case("LIMIT")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
|
|
||||||
cut(u32)(i)
|
cut(u32)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ impl UpdateStatement {
|
||||||
// This is a single record result
|
// This is a single record result
|
||||||
Value::Array(mut a) if self.only => match a.len() {
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
// There was exactly one result
|
// There was exactly one result
|
||||||
v if v == 1 => Ok(a.remove(0)),
|
1 => Ok(a.remove(0)),
|
||||||
// There were no results
|
// There were no results
|
||||||
_ => Err(Error::SingleOnlyOutput),
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::sql::error::Error::Parser;
|
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::escape::quote_str;
|
use crate::sql::escape::quote_str;
|
||||||
|
use crate::sql::ParseError;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::{escaped_transform, is_not, tag, take, take_while_m_n};
|
use nom::bytes::complete::{escaped_transform, is_not, tag, take, take_while_m_n};
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::combinator::value;
|
use nom::combinator::value;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::Err::Failure;
|
use nom::Err;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
@ -14,6 +14,8 @@ use std::ops::Deref;
|
||||||
use std::ops::{self, RangeInclusive};
|
use std::ops::{self, RangeInclusive};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Strand";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Strand";
|
||||||
|
|
||||||
const SINGLE: char = '\'';
|
const SINGLE: char = '\'';
|
||||||
|
@ -93,7 +95,7 @@ pub fn strand(i: &str) -> IResult<&str, Strand> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strand_raw(i: &str) -> IResult<&str, String> {
|
pub fn strand_raw(i: &str) -> IResult<&str, String> {
|
||||||
alt((strand_blank, strand_single, strand_double))(i)
|
expected("a strand", alt((strand_blank, strand_single, strand_double)))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strand_blank(i: &str) -> IResult<&str, String> {
|
fn strand_blank(i: &str) -> IResult<&str, String> {
|
||||||
|
@ -162,7 +164,11 @@ fn char_unicode_bare(i: &str) -> IResult<&str, char> {
|
||||||
// Take exactly 4 bytes
|
// Take exactly 4 bytes
|
||||||
let (i, v) = take(4usize)(i)?;
|
let (i, v) = take(4usize)(i)?;
|
||||||
// Parse them as hex, where an error indicates invalid hex digits
|
// Parse them as hex, where an error indicates invalid hex digits
|
||||||
let v: u16 = u16::from_str_radix(v, 16).map_err(|_| Failure(Parser(i)))?;
|
let v: u16 = u16::from_str_radix(v, 16).map_err(|_| {
|
||||||
|
Err::Failure(ParseError::InvalidUnicode {
|
||||||
|
tried: i,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
if LEADING_SURROGATES.contains(&v) {
|
if LEADING_SURROGATES.contains(&v) {
|
||||||
let leading = v;
|
let leading = v;
|
||||||
|
@ -172,9 +178,15 @@ fn char_unicode_bare(i: &str) -> IResult<&str, char> {
|
||||||
// Take exactly 4 more bytes
|
// Take exactly 4 more bytes
|
||||||
let (i, v) = take(4usize)(i)?;
|
let (i, v) = take(4usize)(i)?;
|
||||||
// Parse them as hex, where an error indicates invalid hex digits
|
// Parse them as hex, where an error indicates invalid hex digits
|
||||||
let trailing = u16::from_str_radix(v, 16).map_err(|_| Failure(Parser(i)))?;
|
let trailing = u16::from_str_radix(v, 16).map_err(|_| {
|
||||||
|
Err::Failure(ParseError::InvalidUnicode {
|
||||||
|
tried: i,
|
||||||
|
})
|
||||||
|
})?;
|
||||||
if !TRAILING_SURROGATES.contains(&trailing) {
|
if !TRAILING_SURROGATES.contains(&trailing) {
|
||||||
return Err(Failure(Parser(i)));
|
return Err(Err::Failure(ParseError::InvalidUnicode {
|
||||||
|
tried: i,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
// Compute the codepoint.
|
// Compute the codepoint.
|
||||||
// https://datacadamia.com/data/type/text/surrogate#from_surrogate_to_character_code
|
// https://datacadamia.com/data/type/text/surrogate#from_surrogate_to_character_code
|
||||||
|
@ -183,12 +195,18 @@ fn char_unicode_bare(i: &str) -> IResult<&str, char> {
|
||||||
+ trailing as u32
|
+ trailing as u32
|
||||||
- *TRAILING_SURROGATES.start() as u32;
|
- *TRAILING_SURROGATES.start() as u32;
|
||||||
// Convert to char
|
// Convert to char
|
||||||
let v = char::from_u32(codepoint).ok_or(Failure(Parser(i)))?;
|
let v = char::from_u32(codepoint).ok_or(Err::Failure(ParseError::InvalidUnicode {
|
||||||
|
tried: i,
|
||||||
|
}))?;
|
||||||
// Return the char
|
// Return the char
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
} else {
|
} else {
|
||||||
// We can convert this to char or error in the case of invalid Unicode character
|
// We can convert this to char or error in the case of invalid Unicode character
|
||||||
let v = char::from_u32(v as u32).filter(|c| *c != 0 as char).ok_or(Failure(Parser(i)))?;
|
let v = char::from_u32(v as u32).filter(|c| *c != 0 as char).ok_or(Err::Failure(
|
||||||
|
ParseError::InvalidUnicode {
|
||||||
|
tried: i,
|
||||||
|
},
|
||||||
|
))?;
|
||||||
// Return the char
|
// Return the char
|
||||||
Ok((i, v))
|
Ok((i, v))
|
||||||
}
|
}
|
||||||
|
@ -203,7 +221,11 @@ fn char_unicode_bracketed(i: &str) -> IResult<&str, char> {
|
||||||
// We can convert this to u32 as the max is 0xffffff
|
// We can convert this to u32 as the max is 0xffffff
|
||||||
let v = u32::from_str_radix(v, 16).unwrap();
|
let v = u32::from_str_radix(v, 16).unwrap();
|
||||||
// We can convert this to char or error in the case of invalid Unicode character
|
// We can convert this to char or error in the case of invalid Unicode character
|
||||||
let v = char::from_u32(v).filter(|c| *c != 0 as char).ok_or(Failure(Parser(i)))?;
|
let v = char::from_u32(v).filter(|c| *c != 0 as char).ok_or(Err::Failure(
|
||||||
|
ParseError::InvalidUnicode {
|
||||||
|
tried: i,
|
||||||
|
},
|
||||||
|
))?;
|
||||||
// Read the } character
|
// Read the } character
|
||||||
let (i, _) = char('}')(i)?;
|
let (i, _) = char('}')(i)?;
|
||||||
// Return the char
|
// Return the char
|
||||||
|
|
|
@ -17,12 +17,18 @@ use crate::sql::statements::select::{select, SelectStatement};
|
||||||
use crate::sql::statements::update::{update, UpdateStatement};
|
use crate::sql::statements::update::{update, UpdateStatement};
|
||||||
use crate::sql::value::{value, Value};
|
use crate::sql::value::{value, Value};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::combinator::{cut, map};
|
use nom::bytes::complete::{tag, tag_no_case};
|
||||||
|
use nom::combinator::{map, opt, peek};
|
||||||
|
use nom::sequence::tuple;
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
use super::comment::{mightbespace, shouldbespace};
|
||||||
|
use super::error::ExplainResultExt;
|
||||||
|
use super::util::expect_delimited;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
|
@ -126,27 +132,29 @@ fn subquery_ifelse(i: &str) -> IResult<&str, Subquery> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subquery_value(i: &str) -> IResult<&str, Subquery> {
|
fn subquery_value(i: &str) -> IResult<&str, Subquery> {
|
||||||
let (i, _) = openparentheses(i)?;
|
expect_delimited(openparentheses, map(value, Subquery::Value), closeparentheses)(i)
|
||||||
let (i, v) = map(value, Subquery::Value)(i)?;
|
|
||||||
let (i, _) = cut(closeparentheses)(i)?;
|
|
||||||
Ok((i, v))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subquery_other(i: &str) -> IResult<&str, Subquery> {
|
fn subquery_other(i: &str) -> IResult<&str, Subquery> {
|
||||||
let _diving = crate::sql::parser::depth::dive()?;
|
alt((expect_delimited(openparentheses, subquery_inner, closeparentheses), |i| {
|
||||||
alt((
|
let (i, v) = subquery_inner(i)?;
|
||||||
|i| {
|
let (i, _) = ending(i)?;
|
||||||
let (i, _) = openparentheses(i)?;
|
let (i, _) = eat_semicolon(i)?;
|
||||||
let (i, v) = subquery_inner(i)?;
|
Ok((i, v))
|
||||||
let (i, _) = cut(closeparentheses)(i)?;
|
}))(i)
|
||||||
Ok((i, v))
|
}
|
||||||
},
|
|
||||||
|i| {
|
fn eat_semicolon(i: &str) -> IResult<&str, ()> {
|
||||||
let (i, v) = subquery_inner(i)?;
|
let (i, _) = opt(|i| {
|
||||||
let (i, _) = ending(i)?;
|
let (i, _) = mightbespace(i)?;
|
||||||
Ok((i, v))
|
let (i, _) = tag(";")(i)?;
|
||||||
},
|
let (i, _) = peek(tuple((
|
||||||
))(i)
|
shouldbespace,
|
||||||
|
alt((tag_no_case("THEN"), tag_no_case("ELSE"), tag_no_case("END"))),
|
||||||
|
)))(i)?;
|
||||||
|
Ok((i, ()))
|
||||||
|
})(i)?;
|
||||||
|
Ok((i, ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subquery_inner(i: &str) -> IResult<&str, Subquery> {
|
fn subquery_inner(i: &str) -> IResult<&str, Subquery> {
|
||||||
|
@ -161,6 +169,27 @@ fn subquery_inner(i: &str) -> IResult<&str, Subquery> {
|
||||||
map(define, Subquery::Define),
|
map(define, Subquery::Define),
|
||||||
map(remove, Subquery::Remove),
|
map(remove, Subquery::Remove),
|
||||||
))(i)
|
))(i)
|
||||||
|
.explain("This statement is not allowed in a subquery", disallowed_subquery_statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disallowed_subquery_statements(i: &str) -> IResult<&str, ()> {
|
||||||
|
let (i, _) = alt((
|
||||||
|
tag_no_case("ANALYZED"),
|
||||||
|
tag_no_case("BEGIN"),
|
||||||
|
tag_no_case("BREAK"),
|
||||||
|
tag_no_case("CONTINUE"),
|
||||||
|
tag_no_case("COMMIT"),
|
||||||
|
tag_no_case("FOR"),
|
||||||
|
tag_no_case("INFO"),
|
||||||
|
tag_no_case("KILL"),
|
||||||
|
tag_no_case("LIVE"),
|
||||||
|
tag_no_case("OPTION"),
|
||||||
|
tag_no_case("RELATE"),
|
||||||
|
tag_no_case("SLEEP"),
|
||||||
|
tag_no_case("THROW"),
|
||||||
|
tag_no_case("USE"),
|
||||||
|
))(i)?;
|
||||||
|
Ok((i, ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -13,6 +13,8 @@ use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Table";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Table";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
|
@ -89,7 +91,7 @@ impl Display for Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table(i: &str) -> IResult<&str, Table> {
|
pub fn table(i: &str) -> IResult<&str, Table> {
|
||||||
let (i, v) = ident_raw(i)?;
|
let (i, v) = expected("a table name", ident_raw)(i)?;
|
||||||
Ok((i, Table(v)))
|
Ok((i, Table(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::error::expected;
|
||||||
|
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Thing";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Thing";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
|
@ -120,7 +122,7 @@ impl Thing {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thing(i: &str) -> IResult<&str, Thing> {
|
pub fn thing(i: &str) -> IResult<&str, Thing> {
|
||||||
alt((thing_raw, thing_single, thing_double))(i)
|
expected("a thing", alt((thing_raw, thing_single, thing_double)))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn thing_single(i: &str) -> IResult<&str, Thing> {
|
fn thing_single(i: &str) -> IResult<&str, Thing> {
|
||||||
|
|
|
@ -1,4 +1,55 @@
|
||||||
use nom::{error::ParseError, Err, IResult, InputLength, Parser};
|
use crate::sql::error::{IResult, ParseError};
|
||||||
|
use nom::{Err, InputLength, Parser};
|
||||||
|
|
||||||
|
/// Parses a parser delimited by two other parsers.
|
||||||
|
///
|
||||||
|
/// This parser failes (not errors) if the second delimiting parser returns an error.
|
||||||
|
pub fn expect_delimited<I, D, V, T, O, O1>(
|
||||||
|
mut prefix: D,
|
||||||
|
mut value: V,
|
||||||
|
mut terminator: T,
|
||||||
|
) -> impl FnMut(I) -> IResult<I, O, ParseError<I>>
|
||||||
|
where
|
||||||
|
I: Clone + InputLength,
|
||||||
|
V: Parser<I, O, ParseError<I>>,
|
||||||
|
D: Parser<I, I, ParseError<I>>,
|
||||||
|
T: Parser<I, O1, ParseError<I>>,
|
||||||
|
{
|
||||||
|
move |i| {
|
||||||
|
let (i, s) = prefix.parse(i)?;
|
||||||
|
let (i, res) = value.parse(i)?;
|
||||||
|
match terminator.parse(i) {
|
||||||
|
Ok((i, _)) => Result::Ok((i, res)),
|
||||||
|
Result::Err(Err::Failure(e)) | Result::Err(Err::Error(e)) => {
|
||||||
|
Result::Err(Err::Failure(ParseError::MissingDelimiter {
|
||||||
|
opened: s,
|
||||||
|
tried: e.tried(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Result::Err(Err::Incomplete(e)) => Result::Err(Err::Incomplete(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_terminator<P, I, O>(
|
||||||
|
open_span: I,
|
||||||
|
mut terminator: P,
|
||||||
|
) -> impl FnMut(I) -> IResult<I, O, ParseError<I>>
|
||||||
|
where
|
||||||
|
I: Clone,
|
||||||
|
P: Parser<I, O, ParseError<I>>,
|
||||||
|
{
|
||||||
|
move |i| match terminator.parse(i) {
|
||||||
|
Ok((i, x)) => Ok((i, x)),
|
||||||
|
Result::Err(Err::Failure(e)) | Result::Err(Err::Error(e)) => {
|
||||||
|
Result::Err(Err::Failure(ParseError::MissingDelimiter {
|
||||||
|
opened: open_span.clone(),
|
||||||
|
tried: e.tried(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Result::Err(Err::Incomplete(e)) => Result::Err(Err::Incomplete(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a delimited list with an option trailing seperator in the form of:
|
/// Parses a delimited list with an option trailing seperator in the form of:
|
||||||
///
|
///
|
||||||
|
@ -17,22 +68,21 @@ use nom::{error::ParseError, Err, IResult, InputLength, Parser};
|
||||||
/// and if there is none it returns a failure. Otherwise completes with an vec of the parsed
|
/// and if there is none it returns a failure. Otherwise completes with an vec of the parsed
|
||||||
/// values.
|
/// values.
|
||||||
///
|
///
|
||||||
pub fn delimited_list0<I, E, D, S, V, T, O, O1, O2, O3>(
|
pub fn delimited_list0<I, D, S, V, T, O, O1, O2>(
|
||||||
mut prefix: D,
|
mut prefix: D,
|
||||||
mut seperator: S,
|
mut seperator: S,
|
||||||
mut value: V,
|
mut value: V,
|
||||||
mut terminator: T,
|
mut terminator: T,
|
||||||
) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
|
) -> impl FnMut(I) -> IResult<I, Vec<O>, ParseError<I>>
|
||||||
where
|
where
|
||||||
I: Clone + InputLength,
|
I: Clone + InputLength,
|
||||||
V: Parser<I, O, E>,
|
V: Parser<I, O, ParseError<I>>,
|
||||||
D: Parser<I, O1, E>,
|
D: Parser<I, I, ParseError<I>>,
|
||||||
S: Parser<I, O2, E>,
|
S: Parser<I, O1, ParseError<I>>,
|
||||||
T: Parser<I, O3, E>,
|
T: Parser<I, O2, ParseError<I>>,
|
||||||
E: ParseError<I>,
|
|
||||||
{
|
{
|
||||||
move |i| {
|
move |i| {
|
||||||
let (i, _) = prefix.parse(i)?;
|
let (i, s) = prefix.parse(i)?;
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut input = i;
|
let mut input = i;
|
||||||
loop {
|
loop {
|
||||||
|
@ -50,12 +100,17 @@ where
|
||||||
Ok((i, _)) => {
|
Ok((i, _)) => {
|
||||||
input = i;
|
input = i;
|
||||||
}
|
}
|
||||||
Err(Err::Error(_)) => match terminator.parse(i) {
|
Err(Err::Error(_)) => match terminator.parse(i.clone()) {
|
||||||
Ok((i, _)) => {
|
Ok((i, _)) => {
|
||||||
input = i;
|
input = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Result::Err(Err::Error(e)) => return Err(Err::Failure(e)),
|
Result::Err(Err::Error(_)) => {
|
||||||
|
return Err(Err::Failure(ParseError::MissingDelimiter {
|
||||||
|
opened: s,
|
||||||
|
tried: i,
|
||||||
|
}))
|
||||||
|
}
|
||||||
Result::Err(e) => return Err(e),
|
Result::Err(e) => return Err(e),
|
||||||
},
|
},
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
|
@ -82,22 +137,21 @@ where
|
||||||
/// and if there is none it returns a failure. Otherwise completes with an vec of the parsed
|
/// and if there is none it returns a failure. Otherwise completes with an vec of the parsed
|
||||||
/// values.
|
/// values.
|
||||||
///
|
///
|
||||||
pub fn delimited_list1<I, E, D, S, V, T, O, O1, O2, O3>(
|
pub fn delimited_list1<I, D, S, V, T, O, O1, O2>(
|
||||||
mut prefix: D,
|
mut prefix: D,
|
||||||
mut seperator: S,
|
mut seperator: S,
|
||||||
mut value: V,
|
mut value: V,
|
||||||
mut terminator: T,
|
mut terminator: T,
|
||||||
) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
|
) -> impl FnMut(I) -> IResult<I, Vec<O>, ParseError<I>>
|
||||||
where
|
where
|
||||||
I: Clone + InputLength,
|
I: Clone + InputLength,
|
||||||
V: Parser<I, O, E>,
|
V: Parser<I, O, ParseError<I>>,
|
||||||
D: Parser<I, O1, E>,
|
D: Parser<I, I, ParseError<I>>,
|
||||||
S: Parser<I, O2, E>,
|
S: Parser<I, O1, ParseError<I>>,
|
||||||
T: Parser<I, O3, E>,
|
T: Parser<I, O2, ParseError<I>>,
|
||||||
E: ParseError<I>,
|
|
||||||
{
|
{
|
||||||
move |i| {
|
move |i| {
|
||||||
let (i, _) = prefix.parse(i)?;
|
let (i, s) = prefix.parse(i)?;
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut input = i;
|
let mut input = i;
|
||||||
loop {
|
loop {
|
||||||
|
@ -115,12 +169,17 @@ where
|
||||||
Ok((i, _)) => {
|
Ok((i, _)) => {
|
||||||
input = i;
|
input = i;
|
||||||
}
|
}
|
||||||
Err(Err::Error(_)) => match terminator.parse(i) {
|
Err(Err::Error(_)) => match terminator.parse(i.clone()) {
|
||||||
Ok((i, _)) => {
|
Ok((i, _)) => {
|
||||||
input = i;
|
input = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Result::Err(Err::Error(e)) => return Err(Err::Failure(e)),
|
Result::Err(Err::Error(_)) => {
|
||||||
|
return Err(Err::Failure(ParseError::MissingDelimiter {
|
||||||
|
opened: s,
|
||||||
|
tried: i,
|
||||||
|
}))
|
||||||
|
}
|
||||||
Result::Err(e) => return Err(e),
|
Result::Err(e) => return Err(e),
|
||||||
},
|
},
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
|
|
|
@ -37,7 +37,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
let stmt = BeginStatement::default();
|
let stmt = BeginStatement;
|
||||||
let value: BeginStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
let value: BeginStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(value, stmt);
|
assert_eq!(value, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
let stmt = BreakStatement::default();
|
let stmt = BreakStatement;
|
||||||
let value: BreakStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
let value: BreakStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(value, stmt);
|
assert_eq!(value, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
let stmt = CancelStatement::default();
|
let stmt = CancelStatement;
|
||||||
let value: CancelStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
let value: CancelStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(value, stmt);
|
assert_eq!(value, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
let stmt = CommitStatement::default();
|
let stmt = CommitStatement;
|
||||||
let value: CommitStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
let value: CommitStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(value, stmt);
|
assert_eq!(value, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
let stmt = ContinueStatement::default();
|
let stmt = ContinueStatement;
|
||||||
let value: ContinueStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
let value: ContinueStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(value, stmt);
|
assert_eq!(value, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::derivable_impls)]
|
||||||
impl Default for ShowStatement {
|
impl Default for ShowStatement {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ShowStatement {
|
ShowStatement {
|
||||||
|
|
|
@ -8,19 +8,20 @@ use crate::fnc::util::string::fuzzy::Fuzzy;
|
||||||
use crate::sql::array::Uniq;
|
use crate::sql::array::Uniq;
|
||||||
use crate::sql::array::{array, Array};
|
use crate::sql::array::{array, Array};
|
||||||
use crate::sql::block::{block, Block};
|
use crate::sql::block::{block, Block};
|
||||||
|
use crate::sql::builtin::builtin_name;
|
||||||
use crate::sql::bytes::Bytes;
|
use crate::sql::bytes::Bytes;
|
||||||
use crate::sql::cast::{cast, Cast};
|
use crate::sql::cast::{cast, Cast};
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
use crate::sql::constant::{constant, Constant};
|
use crate::sql::constant::Constant;
|
||||||
use crate::sql::datetime::{datetime, Datetime};
|
use crate::sql::datetime::{datetime, Datetime};
|
||||||
use crate::sql::duration::{duration, Duration};
|
use crate::sql::duration::{duration, Duration};
|
||||||
use crate::sql::edges::{edges, Edges};
|
use crate::sql::edges::{edges, Edges};
|
||||||
use crate::sql::ending::keyword;
|
use crate::sql::ending::keyword;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::expression::{binary, unary, Expression};
|
use crate::sql::expression::{unary, Expression};
|
||||||
use crate::sql::fmt::{Fmt, Pretty};
|
use crate::sql::fmt::{Fmt, Pretty};
|
||||||
use crate::sql::function::{function, Function};
|
use crate::sql::function::{builtin_function, defined_function, Function};
|
||||||
use crate::sql::future::{future, Future};
|
use crate::sql::future::{future, Future};
|
||||||
use crate::sql::geometry::{geometry, Geometry};
|
use crate::sql::geometry::{geometry, Geometry};
|
||||||
use crate::sql::id::{Gen, Id};
|
use crate::sql::id::{Gen, Id};
|
||||||
|
@ -39,7 +40,7 @@ use crate::sql::subquery::{subquery, Subquery};
|
||||||
use crate::sql::table::{table, Table};
|
use crate::sql::table::{table, Table};
|
||||||
use crate::sql::thing::{thing, Thing};
|
use crate::sql::thing::{thing, Thing};
|
||||||
use crate::sql::uuid::{uuid as unique, Uuid};
|
use crate::sql::uuid::{uuid as unique, Uuid};
|
||||||
use crate::sql::{operator, Query};
|
use crate::sql::{builtin, operator, Query};
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
|
@ -47,7 +48,7 @@ use geo::Point;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::char;
|
use nom::character::complete::char;
|
||||||
use nom::combinator::{self, into, opt};
|
use nom::combinator::{self, cut, into, opt};
|
||||||
use nom::multi::separated_list0;
|
use nom::multi::separated_list0;
|
||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use nom::sequence::terminated;
|
use nom::sequence::terminated;
|
||||||
|
@ -2705,28 +2706,24 @@ impl TryNeg for Value {
|
||||||
/// Parse any `Value` including expressions
|
/// Parse any `Value` including expressions
|
||||||
pub fn value(i: &str) -> IResult<&str, Value> {
|
pub fn value(i: &str) -> IResult<&str, Value> {
|
||||||
let (i, start) = single(i)?;
|
let (i, start) = single(i)?;
|
||||||
let (i, expr_tail) = opt(|i| {
|
if let (i, Some(o)) = opt(operator::binary)(i)? {
|
||||||
let (i, o) = operator::binary(i)?;
|
let (i, r) = cut(value)(i)?;
|
||||||
let (i, r) = value(i)?;
|
|
||||||
Ok((i, (o, r)))
|
|
||||||
})(i)?;
|
|
||||||
let v = if let Some((o, r)) = expr_tail {
|
|
||||||
let expr = match r {
|
let expr = match r {
|
||||||
Value::Expression(r) => r.augment(start, o),
|
Value::Expression(r) => r.augment(start, o),
|
||||||
_ => Expression::new(start, o, r),
|
_ => Expression::new(start, o, r),
|
||||||
};
|
};
|
||||||
Value::from(expr)
|
let v = Value::from(expr);
|
||||||
|
Ok((i, v))
|
||||||
} else {
|
} else {
|
||||||
start
|
Ok((i, start))
|
||||||
};
|
}
|
||||||
Ok((i, v))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse any `Value` excluding binary expressions
|
/// Parse any `Value` excluding binary expressions
|
||||||
pub fn single(i: &str) -> IResult<&str, Value> {
|
pub fn single(i: &str) -> IResult<&str, Value> {
|
||||||
// Dive in `single` (as opposed to `value`) since it is directly
|
// Dive in `single` (as opposed to `value`) since it is directly
|
||||||
// called by `Cast`
|
// called by `Cast`
|
||||||
let _diving = crate::sql::parser::depth::dive()?;
|
let _diving = crate::sql::parser::depth::dive(i)?;
|
||||||
let (i, v) = alt((
|
let (i, v) = alt((
|
||||||
alt((
|
alt((
|
||||||
terminated(
|
terminated(
|
||||||
|
@ -2743,10 +2740,9 @@ pub fn single(i: &str) -> IResult<&str, Value> {
|
||||||
alt((
|
alt((
|
||||||
into(future),
|
into(future),
|
||||||
into(cast),
|
into(cast),
|
||||||
into(function),
|
function_or_const,
|
||||||
into(geometry),
|
into(geometry),
|
||||||
into(subquery),
|
into(subquery),
|
||||||
into(constant),
|
|
||||||
into(datetime),
|
into(datetime),
|
||||||
into(duration),
|
into(duration),
|
||||||
into(unique),
|
into(unique),
|
||||||
|
@ -2770,11 +2766,10 @@ pub fn single(i: &str) -> IResult<&str, Value> {
|
||||||
reparse_idiom_start(v, i)
|
reparse_idiom_start(v, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(i: &str) -> IResult<&str, Value> {
|
pub fn select_start(i: &str) -> IResult<&str, Value> {
|
||||||
let (i, v) = alt((
|
let (i, v) = alt((
|
||||||
alt((
|
alt((
|
||||||
into(unary),
|
into(unary),
|
||||||
into(binary),
|
|
||||||
combinator::value(Value::None, tag_no_case("NONE")),
|
combinator::value(Value::None, tag_no_case("NONE")),
|
||||||
combinator::value(Value::Null, tag_no_case("NULL")),
|
combinator::value(Value::Null, tag_no_case("NULL")),
|
||||||
combinator::value(Value::Bool(true), tag_no_case("true")),
|
combinator::value(Value::Bool(true), tag_no_case("true")),
|
||||||
|
@ -2784,10 +2779,9 @@ pub fn select(i: &str) -> IResult<&str, Value> {
|
||||||
alt((
|
alt((
|
||||||
into(future),
|
into(future),
|
||||||
into(cast),
|
into(cast),
|
||||||
into(function),
|
function_or_const,
|
||||||
into(geometry),
|
into(geometry),
|
||||||
into(subquery),
|
into(subquery),
|
||||||
into(constant),
|
|
||||||
into(datetime),
|
into(datetime),
|
||||||
into(duration),
|
into(duration),
|
||||||
into(unique),
|
into(unique),
|
||||||
|
@ -2808,13 +2802,45 @@ pub fn select(i: &str) -> IResult<&str, Value> {
|
||||||
reparse_idiom_start(v, i)
|
reparse_idiom_start(v, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn function_or_const(i: &str) -> IResult<&str, Value> {
|
||||||
|
alt((into(defined_function), |i| {
|
||||||
|
let (i, v) = builtin_name(i)?;
|
||||||
|
match v {
|
||||||
|
builtin::BuiltinName::Constant(x) => Ok((i, x.into())),
|
||||||
|
builtin::BuiltinName::Function(name) => {
|
||||||
|
builtin_function(name, i).map(|(i, v)| (i, v.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(i: &str) -> IResult<&str, Value> {
|
||||||
|
let _diving = crate::sql::parser::depth::dive(i)?;
|
||||||
|
let (i, start) = select_start(i)?;
|
||||||
|
if let (i, Some(op)) = opt(operator::binary)(i)? {
|
||||||
|
// In a binary expression single ident's arent tables but paths.
|
||||||
|
let start = match start {
|
||||||
|
Value::Table(Table(x)) => Value::Idiom(Idiom::from(x)),
|
||||||
|
x => x,
|
||||||
|
};
|
||||||
|
let (i, r) = cut(value)(i)?;
|
||||||
|
let expr = match r {
|
||||||
|
Value::Expression(r) => r.augment(start, op),
|
||||||
|
_ => Expression::new(start, op, r),
|
||||||
|
};
|
||||||
|
let v = Value::from(expr);
|
||||||
|
Ok((i, v))
|
||||||
|
} else {
|
||||||
|
Ok((i, start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Used in CREATE, UPDATE, and DELETE clauses
|
/// Used in CREATE, UPDATE, and DELETE clauses
|
||||||
pub fn what(i: &str) -> IResult<&str, Value> {
|
pub fn what(i: &str) -> IResult<&str, Value> {
|
||||||
let (i, v) = alt((
|
let (i, v) = alt((
|
||||||
into(idiom::multi_without_start),
|
into(idiom::multi_without_start),
|
||||||
into(function),
|
function_or_const,
|
||||||
into(subquery),
|
into(subquery),
|
||||||
into(constant),
|
|
||||||
into(datetime),
|
into(datetime),
|
||||||
into(duration),
|
into(duration),
|
||||||
into(future),
|
into(future),
|
||||||
|
@ -2831,7 +2857,7 @@ pub fn what(i: &str) -> IResult<&str, Value> {
|
||||||
|
|
||||||
/// Used to parse any simple JSON-like value
|
/// Used to parse any simple JSON-like value
|
||||||
pub fn json(i: &str) -> IResult<&str, Value> {
|
pub fn json(i: &str) -> IResult<&str, Value> {
|
||||||
let _diving = crate::sql::parser::depth::dive()?;
|
let _diving = crate::sql::parser::depth::dive(i)?;
|
||||||
// Use a specific parser for JSON objects
|
// Use a specific parser for JSON objects
|
||||||
fn object(i: &str) -> IResult<&str, Object> {
|
fn object(i: &str) -> IResult<&str, Object> {
|
||||||
let (i, _) = char('{')(i)?;
|
let (i, _) = char('{')(i)?;
|
||||||
|
|
|
@ -12,6 +12,8 @@ use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::error::{expect_tag_no_case, expected};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 1)]
|
||||||
pub struct View {
|
pub struct View {
|
||||||
|
@ -41,7 +43,7 @@ pub fn view(i: &str) -> IResult<&str, View> {
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, expr) = fields(i)?;
|
let (i, expr) = fields(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("FROM")(i)?;
|
let (i, _) = expect_tag_no_case("FROM")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = tables(i)?;
|
let (i, what) = tables(i)?;
|
||||||
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
||||||
|
@ -61,7 +63,8 @@ pub fn view(i: &str) -> IResult<&str, View> {
|
||||||
|
|
||||||
let (i, _) = tag_no_case("AS")(i)?;
|
let (i, _) = tag_no_case("AS")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, (expr, what, cond, group)) = alt((select_view, select_view_delimited))(i)?;
|
let (i, (expr, what, cond, group)) =
|
||||||
|
expected("SELECT or `(`", cut(alt((select_view, select_view_delimited))))(i)?;
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
View {
|
View {
|
||||||
|
|
|
@ -190,9 +190,17 @@ fn excessive_cast_chain_depth() -> Result<(), Error> {
|
||||||
assert_eq!(res.len(), 1);
|
assert_eq!(res.len(), 1);
|
||||||
//
|
//
|
||||||
let tmp = res.next().unwrap();
|
let tmp = res.next().unwrap();
|
||||||
assert!(matches!(tmp, Err(Error::ComputationDepthExceeded)));
|
assert!(
|
||||||
|
matches!(tmp, Err(Error::ComputationDepthExceeded)),
|
||||||
|
"didn't return a computation depth exceeded: {:?}",
|
||||||
|
tmp
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => assert!(matches!(e, Error::ComputationDepthExceeded)),
|
Err(e) => assert!(
|
||||||
|
matches!(e, Error::InvalidQuery(_)),
|
||||||
|
"didn't return a computation depth exceeded: {:?}",
|
||||||
|
e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue