Skip to content

Commit b192e82

Browse files
committed
0 parents  commit b192e82

15 files changed

+1598
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
/target/
3+
**/*.rs.bk

Cargo.lock

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "rust-issue-47364"
3+
version = "0.1.0"
4+
authors = ["Jon Gjengset <[email protected]>"]
5+
6+
[dependencies]
7+
nom_sql = { path = "nom-sql/" }

nom-sql/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Generated by Cargo
2+
# will have compiled files and executables
3+
/target/
4+
5+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6+
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
7+
Cargo.lock

nom-sql/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "nom_sql"
3+
version = "0.0.1"
4+
authors = ["Malte Schwarzkopf <[email protected]>"]
5+
6+
[dependencies]
7+
nom = "^1.2.4"
8+
serde = "1.0"
9+
serde_derive = "1.0"

nom-sql/src/caseless_tag.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/// Case-insensitive tag! variant for nom
2+
///
3+
/// By Paul English (https://gist.github.com/log0ymxm/d9c3fc9598cf2d92b8ae89a9ce5341d8)
4+
///
5+
/// This implementation has some performance issues related to extra memory allocations
6+
/// (see https://github.com/Geal/nom/issues/251), but it works for the moment.
7+
8+
macro_rules! caseless_tag (
9+
($i:expr, $inp: expr) => (
10+
{
11+
#[inline(always)]
12+
fn as_lower(b: &str) -> String {
13+
let s = b.to_string();
14+
s.to_lowercase()
15+
}
16+
17+
let expected = $inp;
18+
let lower = as_lower(&expected);
19+
let bytes = lower.as_bytes();
20+
21+
caseless_tag_bytes!($i,bytes)
22+
}
23+
);
24+
);
25+
26+
macro_rules! caseless_tag_bytes (
27+
($i:expr, $bytes: expr) => (
28+
{
29+
use std::cmp::min;
30+
let len = $i.len();
31+
let blen = $bytes.len();
32+
let m = min(len, blen);
33+
let reduced = &$i[..m];
34+
35+
let s = str::from_utf8(reduced).unwrap();
36+
let s2 = s.to_string();
37+
let lowered = s2.to_lowercase();
38+
let lowered_bytes = lowered.as_bytes();
39+
40+
let b = &$bytes[..m];
41+
42+
let res: IResult<_,_> = if lowered_bytes != b {
43+
IResult::Error(Err::Position(ErrorKind::Tag, $i))
44+
} else if m < blen {
45+
IResult::Incomplete(Needed::Size(blen))
46+
} else {
47+
IResult::Done(&$i[blen..], reduced)
48+
};
49+
res
50+
}
51+
);
52+
);
53+
54+
#[test]
55+
fn test_caseless_tag() {
56+
use nom::{Err, ErrorKind, IResult, Needed};
57+
use std::str;
58+
59+
named!(x, caseless_tag!("AbCD"));
60+
let r = x(&b"abcdefGH"[..]);
61+
assert_eq!(r, IResult::Done(&b"efGH"[..], &b"abcd"[..]));
62+
let r = x(&b"aBcdefGH"[..]);
63+
assert_eq!(r, IResult::Done(&b"efGH"[..], &b"aBcd"[..]));
64+
}

nom-sql/src/column.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
use std::cmp::Ordering;
2+
use std::fmt::{self, Display};
3+
use std::str;
4+
5+
use common::{Literal, SqlType};
6+
7+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
8+
pub enum FunctionExpression {
9+
Avg(Column, bool),
10+
Count(Column, bool),
11+
CountStar,
12+
Sum(Column, bool),
13+
Max(Column),
14+
Min(Column),
15+
GroupConcat(Column, String),
16+
}
17+
18+
impl Display for FunctionExpression {
19+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20+
match *self {
21+
FunctionExpression::Avg(ref col, d) if d => {
22+
write!(f, "avg(distinct {})", col.name.as_str())
23+
}
24+
FunctionExpression::Count(ref col, d) if d => {
25+
write!(f, "count(distinct {})", col.name.as_str())
26+
}
27+
FunctionExpression::Sum(ref col, d) if d => {
28+
write!(f, "sum(distinct {})", col.name.as_str())
29+
}
30+
31+
FunctionExpression::Avg(ref col, _) => write!(f, "avg({})", col.name.as_str()),
32+
FunctionExpression::Count(ref col, _) => write!(f, "count({})", col.name.as_str()),
33+
FunctionExpression::CountStar => write!(f, "count(all)"),
34+
FunctionExpression::Sum(ref col, _) => write!(f, "sum({})", col.name.as_str()),
35+
FunctionExpression::Max(ref col) => write!(f, "max({})", col.name.as_str()),
36+
FunctionExpression::Min(ref col) => write!(f, "min({})", col.name.as_str()),
37+
FunctionExpression::GroupConcat(ref col, ref s) => {
38+
write!(f, "group_concat({}, {})", col.name.as_str(), s)
39+
}
40+
}
41+
}
42+
}
43+
44+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
45+
pub struct Column {
46+
pub name: String,
47+
pub alias: Option<String>,
48+
pub table: Option<String>,
49+
pub function: Option<Box<FunctionExpression>>,
50+
}
51+
52+
impl fmt::Display for Column {
53+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54+
if let Some(ref table) = self.table {
55+
write!(f, "{}.{}", table, self.name)?;
56+
} else {
57+
write!(f, "{}", self.name)?;
58+
}
59+
if let Some(ref alias) = self.alias {
60+
write!(f, " AS {}", alias)?;
61+
}
62+
Ok(())
63+
}
64+
}
65+
66+
impl<'a> From<&'a str> for Column {
67+
fn from(c: &str) -> Column {
68+
match c.find(".") {
69+
None => Column {
70+
name: String::from(c),
71+
alias: None,
72+
table: None,
73+
function: None,
74+
},
75+
Some(i) => Column {
76+
name: String::from(&c[i + 1..]),
77+
alias: None,
78+
table: Some(String::from(&c[0..i])),
79+
function: None,
80+
},
81+
}
82+
}
83+
}
84+
85+
impl Ord for Column {
86+
fn cmp(&self, other: &Column) -> Ordering {
87+
if self.table.is_some() && other.table.is_some() {
88+
match self.table.cmp(&other.table) {
89+
Ordering::Equal => self.name.cmp(&other.name),
90+
x => x,
91+
}
92+
} else {
93+
self.name.cmp(&other.name)
94+
}
95+
}
96+
}
97+
98+
impl PartialOrd for Column {
99+
fn partial_cmp(&self, other: &Column) -> Option<Ordering> {
100+
if self.table.is_some() && other.table.is_some() {
101+
match self.table.cmp(&other.table) {
102+
Ordering::Equal => Some(self.name.cmp(&other.name)),
103+
x => Some(x),
104+
}
105+
} else if self.table.is_none() && other.table.is_none() {
106+
Some(self.name.cmp(&other.name))
107+
} else {
108+
None
109+
}
110+
}
111+
}
112+
113+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
114+
pub enum ColumnConstraint {
115+
NotNull,
116+
DefaultValue(Literal),
117+
AutoIncrement,
118+
}
119+
120+
impl fmt::Display for ColumnConstraint {
121+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122+
match *self {
123+
ColumnConstraint::NotNull => write!(f, "NOT NULL"),
124+
ColumnConstraint::DefaultValue(ref literal) => {
125+
write!(f, "DEFAULT {}", literal.to_string())
126+
}
127+
ColumnConstraint::AutoIncrement => write!(f, "AUTOINCREMENT"),
128+
}
129+
}
130+
}
131+
132+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
133+
pub struct ColumnSpecification {
134+
pub column: Column,
135+
pub sql_type: SqlType,
136+
pub constraints: Vec<ColumnConstraint>,
137+
}
138+
139+
impl fmt::Display for ColumnSpecification {
140+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141+
write!(f, "{} {}", self.column, self.sql_type)?;
142+
for constraint in self.constraints.iter() {
143+
write!(f, " {}", constraint)?;
144+
}
145+
Ok(())
146+
}
147+
}
148+
149+
impl ColumnSpecification {
150+
pub fn new(c: Column, t: SqlType) -> ColumnSpecification {
151+
ColumnSpecification {
152+
column: c,
153+
sql_type: t,
154+
constraints: vec![],
155+
}
156+
}
157+
158+
pub fn with_constraints(
159+
c: Column,
160+
t: SqlType,
161+
ccs: Vec<ColumnConstraint>,
162+
) -> ColumnSpecification {
163+
ColumnSpecification {
164+
column: c,
165+
sql_type: t,
166+
constraints: ccs,
167+
}
168+
}
169+
}
170+
171+
#[cfg(test)]
172+
mod tests {
173+
use super::*;
174+
175+
#[test]
176+
fn column_from_str() {
177+
let s = "table.col";
178+
let c = Column::from(s);
179+
180+
assert_eq!(
181+
c,
182+
Column {
183+
name: String::from("col"),
184+
alias: None,
185+
table: Some(String::from("table")),
186+
function: None,
187+
}
188+
);
189+
}
190+
}

0 commit comments

Comments
 (0)