Skip to content

Commit c05e297

Browse files
Taylor-lagrangeMichaelScofield
authored andcommitted
feat: support range query
Signed-off-by: Ruihang Xia <[email protected]>
1 parent 17d5610 commit c05e297

20 files changed

+1566
-335
lines changed

Cargo.toml

+5-5
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]
@@ -43,9 +39,11 @@ recursive-protection = ["std", "recursive"]
4339
# Enable JSON output in the `cli` example:
4440
json_example = ["serde_json", "serde"]
4541
visitor = ["sqlparser_derive"]
42+
bigdecimal-sql = ["bigdecimal", "df_sqlparser/bigdecimal"]
4643

4744
[dependencies]
4845
bigdecimal = { version = "0.4.1", features = ["serde"], optional = true }
46+
df_sqlparser = { package = "sqlparser", version = "0.54.0" }
4947
log = "0.4"
5048
recursive = { version = "0.1.1", optional = true}
5149

@@ -55,6 +53,8 @@ serde = { version = "1.0", features = ["derive"], optional = true }
5553
# https://github.com/rust-lang/cargo/issues/1596
5654
serde_json = { version = "1.0", optional = true }
5755
sqlparser_derive = { version = "0.3.0", path = "derive", optional = true }
56+
regex = "1"
57+
lazy_static = "1.4.0"
5858

5959
[dev-dependencies]
6060
simple_logger = "5.0"

derive/src/lib.rs

+327-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +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, Type, TypePath,
2525
};
26-
use syn::{Path, PathArguments};
26+
use syn::{
27+
AngleBracketedGenericArguments, DataEnum, DataStruct, FieldsNamed, FieldsUnnamed,
28+
GenericArgument, MetaList, Path, PathArguments, PathSegment,
29+
};
2730

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

rust-toolchain

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
stable

0 commit comments

Comments
 (0)