Skip to content

Commit 71dd860

Browse files
Taylor-lagrangewaynexia
authored andcommitted
feat: support range query
Signed-off-by: Ruihang Xia <[email protected]>
1 parent 334a5bf commit 71dd860

File tree

19 files changed

+1513
-289
lines changed

19 files changed

+1513
-289
lines changed

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ documentation = "https://docs.rs/sqlparser/"
2525
keywords = ["ansi", "sql", "lexer", "parser"]
2626
repository = "https://github.com/apache/datafusion-sqlparser-rs"
2727
license = "Apache-2.0"
28-
include = [
29-
"src/**/*.rs",
30-
"Cargo.toml",
31-
"LICENSE.TXT",
32-
]
28+
include = ["src/**/*.rs", "Cargo.toml", "LICENSE.TXT"]
3329
edition = "2021"
3430

3531
[lib]
@@ -41,17 +37,21 @@ default = ["std"]
4137
std = []
4238
# Enable JSON output in the `cli` example:
4339
json_example = ["serde_json", "serde"]
44-
visitor = ["sqlparser_derive"]
40+
visitor = []
41+
bigdecimal-sql = ["bigdecimal", "df_sqlparser/bigdecimal"]
4542

4643
[dependencies]
4744
bigdecimal = { version = "0.4.1", features = ["serde"], optional = true }
45+
df_sqlparser = { package = "sqlparser", version = "0.52.0" }
4846
log = "0.4"
4947
serde = { version = "1.0", features = ["derive"], optional = true }
5048
# serde_json is only used in examples/cli, but we have to put it outside
5149
# of dev-dependencies because of
5250
# https://github.com/rust-lang/cargo/issues/1596
5351
serde_json = { version = "1.0", optional = true }
54-
sqlparser_derive = { version = "0.2.0", path = "derive", optional = true }
52+
sqlparser_derive = { version = "0.2.0", path = "derive" }
53+
regex = "1"
54+
lazy_static = "1.4.0"
5555

5656
[dev-dependencies]
5757
simple_logger = "5.0"

derive/src/lib.rs

Lines changed: 327 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use proc_macro2::TokenStream;
18+
use proc_macro2::{Literal, TokenStream};
1919
use quote::{format_ident, quote, quote_spanned, ToTokens};
2020
use syn::spanned::Spanned;
2121
use syn::{
2222
parse::{Parse, ParseStream},
2323
parse_macro_input, parse_quote, Attribute, Data, DeriveInput, Fields, GenericParam, Generics,
2424
Ident, Index, LitStr, Meta, Token,
2525
};
26+
use syn::{
27+
AngleBracketedGenericArguments, DataEnum, DataStruct, FieldsNamed, FieldsUnnamed,
28+
GenericArgument, MetaList, Path, PathArguments, PathSegment, Type, TypePath,
29+
};
2630

2731
/// Implementation of `[#derive(Visit)]`
2832
#[proc_macro_derive(VisitMut, attributes(visit))]
@@ -256,3 +260,325 @@ fn visit_children(
256260
Data::Union(_) => unimplemented!(),
257261
}
258262
}
263+
264+
/// Determine the variable type to decide which method in the `Convert` trait to use
265+
fn get_var_type(ty: &Type) -> proc_macro2::TokenStream {
266+
let span = ty.span();
267+
if let Type::Path(TypePath {
268+
path: Path { segments, .. },
269+
..
270+
}) = ty
271+
{
272+
if let Some(PathSegment { ident, arguments }) = segments.first() {
273+
return match ident.to_string().as_str() {
274+
"Option" => {
275+
if let PathArguments::AngleBracketed(AngleBracketedGenericArguments {
276+
args,
277+
..
278+
}) = arguments
279+
{
280+
if let Some(GenericArgument::Type(Type::Path(TypePath {
281+
path: Path { segments, .. },
282+
..
283+
}))) = args.first()
284+
{
285+
if let Some(PathSegment { ident, .. }) = segments.first() {
286+
return match ident.to_string().as_str() {
287+
"Box" => quote_spanned!(span => Convert::convert_option_box),
288+
"Vec" => quote_spanned!(span => Convert::convert_option_vec),
289+
_ => quote_spanned!(span => Convert::convert_option),
290+
};
291+
}
292+
}
293+
}
294+
quote_spanned!(span => Convert::convert_option)
295+
}
296+
"Vec" => {
297+
if let PathArguments::AngleBracketed(AngleBracketedGenericArguments {
298+
args,
299+
..
300+
}) = arguments
301+
{
302+
if let Some(GenericArgument::Type(Type::Path(TypePath {
303+
path: Path { segments, .. },
304+
..
305+
}))) = args.first()
306+
{
307+
if let Some(PathSegment { ident, .. }) = segments.first() {
308+
return match ident.to_string().as_str() {
309+
"Vec" => quote_spanned!(span => Convert::convert_matrix),
310+
"Box" => quote_spanned!(span => Convert::convert_vec_box),
311+
_ => quote_spanned!(span => Convert::convert_vec),
312+
};
313+
}
314+
}
315+
}
316+
quote_spanned!(span => Convert::convert_vec)
317+
}
318+
"Box" => quote_spanned!(span => Convert::convert_box),
319+
_ => quote_spanned!(span => Convert::convert),
320+
};
321+
}
322+
}
323+
quote_spanned!(span => Convert::convert)
324+
}
325+
326+
/// Obtain the struct path where `datafusion` `sqlparser` is located from derive macro helper attribute `df_path`,
327+
/// if value not given, the default return is `df_sqlparser::ast`
328+
fn get_crate_path(st: &syn::DeriveInput) -> proc_macro2::TokenStream {
329+
let span = st.span();
330+
for attr in &st.attrs {
331+
let Meta::List(MetaList {
332+
path: Path { segments, .. },
333+
tokens,
334+
..
335+
}) = &attr.meta
336+
else {
337+
continue;
338+
};
339+
if let Some(PathSegment { ident, .. }) = segments.first() {
340+
if ident.to_string().as_str() == "df_path" {
341+
return tokens.clone();
342+
}
343+
}
344+
}
345+
quote_spanned!(span => df_sqlparser::ast)
346+
}
347+
348+
/// Check whether the attribute `ignore_item` exists. If the attribute exists,
349+
/// the corresponding convert method will not be generated.
350+
/// If exist attribute `ignore_item`
351+
/// 1. enum conversion returns panic
352+
/// 2. struct conversion does not generate the corresponding field
353+
fn ignore_convert(attrs: &Vec<Attribute>) -> bool {
354+
for attr in attrs {
355+
let Meta::Path(Path { segments, .. }) = &attr.meta else {
356+
continue;
357+
};
358+
if let Some(PathSegment { ident, .. }) = segments.first() {
359+
if ident.to_string().as_str() == "ignore_item" {
360+
return true;
361+
}
362+
}
363+
}
364+
false
365+
}
366+
367+
fn convert_struct(st: &syn::DeriveInput) -> proc_macro2::TokenStream {
368+
let name = &st.ident;
369+
let path = get_crate_path(st);
370+
// for struct pattern like
371+
// struct xxx {
372+
// xxx: xxx
373+
// }
374+
if let Data::Struct(DataStruct {
375+
fields: Fields::Named(FieldsNamed { named, .. }),
376+
..
377+
}) = &st.data
378+
{
379+
let span = named.span();
380+
let mut fields: Vec<proc_macro2::TokenStream> = Vec::with_capacity(named.len());
381+
for field in named {
382+
if ignore_convert(&field.attrs) {
383+
continue;
384+
}
385+
let field_name = field.ident.clone().unwrap();
386+
let var_type = get_var_type(&field.ty);
387+
let span = field_name.span();
388+
let code = quote_spanned! { span =>
389+
#field_name: #var_type(value.#field_name),
390+
};
391+
fields.push(code);
392+
}
393+
return quote_spanned! { span =>
394+
impl From<#name> for #path::#name {
395+
#[allow(unused_variables)]
396+
fn from(value: #name) -> Self {
397+
Self {
398+
#(#fields)*
399+
}
400+
}
401+
}
402+
};
403+
}
404+
// for struct pattern like
405+
// struct xxx(xxxx);
406+
if let Data::Struct(DataStruct {
407+
fields: Fields::Unnamed(FieldsUnnamed { unnamed, .. }),
408+
..
409+
}) = &st.data
410+
{
411+
let span = unnamed.span();
412+
let mut fields: Vec<proc_macro2::TokenStream> = Vec::with_capacity(unnamed.len());
413+
for i in 0..unnamed.len() {
414+
if ignore_convert(&unnamed[i].attrs) {
415+
continue;
416+
}
417+
let field_name = Literal::usize_unsuffixed(i);
418+
let var_type = get_var_type(&unnamed[i].ty);
419+
let span = unnamed[i].span();
420+
let code = quote_spanned! { span =>
421+
#var_type(value.#field_name),
422+
};
423+
fields.push(code);
424+
}
425+
return quote_spanned! { span =>
426+
impl From<#name> for #path::#name {
427+
#[allow(unused_variables)]
428+
fn from(value: #name) -> Self {
429+
Self(#(#fields)*)
430+
}
431+
}
432+
};
433+
}
434+
panic!("Unrecognised Struct Type{}", st.to_token_stream())
435+
}
436+
437+
fn convert_enum(st: &DeriveInput) -> proc_macro2::TokenStream {
438+
let name = &st.ident;
439+
let path = get_crate_path(st);
440+
if let Data::Enum(DataEnum { variants, .. }) = &st.data {
441+
let span = variants.span();
442+
let mut fields: Vec<proc_macro2::TokenStream> = Vec::with_capacity(variants.len());
443+
for field in variants {
444+
let enum_name = &field.ident;
445+
let span = enum_name.span();
446+
let ignore_convert = ignore_convert(&field.attrs);
447+
// for enum item like xxxxxx(xxx)
448+
if let Fields::Unnamed(FieldsUnnamed { unnamed, .. }) = &field.fields {
449+
let inner_names = ('a'..='z')
450+
.map(|x| Ident::new(x.to_string().as_str(), unnamed.span()))
451+
.collect::<Vec<_>>()[..unnamed.len()]
452+
.to_vec();
453+
let mut codes: Vec<proc_macro2::TokenStream> = Vec::with_capacity(unnamed.len());
454+
let inner_fields: Vec<_> = inner_names.iter().map(|x| quote!(#x,)).collect();
455+
for (inner_name, field) in inner_names.iter().zip(unnamed.iter()) {
456+
let var_type = get_var_type(&field.ty);
457+
let span = field.span();
458+
codes.push(quote_spanned! { span =>
459+
#var_type(#inner_name),
460+
});
461+
}
462+
fields.push(if ignore_convert {
463+
quote_spanned! { span =>
464+
#name::#enum_name(#(#inner_fields)*) => panic!("Convert on this item is ignored"),
465+
}
466+
} else {
467+
quote_spanned! { span =>
468+
#name::#enum_name(#(#inner_fields)*) => Self::#enum_name(#(#codes)*),
469+
}
470+
});
471+
}
472+
// for enum item like
473+
// xxxxxx {
474+
// xxx: xxxx,
475+
// },
476+
if let Fields::Named(FieldsNamed { named, .. }) = &field.fields {
477+
let mut inner_fields: Vec<proc_macro2::TokenStream> =
478+
Vec::with_capacity(named.len());
479+
let mut codes: Vec<proc_macro2::TokenStream> = Vec::with_capacity(named.len());
480+
let span = named.span();
481+
for field in named {
482+
let field_name = field.ident.clone().unwrap();
483+
let span = field_name.span();
484+
let var_type = get_var_type(&field.ty);
485+
inner_fields.push(quote_spanned!(span => #field_name,));
486+
codes.push(quote_spanned! { span =>
487+
#field_name: #var_type(#field_name),
488+
});
489+
}
490+
fields.push(if ignore_convert {
491+
quote_spanned! { span =>
492+
#name::#enum_name{#(#inner_fields)*} => panic!("Convert on this item is ignored"),
493+
}
494+
} else {
495+
quote_spanned! { span =>
496+
#name::#enum_name{#(#inner_fields)*} => Self::#enum_name{#(#codes)*},
497+
}
498+
});
499+
}
500+
// for enum item like
501+
// xxxxxx
502+
if let Fields::Unit = &field.fields {
503+
let span = field.span();
504+
fields.push(if ignore_convert {
505+
quote_spanned! { span =>
506+
#name::#enum_name => panic!("Convert on this item is ignored"),
507+
}
508+
} else {
509+
quote_spanned! { span =>
510+
#name::#enum_name => Self::#enum_name,
511+
}
512+
});
513+
}
514+
}
515+
return quote_spanned! { span =>
516+
impl From<#name> for #path::#name {
517+
#[allow(unused_variables)]
518+
fn from(value: #name) -> Self {
519+
match value{
520+
#(#fields)*
521+
}
522+
}
523+
}
524+
};
525+
}
526+
panic!("Unrecognised Enum Type{}", st.to_token_stream())
527+
}
528+
529+
fn convert_union(st: &DeriveInput) -> proc_macro2::TokenStream {
530+
let name = &st.ident;
531+
let path = get_crate_path(st);
532+
533+
if let Data::Union(data_union) = &st.data {
534+
let span = data_union.fields.span();
535+
let mut fields: Vec<proc_macro2::TokenStream> =
536+
Vec::with_capacity(data_union.fields.named.len());
537+
538+
for field in &data_union.fields.named {
539+
if ignore_convert(&field.attrs) {
540+
continue;
541+
}
542+
let field_name = field.ident.clone().unwrap();
543+
let var_type = get_var_type(&field.ty);
544+
let span = field_name.span();
545+
let code = quote_spanned! { span =>
546+
#field_name: unsafe { #var_type(value.#field_name) },
547+
};
548+
fields.push(code);
549+
}
550+
551+
quote_spanned! { span =>
552+
impl From<#name> for #path::#name {
553+
#[allow(unused_variables)]
554+
fn from(value: #name) -> Self {
555+
unsafe {
556+
Self {
557+
#(#fields)*
558+
}
559+
}
560+
}
561+
}
562+
}
563+
} else {
564+
panic!("Expected Union type")
565+
}
566+
}
567+
568+
fn expand_df_convert(st: &DeriveInput) -> proc_macro2::TokenStream {
569+
match st.data {
570+
syn::Data::Struct(_) => convert_struct(st),
571+
syn::Data::Enum(_) => convert_enum(st),
572+
syn::Data::Union(_) => convert_union(st),
573+
}
574+
}
575+
576+
/// Derive macro to implement `From` Trait. Convert the current sqlparser struct to the struct used by datafusion sqlparser.
577+
/// There are two helper attributes that can be marked on the derive struct/enum, affecting the generated Convert function
578+
/// 1. `#[df_path(....)]`: Most structures are defined in `df_sqlparser::ast`, if the path of some structures is not in this path,
579+
/// user need to specify `df_path` to tell the compiler the location of this struct/enum
580+
/// 2. `#[ignore_item]`: Marked on the field of the struct/enum, indicating that the Convert method of the field of the struct/enum is not generated·
581+
#[proc_macro_derive(DFConvert, attributes(df_path, ignore_item))]
582+
pub fn derive_df_convert(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
583+
expand_df_convert(&parse_macro_input!(input as DeriveInput)).into()
584+
}

rust-toolchain

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
stable

0 commit comments

Comments
 (0)