1use super::{DialectImpl, DialectType};
13use crate::error::Result;
14use crate::expressions::{
15 Alias, BinaryOp, CeilFunc, Column, Exists, Expression, From, Function, FunctionBody,
16 Identifier, JsonExtractFunc, LikeOp, Literal, Select, SplitFunc, StringAggFunc, UnaryFunc,
17 UnnestFunc, VarArgFunc, Where,
18};
19#[cfg(feature = "generate")]
20use crate::generator::GeneratorConfig;
21use crate::tokens::TokenizerConfig;
22
23pub struct BigQueryDialect;
25
26impl DialectImpl for BigQueryDialect {
27 fn dialect_type(&self) -> DialectType {
28 DialectType::BigQuery
29 }
30
31 fn tokenizer_config(&self) -> TokenizerConfig {
32 let mut config = TokenizerConfig::default();
33 config.identifiers.remove(&'"');
36 config.identifiers.insert('`', '`');
37 config.identifier_backslash_escapes = true;
38 config.quotes.insert("\"".to_string(), "\"".to_string());
40 config.quotes.insert("'''".to_string(), "'''".to_string());
42 config
43 .quotes
44 .insert("\"\"\"".to_string(), "\"\"\"".to_string());
45 config.string_escapes = vec!['\'', '\\'];
47 config.b_prefix_is_byte_string = true;
49 config.hex_number_strings = true;
51 config.hex_string_is_integer_type = true;
53 config.hash_comments = true;
55 config
56 }
57
58 #[cfg(feature = "generate")]
59
60 fn generator_config(&self) -> GeneratorConfig {
61 use crate::generator::{IdentifierQuoteStyle, NormalizeFunctions};
62 GeneratorConfig {
63 identifier_quote: '`',
64 identifier_quote_style: IdentifierQuoteStyle::BACKTICK,
65 dialect: Some(DialectType::BigQuery),
66 normalize_functions: NormalizeFunctions::None,
68 interval_allows_plural_form: false,
70 join_hints: false,
71 query_hints: false,
72 table_hints: false,
73 limit_fetch_style: crate::generator::LimitFetchStyle::Limit,
74 rename_table_with_db: false,
75 nvl2_supported: false,
76 unnest_with_ordinality: false,
77 collate_is_func: true,
78 limit_only_literals: true,
79 supports_table_alias_columns: false,
80 unpivot_aliases_are_identifiers: false,
81 json_key_value_pair_sep: ",",
82 null_ordering_supported: false,
83 ignore_nulls_in_func: true,
84 json_path_single_quote_escape: true,
85 can_implement_array_any: true,
86 supports_to_number: false,
87 named_placeholder_token: "@",
88 hex_func: "TO_HEX",
89 with_properties_prefix: "OPTIONS",
90 supports_exploding_projections: false,
91 except_intersect_support_all_clause: false,
92 supports_unix_seconds: true,
93 try_supported: true,
95 semi_anti_join_with_side: false,
97 ..Default::default()
98 }
99 }
100
101 #[cfg(feature = "transpile")]
102
103 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
104 match expr {
105 Expression::DataType(dt) => self.transform_data_type(dt),
107
108 Expression::Table(mut table)
109 if table.catalog.is_none()
110 && table.alias.is_none()
111 && table.schema.as_ref().map_or(false, |schema| {
112 schema.quoted && schema.name.contains("INFORMATION_SCHEMA")
113 }) =>
114 {
115 let schema = table.schema.take().expect("schema checked above");
116 let old_name = table.name.clone();
117 let alias = old_name.name.clone();
118 table.name = Identifier {
119 name: format!("{}.{}", schema.name, old_name.name),
120 quoted: true,
121 trailing_comments: old_name.trailing_comments,
122 span: old_name.span,
123 };
124 table.alias = Some(Identifier::new(alias));
125 Ok(Expression::Table(table))
126 }
127
128 Expression::IfNull(f) => Ok(Expression::IfNull(f)),
131
132 Expression::Nvl(f) => Ok(Expression::IfNull(f)),
134
135 Expression::Coalesce(f) => Ok(Expression::Coalesce(f)),
137
138 Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
141 this: f.this,
142 separator: f.separator,
143 order_by: f.order_by,
144 distinct: f.distinct,
145 filter: f.filter,
146 limit: None,
147 inferred_type: None,
148 }))),
149
150 Expression::TryCast(c) => {
156 let transformed_type = match self.transform_data_type(c.to)? {
157 Expression::DataType(dt) => dt,
158 _ => return Err(crate::error::Error::parse("Expected DataType", 0, 0, 0, 0)),
159 };
160 Ok(Expression::SafeCast(Box::new(crate::expressions::Cast {
161 this: c.this,
162 to: transformed_type,
163 trailing_comments: c.trailing_comments,
164 double_colon_syntax: c.double_colon_syntax,
165 format: c.format,
166 default: c.default,
167 inferred_type: None,
168 })))
169 }
170
171 Expression::ILike(op) => {
174 let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
175 let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
176 Ok(Expression::Like(Box::new(LikeOp {
177 left: lower_left,
178 right: lower_right,
179 escape: op.escape,
180 quantifier: op.quantifier,
181 inferred_type: None,
182 })))
183 }
184
185 Expression::RegexpLike(f) => Ok(Expression::Function(Box::new(Function::new(
187 "REGEXP_CONTAINS".to_string(),
188 vec![f.this, f.pattern],
189 )))),
190
191 Expression::Explode(f) => Ok(Expression::Unnest(Box::new(
194 crate::expressions::UnnestFunc {
195 this: f.this,
196 expressions: Vec::new(),
197 with_ordinality: false,
198 alias: None,
199 offset_alias: None,
200 inferred_type: None,
201 },
202 ))),
203
204 Expression::ExplodeOuter(f) => Ok(Expression::Unnest(Box::new(
206 crate::expressions::UnnestFunc {
207 this: f.this,
208 expressions: Vec::new(),
209 with_ordinality: false,
210 alias: None,
211 offset_alias: None,
212 inferred_type: None,
213 },
214 ))),
215
216 Expression::GenerateSeries(f) => {
218 let mut args = Vec::new();
219 if let Some(start) = f.start {
220 args.push(*start);
221 }
222 if let Some(end) = f.end {
223 args.push(*end);
224 }
225 if let Some(step) = f.step {
226 args.push(*step);
227 }
228 Ok(Expression::Function(Box::new(Function::new(
229 "GENERATE_ARRAY".to_string(),
230 args,
231 ))))
232 }
233
234 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
237 "BIT_AND".to_string(),
238 vec![f.this],
239 )))),
240
241 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
243 "BIT_OR".to_string(),
244 vec![f.this],
245 )))),
246
247 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
249 "BIT_XOR".to_string(),
250 vec![f.this],
251 )))),
252
253 Expression::BitwiseCount(f) => Ok(Expression::Function(Box::new(Function::new(
255 "BIT_COUNT".to_string(),
256 vec![f.this],
257 )))),
258
259 Expression::ByteLength(f) => Ok(Expression::Function(Box::new(Function::new(
261 "BYTE_LENGTH".to_string(),
262 vec![f.this],
263 )))),
264
265 Expression::IntDiv(f) => Ok(Expression::Function(Box::new(Function::new(
267 "DIV".to_string(),
268 vec![f.this, f.expression],
269 )))),
270
271 Expression::Int64(f) => Ok(Expression::Function(Box::new(Function::new(
273 "INT64".to_string(),
274 vec![f.this],
275 )))),
276
277 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
280 seed: None,
281 lower: None,
282 upper: None,
283 }))),
284
285 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
288 "GENERATE_UUID".to_string(),
289 vec![],
290 )))),
291
292 Expression::ApproxDistinct(f) => Ok(Expression::Function(Box::new(Function::new(
295 "APPROX_COUNT_DISTINCT".to_string(),
296 vec![f.this],
297 )))),
298
299 Expression::ArgMax(f) => Ok(Expression::Function(Box::new(Function::new(
301 "MAX_BY".to_string(),
302 vec![*f.this, *f.expression],
303 )))),
304
305 Expression::ArgMin(f) => Ok(Expression::Function(Box::new(Function::new(
307 "MIN_BY".to_string(),
308 vec![*f.this, *f.expression],
309 )))),
310
311 Expression::CountIf(f) => Ok(Expression::Function(Box::new(Function::new(
314 "COUNTIF".to_string(),
315 vec![f.this],
316 )))),
317
318 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
321
322 Expression::Unhex(f) => Ok(Expression::Function(Box::new(Function::new(
325 "FROM_HEX".to_string(),
326 vec![*f.this],
327 )))),
328
329 Expression::UnixToTime(f) => {
331 let scale = f.scale.unwrap_or(0);
332 match scale {
333 0 => Ok(Expression::Function(Box::new(Function::new(
334 "TIMESTAMP_SECONDS".to_string(),
335 vec![*f.this],
336 )))),
337 3 => Ok(Expression::Function(Box::new(Function::new(
338 "TIMESTAMP_MILLIS".to_string(),
339 vec![*f.this],
340 )))),
341 6 => Ok(Expression::Function(Box::new(Function::new(
342 "TIMESTAMP_MICROS".to_string(),
343 vec![*f.this],
344 )))),
345 _ => {
346 let div_expr =
348 Expression::Div(Box::new(crate::expressions::BinaryOp::new(
349 *f.this,
350 Expression::Function(Box::new(Function::new(
351 "POWER".to_string(),
352 vec![Expression::number(10), Expression::number(scale)],
353 ))),
354 )));
355 let cast_expr = Expression::Cast(Box::new(crate::expressions::Cast {
356 this: div_expr,
357 to: crate::expressions::DataType::Custom {
358 name: "INT64".to_string(),
359 },
360 double_colon_syntax: false,
361 trailing_comments: vec![],
362 format: None,
363 default: None,
364 inferred_type: None,
365 }));
366 Ok(Expression::Function(Box::new(Function::new(
367 "TIMESTAMP_SECONDS".to_string(),
368 vec![cast_expr],
369 ))))
370 }
371 }
372 }
373
374 Expression::DateDiff(f) => {
377 let unit_str = match f.unit {
379 Some(crate::expressions::IntervalUnit::Year) => "YEAR",
380 Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
381 Some(crate::expressions::IntervalUnit::Month) => "MONTH",
382 Some(crate::expressions::IntervalUnit::Week) => "WEEK",
383 Some(crate::expressions::IntervalUnit::Day) => "DAY",
384 Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
385 Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
386 Some(crate::expressions::IntervalUnit::Second) => "SECOND",
387 Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
388 Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
389 Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
390 None => "DAY",
391 };
392 let unit = Expression::Identifier(crate::expressions::Identifier {
393 name: unit_str.to_string(),
394 quoted: false,
395 trailing_comments: Vec::new(),
396 span: None,
397 });
398 Ok(Expression::Function(Box::new(Function::new(
399 "DATE_DIFF".to_string(),
400 vec![f.this, f.expression, unit],
401 ))))
402 }
403
404 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
407 "VAR_POP".to_string(),
408 vec![f.this],
409 )))),
410
411 Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
414 "SHA1".to_string(),
415 vec![f.this],
416 )))),
417
418 Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
420 "SHA1".to_string(),
421 vec![f.this],
422 )))),
423
424 Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
426 "MD5".to_string(),
427 vec![*f.this],
428 )))),
429
430 Expression::JSONBool(f) => Ok(Expression::Function(Box::new(Function::new(
433 "BOOL".to_string(),
434 vec![f.this],
435 )))),
436
437 Expression::StringFunc(f) => Ok(Expression::Function(Box::new(Function::new(
439 "STRING".to_string(),
440 vec![*f.this],
441 )))),
442
443 Expression::DateFromUnixDate(f) => Ok(Expression::Function(Box::new(Function::new(
446 "DATE_FROM_UNIX_DATE".to_string(),
447 vec![f.this],
448 )))),
449
450 Expression::UnixDate(f) => Ok(Expression::Function(Box::new(Function::new(
452 "UNIX_DATE".to_string(),
453 vec![f.this],
454 )))),
455
456 Expression::TimestampDiff(f) => Ok(Expression::Function(Box::new(Function::new(
458 "TIMESTAMP_DIFF".to_string(),
459 vec![*f.this, *f.expression],
460 )))),
461
462 Expression::FromTimeZone(f) => Ok(Expression::Function(Box::new(Function::new(
464 "DATETIME".to_string(),
465 vec![*f.this],
466 )))),
467
468 Expression::TsOrDsToDatetime(f) => Ok(Expression::Function(Box::new(Function::new(
470 "DATETIME".to_string(),
471 vec![f.this],
472 )))),
473
474 Expression::TsOrDsToTimestamp(f) => Ok(Expression::Function(Box::new(Function::new(
476 "TIMESTAMP".to_string(),
477 vec![f.this],
478 )))),
479
480 Expression::Timestamp(f) => {
483 let mut args = Vec::new();
484 if let Some(this) = f.this {
485 args.push(*this);
486 }
487 if let Some(zone) = f.zone {
488 args.push(*zone);
489 }
490 Ok(Expression::Function(Box::new(Function::new(
491 "TIMESTAMP".to_string(),
492 args,
493 ))))
494 }
495
496 Expression::Round(f) => {
497 let mut args = vec![f.this];
498 if let Some(decimals) = f.decimals {
499 args.push(decimals);
500 }
501 Ok(Expression::Function(Box::new(Function::new(
502 "ROUND".to_string(),
503 args,
504 ))))
505 }
506
507 Expression::IfFunc(f) => {
509 let mut args = vec![f.condition, f.true_value];
510 if let Some(false_val) = f.false_value {
511 args.push(false_val);
512 } else {
513 args.push(Expression::Null(crate::expressions::Null));
514 }
515 Ok(Expression::Function(Box::new(Function::new(
516 "IF".to_string(),
517 args,
518 ))))
519 }
520
521 Expression::HexStringExpr(f) => Ok(Expression::Function(Box::new(Function::new(
523 "FROM_HEX".to_string(),
524 vec![*f.this],
525 )))),
526
527 Expression::ApproxTopK(f) => {
530 let mut args = vec![*f.this];
531 if let Some(expr) = f.expression {
532 args.push(*expr);
533 }
534 Ok(Expression::Function(Box::new(Function::new(
535 "APPROX_TOP_COUNT".to_string(),
536 args,
537 ))))
538 }
539
540 Expression::SafeDivide(f) => Ok(Expression::Function(Box::new(Function::new(
542 "SAFE_DIVIDE".to_string(),
543 vec![*f.this, *f.expression],
544 )))),
545
546 Expression::JSONKeysAtDepth(f) => Ok(Expression::Function(Box::new(Function::new(
548 "JSON_KEYS".to_string(),
549 vec![*f.this],
550 )))),
551
552 Expression::JSONValueArray(f) => Ok(Expression::Function(Box::new(Function::new(
554 "JSON_VALUE_ARRAY".to_string(),
555 vec![*f.this],
556 )))),
557
558 Expression::DateFromParts(f) => {
560 let mut args = Vec::new();
561 if let Some(y) = f.year {
562 args.push(*y);
563 }
564 if let Some(m) = f.month {
565 args.push(*m);
566 }
567 if let Some(d) = f.day {
568 args.push(*d);
569 }
570 Ok(Expression::Function(Box::new(Function::new(
571 "DATE".to_string(),
572 args,
573 ))))
574 }
575
576 Expression::Split(f) => {
579 let delimiter = match &f.delimiter {
581 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(s) if s.is_empty()) =>
582 {
583 let Literal::String(_) = lit.as_ref() else {
584 unreachable!()
585 };
586 Expression::Literal(Box::new(Literal::String(",".to_string())))
587 }
588 _ => f.delimiter,
589 };
590 Ok(Expression::Split(Box::new(SplitFunc {
591 this: f.this,
592 delimiter,
593 })))
594 }
595
596 Expression::Cast(c) => {
600 use crate::expressions::DataType;
601 let is_json = matches!(c.to, DataType::Json | DataType::JsonB)
603 || matches!(&c.to, DataType::Custom { name } if name.eq_ignore_ascii_case("JSON") || name.eq_ignore_ascii_case("JSONB"));
604 if is_json {
605 return Ok(Expression::ParseJson(Box::new(UnaryFunc::new(c.this))));
606 }
607 if c.format.is_some() {
609 let is_temporal = matches!(
610 c.to,
611 DataType::Date | DataType::Timestamp { .. } | DataType::Time { .. }
612 ) || matches!(&c.to, DataType::Custom { name } if
613 name.eq_ignore_ascii_case("TIMESTAMP") ||
614 name.eq_ignore_ascii_case("DATE") ||
615 name.eq_ignore_ascii_case("DATETIME") ||
616 name.eq_ignore_ascii_case("TIME")
617 );
618 if is_temporal {
619 let format_expr = c.format.as_ref().unwrap().as_ref();
620 let (actual_format, timezone) = match format_expr {
622 Expression::AtTimeZone(ref atz) => {
623 (atz.this.clone(), Some(atz.zone.clone()))
624 }
625 _ => (format_expr.clone(), None),
626 };
627 let strftime_fmt = Self::bq_cast_format_to_strftime(&actual_format);
628 let func_name = match &c.to {
629 DataType::Date => "PARSE_DATE",
630 DataType::Custom { name } if name.eq_ignore_ascii_case("DATE") => {
631 "PARSE_DATE"
632 }
633 DataType::Custom { name } if name.eq_ignore_ascii_case("DATETIME") => {
634 "PARSE_DATETIME"
635 }
636 _ => "PARSE_TIMESTAMP",
637 };
638 let mut func_args = vec![strftime_fmt, c.this];
639 if let Some(tz) = timezone {
640 func_args.push(tz);
641 }
642 return Ok(Expression::Function(Box::new(Function::new(
643 func_name.to_string(),
644 func_args,
645 ))));
646 }
647 }
648 let transformed_type = match self.transform_data_type(c.to)? {
649 Expression::DataType(dt) => dt,
650 _ => return Err(crate::error::Error::parse("Expected DataType", 0, 0, 0, 0)),
651 };
652 Ok(Expression::Cast(Box::new(crate::expressions::Cast {
653 this: c.this,
654 to: transformed_type,
655 trailing_comments: c.trailing_comments,
656 double_colon_syntax: c.double_colon_syntax,
657 format: c.format,
658 default: c.default,
659 inferred_type: None,
660 })))
661 }
662
663 Expression::SafeCast(c) => {
665 let transformed_type = match self.transform_data_type(c.to)? {
666 Expression::DataType(dt) => dt,
667 _ => return Err(crate::error::Error::parse("Expected DataType", 0, 0, 0, 0)),
668 };
669 Ok(Expression::SafeCast(Box::new(crate::expressions::Cast {
670 this: c.this,
671 to: transformed_type,
672 trailing_comments: c.trailing_comments,
673 double_colon_syntax: c.double_colon_syntax,
674 format: c.format,
675 default: c.default,
676 inferred_type: None,
677 })))
678 }
679
680 Expression::Select(mut select) => {
683 if select.group_by.is_some() && select.order_by.is_some() {
684 let aliases: Vec<(Expression, Identifier)> = select
686 .expressions
687 .iter()
688 .filter_map(|e| {
689 if let Expression::Alias(a) = e {
690 Some((a.this.clone(), a.alias.clone()))
691 } else {
692 None
693 }
694 })
695 .collect();
696
697 if let Some(ref mut group_by) = select.group_by {
698 for grouped in group_by.expressions.iter_mut() {
699 if matches!(grouped, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)))
701 {
702 continue;
703 }
704 for (expr, alias_ident) in &aliases {
706 if grouped == expr {
707 *grouped = Expression::boxed_column(Column {
708 name: alias_ident.clone(),
709 table: None,
710 join_mark: false,
711 trailing_comments: Vec::new(),
712 span: None,
713 inferred_type: None,
714 });
715 break;
716 }
717 }
718 }
719 }
720 }
721 Ok(Expression::Select(select))
722 }
723
724 Expression::ArrayContains(f) => {
726 let array_expr = f.this;
727 let value_expr = f.expression;
728
729 let unnest = Expression::Unnest(Box::new(UnnestFunc {
731 this: array_expr,
732 expressions: Vec::new(),
733 with_ordinality: false,
734 alias: None,
735 offset_alias: None,
736 inferred_type: None,
737 }));
738 let aliased_unnest = Expression::Alias(Box::new(Alias {
739 this: unnest,
740 alias: Identifier::new("_col"),
741 column_aliases: Vec::new(),
742 alias_explicit_as: false,
743 alias_keyword: None,
744 pre_alias_comments: Vec::new(),
745 trailing_comments: Vec::new(),
746 inferred_type: None,
747 }));
748 let col_ref = Expression::boxed_column(Column {
749 name: Identifier::new("_col"),
750 table: None,
751 join_mark: false,
752 trailing_comments: Vec::new(),
753 span: None,
754 inferred_type: None,
755 });
756 let where_clause = Where {
757 this: Expression::Eq(Box::new(BinaryOp {
758 left: col_ref,
759 right: value_expr,
760 left_comments: Vec::new(),
761 operator_comments: Vec::new(),
762 trailing_comments: Vec::new(),
763 inferred_type: None,
764 })),
765 };
766 let inner_select = Expression::Select(Box::new(Select {
767 vertica: None,
768 expressions: vec![Expression::Literal(Box::new(Literal::Number(
769 "1".to_string(),
770 )))],
771 from: Some(From {
772 expressions: vec![aliased_unnest],
773 }),
774 where_clause: Some(where_clause),
775 ..Default::default()
776 }));
777 Ok(Expression::Exists(Box::new(Exists {
778 this: inner_select,
779 not: false,
780 })))
781 }
782
783 Expression::JsonObject(mut f) => {
786 if f.pairs.len() == 1 {
787 let keys_exprs = match &f.pairs[0].0 {
789 Expression::Array(arr) => Some(&arr.expressions),
790 Expression::ArrayFunc(arr) => Some(&arr.expressions),
791 _ => None,
792 };
793 let vals_exprs = match &f.pairs[0].1 {
794 Expression::Array(arr) => Some(&arr.expressions),
795 Expression::ArrayFunc(arr) => Some(&arr.expressions),
796 _ => None,
797 };
798 if let (Some(keys), Some(vals)) = (keys_exprs, vals_exprs) {
799 if keys.len() == vals.len() {
800 let new_pairs: Vec<(Expression, Expression)> = keys
801 .iter()
802 .zip(vals.iter())
803 .map(|(k, v)| (k.clone(), v.clone()))
804 .collect();
805 f.pairs = new_pairs;
806 }
807 }
808 }
809 Ok(Expression::JsonObject(f))
810 }
811
812 Expression::ModFunc(mut f) => {
815 if let Expression::Paren(paren) = f.this {
817 f.this = paren.this;
818 }
819 Ok(Expression::ModFunc(f))
820 }
821
822 Expression::JSONExtract(e) if e.variant_extract.is_some() => {
824 let path = match *e.expression {
825 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
826 let Literal::String(s) = lit.as_ref() else {
827 unreachable!()
828 };
829 let normalized = if s.starts_with('$') {
830 s.clone()
831 } else if s.starts_with('[') {
832 format!("${}", s)
833 } else {
834 format!("$.{}", s)
835 };
836 Expression::Literal(Box::new(Literal::String(normalized)))
837 }
838 other => other,
839 };
840 Ok(Expression::Function(Box::new(Function::new(
841 "JSON_EXTRACT".to_string(),
842 vec![*e.this, path],
843 ))))
844 }
845
846 Expression::Function(f) => self.transform_function(*f),
848
849 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
851
852 Expression::MethodCall(mc) => self.transform_method_call(*mc),
855
856 Expression::CreateFunction(mut cf) => {
859 if let Some(ref mut rtb) = cf.returns_table_body {
860 if rtb.starts_with("TABLE (") || rtb.starts_with("TABLE(") {
861 let inner = if rtb.starts_with("TABLE (") {
863 &rtb["TABLE (".len()..rtb.len() - 1]
864 } else {
865 &rtb["TABLE(".len()..rtb.len() - 1]
866 };
867 let converted = inner
869 .replace(" INT,", " INT64,")
870 .replace(" INT)", " INT64)")
871 .replace(" INTEGER,", " INT64,")
872 .replace(" INTEGER)", " INT64)")
873 .replace(" FLOAT,", " FLOAT64,")
874 .replace(" FLOAT)", " FLOAT64)")
875 .replace(" BOOLEAN,", " BOOL,")
876 .replace(" BOOLEAN)", " BOOL)")
877 .replace(" VARCHAR", " STRING")
878 .replace(" TEXT", " STRING");
879 let converted = if converted.ends_with(" INT") {
881 format!("{}{}", &converted[..converted.len() - 4], " INT64")
882 } else {
883 converted
884 };
885 *rtb = format!("TABLE <{}>", converted);
886 cf.is_table_function = true;
887 }
888 }
889 if cf.is_table_function {
891 if let Some(ref body) = cf.body {
892 if matches!(body, FunctionBody::StringLiteral(_)) {
893 if let Some(FunctionBody::StringLiteral(sql)) = cf.body.take() {
894 if let Ok(parsed) = crate::parser::Parser::parse_sql(&sql) {
896 if let Some(stmt) = parsed.into_iter().next() {
897 cf.body = Some(FunctionBody::Expression(stmt));
898 } else {
899 cf.body = Some(FunctionBody::StringLiteral(sql));
900 }
901 } else {
902 cf.body = Some(FunctionBody::StringLiteral(sql));
903 }
904 }
905 }
906 }
907 }
908 Ok(Expression::CreateFunction(cf))
909 }
910
911 _ => Ok(expr),
913 }
914 }
915}
916
917#[cfg(feature = "transpile")]
918impl BigQueryDialect {
919 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
921 use crate::expressions::DataType;
922 let transformed = match dt {
923 DataType::BigInt { .. } => DataType::Custom {
925 name: "INT64".to_string(),
926 },
927 DataType::Int { .. } => DataType::Custom {
929 name: "INT64".to_string(),
930 },
931 DataType::SmallInt { .. } => DataType::Custom {
933 name: "INT64".to_string(),
934 },
935 DataType::TinyInt { .. } => DataType::Custom {
937 name: "INT64".to_string(),
938 },
939 DataType::Float { .. } => DataType::Custom {
941 name: "FLOAT64".to_string(),
942 },
943 DataType::Double { .. } => DataType::Custom {
945 name: "FLOAT64".to_string(),
946 },
947 DataType::Boolean => DataType::Custom {
949 name: "BOOL".to_string(),
950 },
951 DataType::Char { .. } => DataType::Custom {
953 name: "STRING".to_string(),
954 },
955 DataType::VarChar { .. } => DataType::Custom {
957 name: "STRING".to_string(),
958 },
959 DataType::Text => DataType::Custom {
961 name: "STRING".to_string(),
962 },
963 DataType::String { .. } => DataType::Custom {
965 name: "STRING".to_string(),
966 },
967 DataType::Binary { .. } => DataType::Custom {
969 name: "BYTES".to_string(),
970 },
971 DataType::VarBinary { .. } => DataType::Custom {
973 name: "BYTES".to_string(),
974 },
975 DataType::Blob => DataType::Custom {
977 name: "BYTES".to_string(),
978 },
979 decimal @ DataType::Decimal { .. } => decimal,
982 DataType::Timestamp {
986 timezone: false, ..
987 } => DataType::Custom {
988 name: "TIMESTAMP".to_string(),
989 },
990 DataType::Timestamp { timezone: true, .. } => DataType::Custom {
991 name: "TIMESTAMP".to_string(),
992 },
993 DataType::Uuid => DataType::Custom {
995 name: "STRING".to_string(),
996 },
997 DataType::Custom { ref name } if name.eq_ignore_ascii_case("RECORD") => {
999 DataType::Custom {
1000 name: "STRUCT".to_string(),
1001 }
1002 }
1003 DataType::Custom { ref name } if name.eq_ignore_ascii_case("TIMESTAMPTZ") => {
1005 DataType::Custom {
1006 name: "TIMESTAMP".to_string(),
1007 }
1008 }
1009 DataType::Custom { ref name } if name.eq_ignore_ascii_case("BYTEINT") => {
1011 DataType::Custom {
1012 name: "INT64".to_string(),
1013 }
1014 }
1015 other => other,
1017 };
1018 Ok(Expression::DataType(transformed))
1019 }
1020
1021 fn transform_function(&self, f: Function) -> Result<Expression> {
1022 let name_upper = f.name.to_uppercase();
1023 match name_upper.as_str() {
1024 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1026 original_name: None,
1027 expressions: f.args,
1028 inferred_type: None,
1029 }))),
1030
1031 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1033 original_name: None,
1034 expressions: f.args,
1035 inferred_type: None,
1036 }))),
1037
1038 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1040 original_name: None,
1041 expressions: f.args,
1042 inferred_type: None,
1043 }))),
1044
1045 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1047 Function::new("STRING_AGG".to_string(), f.args),
1048 ))),
1049
1050 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1052 "SUBSTRING".to_string(),
1053 f.args,
1054 )))),
1055
1056 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
1058 seed: None,
1059 lower: None,
1060 upper: None,
1061 }))),
1062
1063 "CURRENT_DATE" if f.args.is_empty() => {
1066 Ok(Expression::CurrentDate(crate::expressions::CurrentDate))
1067 }
1068 "CURRENT_DATE" => Ok(Expression::Function(Box::new(Function {
1069 on_error: None,
1070 qualified_name: Vec::new(),
1071 source_dialect: None,
1072
1073 name: "CURRENT_DATE".to_string(),
1074 args: f.args,
1075 distinct: false,
1076 trailing_comments: Vec::new(),
1077 use_bracket_syntax: false,
1078 no_parens: false,
1079 quoted: false,
1080 span: None,
1081 inferred_type: None,
1082 }))),
1083
1084 "NOW" => Ok(Expression::CurrentTimestamp(
1086 crate::expressions::CurrentTimestamp {
1087 precision: None,
1088 sysdate: false,
1089 },
1090 )),
1091
1092 "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
1094 "PARSE_DATE".to_string(),
1095 f.args,
1096 )))),
1097
1098 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
1100 "PARSE_TIMESTAMP".to_string(),
1101 f.args,
1102 )))),
1103
1104 "TO_TIME" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1106 "TIME".to_string(),
1107 f.args,
1108 )))),
1109
1110 "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
1112 "FORMAT_DATE".to_string(),
1113 f.args,
1114 )))),
1115
1116 "POSITION" if f.args.len() == 2 => {
1119 let mut args = f.args;
1120 let first = args.remove(0);
1122 let second = args.remove(0);
1123 Ok(Expression::Function(Box::new(Function::new(
1124 "STRPOS".to_string(),
1125 vec![second, first],
1126 ))))
1127 }
1128
1129 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc::new(
1131 f.args.into_iter().next().unwrap(),
1132 )))),
1133
1134 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
1136 this: f.args.into_iter().next().unwrap(),
1137 decimals: None,
1138 to: None,
1139 }))),
1140
1141 "GETDATE" => Ok(Expression::CurrentTimestamp(
1143 crate::expressions::CurrentTimestamp {
1144 precision: None,
1145 sysdate: false,
1146 },
1147 )),
1148
1149 "CARDINALITY" if f.args.len() == 1 => Ok(Expression::ArrayLength(Box::new(
1152 UnaryFunc::new(f.args.into_iter().next().unwrap()),
1153 ))),
1154
1155 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(Function::new(
1159 "GENERATE_ARRAY".to_string(),
1160 f.args,
1161 )))),
1162
1163 "APPROX_DISTINCT" => Ok(Expression::Function(Box::new(Function::new(
1166 "APPROX_COUNT_DISTINCT".to_string(),
1167 f.args,
1168 )))),
1169
1170 "COUNT_IF" => Ok(Expression::Function(Box::new(Function::new(
1172 "COUNTIF".to_string(),
1173 f.args,
1174 )))),
1175
1176 "SHA" => Ok(Expression::Function(Box::new(Function::new(
1178 "SHA1".to_string(),
1179 f.args,
1180 )))),
1181
1182 "SHA2" => Ok(Expression::Function(Box::new(Function::new(
1184 "SHA256".to_string(),
1185 f.args,
1186 )))),
1187
1188 "MD5" => Ok(Expression::Function(Box::new(Function::new(
1191 "MD5".to_string(),
1192 f.args,
1193 )))),
1194
1195 "DATEADD" if f.args.len() == 3 => {
1200 let mut args = f.args;
1201 let unit_expr = args.remove(0);
1202 let amount = args.remove(0);
1203 let date = args.remove(0);
1204 let unit_name = match &unit_expr {
1206 Expression::Identifier(id) => id.name.to_uppercase(),
1207 Expression::Var(v) => v.this.to_uppercase(),
1208 Expression::Column(col) if col.table.is_none() => col.name.name.to_uppercase(),
1209 _ => "DAY".to_string(),
1210 };
1211 let unit = match unit_name.as_str() {
1212 "YEAR" | "YEARS" | "YY" | "YYYY" => crate::expressions::IntervalUnit::Year,
1213 "QUARTER" | "QUARTERS" | "QQ" | "Q" => {
1214 crate::expressions::IntervalUnit::Quarter
1215 }
1216 "MONTH" | "MONTHS" | "MM" | "M" => crate::expressions::IntervalUnit::Month,
1217 "WEEK" | "WEEKS" | "WK" | "WW" => crate::expressions::IntervalUnit::Week,
1218 "DAY" | "DAYS" | "DD" | "D" | "DAYOFMONTH" => {
1219 crate::expressions::IntervalUnit::Day
1220 }
1221 "HOUR" | "HOURS" | "HH" => crate::expressions::IntervalUnit::Hour,
1222 "MINUTE" | "MINUTES" | "MI" | "N" => crate::expressions::IntervalUnit::Minute,
1223 "SECOND" | "SECONDS" | "SS" | "S" => crate::expressions::IntervalUnit::Second,
1224 "MILLISECOND" | "MILLISECONDS" | "MS" => {
1225 crate::expressions::IntervalUnit::Millisecond
1226 }
1227 "MICROSECOND" | "MICROSECONDS" | "US" => {
1228 crate::expressions::IntervalUnit::Microsecond
1229 }
1230 _ => crate::expressions::IntervalUnit::Day,
1231 };
1232 Ok(Expression::DateAdd(Box::new(
1233 crate::expressions::DateAddFunc {
1234 this: date,
1235 interval: amount,
1236 unit,
1237 },
1238 )))
1239 }
1240 "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
1241 "DATE_ADD".to_string(),
1242 f.args,
1243 )))),
1244
1245 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
1247 "DATE_DIFF".to_string(),
1248 f.args,
1249 )))),
1250
1251 "TIMESTAMPDIFF" => Ok(Expression::Function(Box::new(Function::new(
1253 "TIMESTAMP_DIFF".to_string(),
1254 f.args,
1255 )))),
1256
1257 "NEWID" | "UUID" => Ok(Expression::Function(Box::new(Function::new(
1264 "GENERATE_UUID".to_string(),
1265 vec![],
1266 )))),
1267
1268 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(Function::new(
1270 "EDIT_DISTANCE".to_string(),
1271 f.args,
1272 )))),
1273
1274 "UNIX_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
1276 "UNIX_SECONDS".to_string(),
1277 f.args,
1278 )))),
1279
1280 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1282 "TIMESTAMP_SECONDS".to_string(),
1283 f.args,
1284 )))),
1285
1286 "CHAR_LENGTH" | "CHARACTER_LENGTH" => Ok(Expression::Function(Box::new(
1288 Function::new("LENGTH".to_string(), f.args),
1289 ))),
1290
1291 "OCTET_LENGTH" => Ok(Expression::Function(Box::new(Function::new(
1293 "BYTE_LENGTH".to_string(),
1294 f.args,
1295 )))),
1296
1297 "JSON_EXTRACT_STRING_ARRAY" => Ok(Expression::Function(Box::new(Function::new(
1299 "JSON_VALUE_ARRAY".to_string(),
1300 f.args,
1301 )))),
1302
1303 "SPLIT" if f.args.len() == 1 => {
1308 let mut args = f.args;
1309 args.push(Expression::Literal(Box::new(Literal::String(
1310 ",".to_string(),
1311 ))));
1312 Ok(Expression::Split(Box::new(SplitFunc {
1313 this: args.remove(0),
1314 delimiter: args.remove(0),
1315 })))
1316 }
1317
1318 "SPLIT" if f.args.len() == 2 => {
1320 let mut args = f.args;
1321 Ok(Expression::Split(Box::new(SplitFunc {
1322 this: args.remove(0),
1323 delimiter: args.remove(0),
1324 })))
1325 }
1326
1327 "REGEXP_SUBSTR" if f.args.len() >= 2 => {
1329 let args = if f.args.len() > 4 {
1331 f.args[..4].to_vec()
1332 } else {
1333 f.args
1334 };
1335 Ok(Expression::Function(Box::new(Function::new(
1336 "REGEXP_EXTRACT".to_string(),
1337 args,
1338 ))))
1339 }
1340 "REGEXP_SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1341 "REGEXP_EXTRACT".to_string(),
1342 f.args,
1343 )))),
1344
1345 "REGEXP_REPLACE" if f.args.len() > 3 => {
1347 let args = f.args[..3].to_vec();
1348 Ok(Expression::Function(Box::new(Function::new(
1349 "REGEXP_REPLACE".to_string(),
1350 args,
1351 ))))
1352 }
1353
1354 "OBJECT_CONSTRUCT_KEEP_NULL" => Ok(Expression::Function(Box::new(Function::new(
1356 "JSON_OBJECT".to_string(),
1357 f.args,
1358 )))),
1359
1360 "EDITDISTANCE" if f.args.len() == 3 => {
1362 let col1 = f.args[0].clone();
1363 let col2 = f.args[1].clone();
1364 let max_dist = f.args[2].clone();
1365 Ok(Expression::Function(Box::new(Function::new(
1366 "EDIT_DISTANCE".to_string(),
1367 vec![
1368 col1,
1369 col2,
1370 Expression::NamedArgument(Box::new(crate::expressions::NamedArgument {
1371 name: crate::expressions::Identifier::new("max_distance".to_string()),
1372 value: max_dist,
1373 separator: crate::expressions::NamedArgSeparator::DArrow,
1374 })),
1375 ],
1376 ))))
1377 }
1378 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
1379 Function::new("EDIT_DISTANCE".to_string(), f.args),
1380 ))),
1381
1382 "HEX_DECODE_BINARY" => Ok(Expression::Function(Box::new(Function::new(
1384 "FROM_HEX".to_string(),
1385 f.args,
1386 )))),
1387
1388 "PARSE_DATE"
1391 | "PARSE_DATETIME"
1392 | "PARSE_TIMESTAMP"
1393 | "SAFE.PARSE_DATE"
1394 | "SAFE.PARSE_DATETIME"
1395 | "SAFE.PARSE_TIMESTAMP" => {
1396 let args = self.normalize_time_format_args(f.args);
1397 Ok(Expression::Function(Box::new(Function {
1398 on_error: None,
1399 qualified_name: Vec::new(),
1400 source_dialect: None,
1401
1402 name: f.name,
1403 args,
1404 distinct: f.distinct,
1405 no_parens: f.no_parens,
1406 trailing_comments: f.trailing_comments,
1407 quoted: f.quoted,
1408 use_bracket_syntax: f.use_bracket_syntax,
1409 span: None,
1410 inferred_type: None,
1411 })))
1412 }
1413
1414 "GET_PATH" if f.args.len() == 2 => {
1416 let mut args = f.args;
1417 let this = args.remove(0);
1418 let path = args.remove(0);
1419 let json_path = match &path {
1420 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
1421 let Literal::String(s) = lit.as_ref() else {
1422 unreachable!()
1423 };
1424 let normalized = if s.starts_with('$') {
1425 s.clone()
1426 } else if s.starts_with('[') {
1427 format!("${}", s)
1428 } else {
1429 format!("$.{}", s)
1430 };
1431 Expression::Literal(Box::new(Literal::String(normalized)))
1432 }
1433 _ => path,
1434 };
1435 Ok(Expression::JsonExtract(Box::new(JsonExtractFunc {
1436 options: None,
1437 source_dialect: None,
1438
1439 this,
1440 path: json_path,
1441 returning: None,
1442 arrow_syntax: false,
1443 hash_arrow_syntax: false,
1444 wrapper_option: None,
1445 quotes_option: None,
1446 on_scalar_string: false,
1447 on_error: None,
1448 })))
1449 }
1450
1451 _ => Ok(Expression::Function(Box::new(f))),
1453 }
1454 }
1455
1456 fn transform_aggregate_function(
1457 &self,
1458 f: Box<crate::expressions::AggregateFunction>,
1459 ) -> Result<Expression> {
1460 let name_upper = f.name.to_uppercase();
1461 match name_upper.as_str() {
1462 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1464 Function::new("STRING_AGG".to_string(), f.args),
1465 ))),
1466
1467 _ => Ok(Expression::AggregateFunction(f)),
1469 }
1470 }
1471
1472 fn transform_method_call(&self, mc: crate::expressions::MethodCall) -> Result<Expression> {
1475 use crate::expressions::MethodCall;
1476
1477 if let Expression::Column(ref col) = mc.this {
1479 if col.name.name.eq_ignore_ascii_case("SAFE") {
1480 let method_upper = mc.method.name.to_uppercase();
1481 if method_upper == "PARSE_DATE"
1482 || method_upper == "PARSE_DATETIME"
1483 || method_upper == "PARSE_TIMESTAMP"
1484 {
1485 let args = self.normalize_time_format_args(mc.args);
1487 return Ok(Expression::MethodCall(Box::new(MethodCall {
1488 this: mc.this,
1489 method: mc.method,
1490 args,
1491 })));
1492 }
1493 }
1494 }
1495
1496 Ok(Expression::MethodCall(Box::new(mc)))
1498 }
1499
1500 fn normalize_time_format_args(&self, args: Vec<Expression>) -> Vec<Expression> {
1503 args.into_iter()
1504 .enumerate()
1505 .map(|(i, arg)| {
1506 if i == 0 {
1508 if let Expression::Literal(ref lit) = arg {
1509 if let Literal::String(s) = lit.as_ref() {
1510 let normalized = self.normalize_time_format(&s);
1511 return Expression::Literal(Box::new(Literal::String(normalized)));
1512 }
1513 }
1514 }
1515 arg
1516 })
1517 .collect()
1518 }
1519
1520 fn normalize_time_format(&self, format: &str) -> String {
1524 format.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T")
1525 }
1526
1527 fn bq_cast_format_to_strftime(format_expr: &Expression) -> Expression {
1530 use crate::expressions::Literal;
1531 if let Expression::Literal(lit) = format_expr {
1532 if let Literal::String(s) = lit.as_ref() {
1533 let result = s
1534 .replace("YYYYMMDD", "%Y%m%d")
1535 .replace("YYYY", "%Y")
1536 .replace("YY", "%y")
1537 .replace("MONTH", "%B")
1538 .replace("MON", "%b")
1539 .replace("MM", "%m")
1540 .replace("DD", "%d")
1541 .replace("HH24", "%H")
1542 .replace("HH12", "%I")
1543 .replace("HH", "%I")
1544 .replace("MI", "%M")
1545 .replace("SSTZH", "%S%z")
1546 .replace("SS", "%S")
1547 .replace("TZH", "%z");
1548 let normalized = result.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T");
1550 return Expression::Literal(Box::new(Literal::String(normalized)));
1551 }
1552 }
1553 format_expr.clone()
1554 }
1555}
1556
1557#[cfg(test)]
1558mod tests {
1559 use super::*;
1560 use crate::dialects::Dialect;
1561 use crate::parse_one;
1562
1563 fn transpile_to_bigquery(sql: &str) -> String {
1564 let dialect = Dialect::get(DialectType::Generic);
1565 let result = dialect
1566 .transpile(sql, DialectType::BigQuery)
1567 .expect("Transpile failed");
1568 result[0].clone()
1569 }
1570
1571 #[test]
1572 fn test_ifnull_identity() {
1573 let result = transpile_to_bigquery("SELECT IFNULL(a, b)");
1575 assert!(
1576 result.contains("COALESCE"),
1577 "Expected COALESCE, got: {}",
1578 result
1579 );
1580 }
1581
1582 #[test]
1583 fn test_nvl_to_ifnull() {
1584 let result = transpile_to_bigquery("SELECT NVL(a, b)");
1586 assert!(
1587 result.contains("IFNULL"),
1588 "Expected IFNULL, got: {}",
1589 result
1590 );
1591 }
1592
1593 #[test]
1594 fn test_try_cast_to_safe_cast() {
1595 let result = transpile_to_bigquery("SELECT TRY_CAST(a AS INT)");
1596 assert!(
1597 result.contains("SAFE_CAST"),
1598 "Expected SAFE_CAST, got: {}",
1599 result
1600 );
1601 }
1602
1603 #[test]
1604 fn test_random_to_rand() {
1605 let result = transpile_to_bigquery("SELECT RANDOM()");
1606 assert!(result.contains("RAND"), "Expected RAND, got: {}", result);
1607 }
1608
1609 #[test]
1610 fn test_basic_select() {
1611 let result = transpile_to_bigquery("SELECT a, b FROM users WHERE id = 1");
1612 assert!(result.contains("SELECT"));
1613 assert!(result.contains("FROM users"));
1614 }
1615
1616 #[test]
1617 fn test_group_concat_to_string_agg() {
1618 let result = transpile_to_bigquery("SELECT GROUP_CONCAT(name)");
1619 assert!(
1620 result.contains("STRING_AGG"),
1621 "Expected STRING_AGG, got: {}",
1622 result
1623 );
1624 }
1625
1626 #[test]
1627 fn test_generate_series_to_generate_array() {
1628 let result = transpile_to_bigquery("SELECT GENERATE_SERIES(1, 10)");
1629 assert!(
1630 result.contains("GENERATE_ARRAY"),
1631 "Expected GENERATE_ARRAY, got: {}",
1632 result
1633 );
1634 }
1635
1636 #[test]
1637 fn test_backtick_identifiers() {
1638 let dialect = BigQueryDialect;
1640 let config = dialect.generator_config();
1641 assert_eq!(config.identifier_quote, '`');
1642 }
1643
1644 fn bigquery_identity(sql: &str, expected: &str) {
1645 let dialect = Dialect::get(DialectType::BigQuery);
1646 let ast = dialect.parse(sql).expect("Parse failed");
1647 let transformed = dialect.transform(ast[0].clone()).expect("Transform failed");
1648 let result = dialect.generate(&transformed).expect("Generate failed");
1649 assert_eq!(result, expected, "SQL: {}", sql);
1650 }
1651
1652 #[test]
1653 fn test_numeric_parameters_survive_transformation_479() {
1654 use crate::expressions::DataType;
1655 use crate::traversal::ExpressionWalk;
1656
1657 let dialect = Dialect::get(DialectType::BigQuery);
1658 let ast = parse_one(
1659 "SELECT CAST(1.005 AS NUMERIC(10, 2))",
1660 DialectType::BigQuery,
1661 )
1662 .unwrap();
1663 let transformed = dialect.transform(ast).unwrap();
1664 assert!(transformed.dfs().any(|node| matches!(node,
1665 Expression::Cast(cast) if matches!(cast.to, DataType::Decimal { precision: Some(10), scale: Some(2) })
1666 )));
1667 assert_eq!(
1668 dialect.generate(&transformed).unwrap(),
1669 "SELECT CAST(1.005 AS NUMERIC)"
1670 );
1671 for (sql, expected) in [
1672 (
1673 "SELECT SAFE_CAST(1 AS NUMERIC(10, 2))",
1674 "SELECT SAFE_CAST(1 AS NUMERIC)",
1675 ),
1676 (
1677 "SELECT CAST(a AS ARRAY<NUMERIC(10, 2)>)",
1678 "SELECT CAST(a AS ARRAY<NUMERIC>)",
1679 ),
1680 (
1681 "CREATE TABLE t (a NUMERIC(10, 2), b NUMERIC)",
1682 "CREATE TABLE t (a NUMERIC(10, 2), b NUMERIC)",
1683 ),
1684 ] {
1685 bigquery_identity(sql, expected);
1686 }
1687 }
1688
1689 #[test]
1690 fn test_named_query_parameters_478() {
1691 use crate::expressions::ParameterStyle;
1692 use crate::traversal::ExpressionWalk;
1693
1694 for (sql, name, quoted) in [
1695 ("SELECT @sites", "sites", false),
1696 ("SELECT @Sites_1", "Sites_1", false),
1697 ("SELECT @select", "select", false),
1698 ("SELECT @`a b`", "a b", true),
1699 ("SELECT @param.dataField", "param", false),
1700 ("SELECT @ids[OFFSET(0)]", "ids", false),
1701 ("SELECT @sites + 1", "sites", false),
1702 ] {
1703 let ast = parse_one(sql, DialectType::BigQuery).unwrap();
1704 let parameters: Vec<_> = ast
1705 .dfs()
1706 .filter_map(|node| match node {
1707 Expression::Parameter(parameter) => Some(parameter),
1708 _ => None,
1709 })
1710 .collect();
1711 assert_eq!(parameters.len(), 1, "{sql}");
1712 assert_eq!(parameters[0].name.as_deref(), Some(name), "{sql}");
1713 assert_eq!(parameters[0].style, ParameterStyle::At, "{sql}");
1714 assert_eq!(parameters[0].quoted, quoted, "{sql}");
1715 assert!(
1716 !ast.dfs().any(|node| match node {
1717 Expression::Abs(_) => true,
1718 Expression::Column(column) => column.name.name.starts_with('@'),
1719 _ => false,
1720 }),
1721 "{sql}"
1722 );
1723 bigquery_identity(sql, sql);
1724 }
1725 bigquery_identity("SELECT @ sites", "SELECT @sites");
1726 bigquery_identity("SELECT @@time_zone", "SELECT @@time_zone");
1727 bigquery_identity("SELECT ?", "SELECT ?");
1728 for sql in [
1729 "SELECT @",
1730 "SELECT @1",
1731 "SELECT @-1",
1732 "SELECT @(1)",
1733 "SELECT @'name'",
1734 "SELECT @foo$bar",
1735 ] {
1736 assert!(parse_one(sql, DialectType::BigQuery).is_err(), "{sql}");
1737 }
1738 }
1739
1740 #[test]
1741 fn test_safe_namespace_parses_as_function() {
1742 let expr = parse_one(
1743 "SELECT SAFE.PARSE_JSON(data) AS json_data FROM t",
1744 DialectType::BigQuery,
1745 )
1746 .expect("parse");
1747
1748 let Expression::Select(select) = expr else {
1749 panic!("expected SELECT");
1750 };
1751 let Expression::Alias(alias) = &select.expressions[0] else {
1752 panic!("expected alias");
1753 };
1754 let Expression::Function(function) = &alias.this else {
1755 panic!("expected SAFE namespace call to parse as Function");
1756 };
1757
1758 assert_eq!(function.name, "SAFE.PARSE_JSON");
1759 assert_eq!(function.args.len(), 1);
1760 }
1761
1762 #[test]
1763 fn test_safe_namespace_identity() {
1764 bigquery_identity("SAFE.PARSE_JSON(data)", "SAFE.PARSE_JSON(data)");
1765 bigquery_identity(
1766 "SAFE.PARSE_DATE('%Y-%m-%d', date_col)",
1767 "SAFE.PARSE_DATE('%F', date_col)",
1768 );
1769 bigquery_identity("SAFE.DIVIDE(a, b)", "SAFE.DIVIDE(a, b)");
1770 }
1771
1772 #[test]
1773 fn test_cast_char_to_string() {
1774 bigquery_identity("CAST(x AS CHAR)", "CAST(x AS STRING)");
1775 }
1776
1777 #[test]
1778 fn test_cast_varchar_to_string() {
1779 bigquery_identity("CAST(x AS VARCHAR)", "CAST(x AS STRING)");
1780 }
1781
1782 #[test]
1783 fn test_cast_nchar_to_string() {
1784 bigquery_identity("CAST(x AS NCHAR)", "CAST(x AS STRING)");
1785 }
1786
1787 #[test]
1788 fn test_cast_nvarchar_to_string() {
1789 bigquery_identity("CAST(x AS NVARCHAR)", "CAST(x AS STRING)");
1790 }
1791
1792 #[test]
1793 fn test_cast_timestamptz_to_timestamp() {
1794 bigquery_identity("CAST(x AS TIMESTAMPTZ)", "CAST(x AS TIMESTAMP)");
1795 }
1796
1797 #[test]
1798 fn test_cast_record_to_struct() {
1799 bigquery_identity("CAST(x AS RECORD)", "CAST(x AS STRUCT)");
1800 }
1801
1802 #[test]
1803 fn test_json_literal_to_parse_json() {
1804 bigquery_identity(
1806 "SELECT JSON '\"foo\"' AS json_data",
1807 "SELECT PARSE_JSON('\"foo\"') AS json_data",
1808 );
1809 }
1810
1811 #[test]
1812 fn test_grant_as_alias_not_quoted() {
1813 bigquery_identity(
1815 "SELECT GRANT FROM (SELECT 'input' AS GRANT)",
1816 "SELECT GRANT FROM (SELECT 'input' AS GRANT)",
1817 );
1818 }
1819
1820 #[test]
1821 fn test_timestamp_literal_to_cast() {
1822 bigquery_identity(
1824 "CREATE VIEW `d.v` OPTIONS (expiration_timestamp=TIMESTAMP '2020-01-02T04:05:06.007Z') AS SELECT 1 AS c",
1825 "CREATE VIEW `d.v` OPTIONS (expiration_timestamp=CAST('2020-01-02T04:05:06.007Z' AS TIMESTAMP)) AS SELECT 1 AS c"
1826 );
1827 }
1828
1829 #[test]
1830 fn test_date_literal_to_cast_in_extract() {
1831 bigquery_identity(
1833 "EXTRACT(WEEK(THURSDAY) FROM DATE '2013-12-25')",
1834 "EXTRACT(WEEK(THURSDAY) FROM CAST('2013-12-25' AS DATE))",
1835 );
1836 }
1837
1838 #[test]
1839 fn test_json_object_with_json_literals() {
1840 bigquery_identity(
1842 "SELECT JSON_OBJECT('a', JSON '10') AS json_data",
1843 "SELECT JSON_OBJECT('a', PARSE_JSON('10')) AS json_data",
1844 );
1845 }
1846
1847 #[test]
1852 fn test_safe_parse_date_format_normalization() {
1853 bigquery_identity(
1855 "SAFE.PARSE_DATE('%Y-%m-%d', '2024-01-15')",
1856 "SAFE.PARSE_DATE('%F', '2024-01-15')",
1857 );
1858 }
1859
1860 #[test]
1861 fn test_safe_parse_datetime_format_normalization() {
1862 bigquery_identity(
1864 "SAFE.PARSE_DATETIME('%Y-%m-%d %H:%M:%S', '2024-01-15 10:30:00')",
1865 "SAFE.PARSE_DATETIME('%F %T', '2024-01-15 10:30:00')",
1866 );
1867 }
1868
1869 #[test]
1870 fn test_safe_parse_timestamp_format_normalization() {
1871 bigquery_identity(
1873 "SAFE.PARSE_TIMESTAMP('%Y-%m-%d %H:%M:%S', '2024-01-15 10:30:00')",
1874 "SAFE.PARSE_TIMESTAMP('%F %T', '2024-01-15 10:30:00')",
1875 );
1876 }
1877
1878 #[test]
1879 fn test_datetime_literal_to_cast() {
1880 bigquery_identity(
1882 "LAST_DAY(DATETIME '2008-11-10 15:30:00', WEEK(SUNDAY))",
1883 "LAST_DAY(CAST('2008-11-10 15:30:00' AS DATETIME), WEEK)",
1884 );
1885 }
1886
1887 #[test]
1888 fn test_last_day_week_modifier_stripped() {
1889 bigquery_identity("LAST_DAY(col, WEEK(MONDAY))", "LAST_DAY(col, WEEK)");
1891 }
1892
1893 #[test]
1894 fn test_hash_line_comment_parses() {
1895 let result = parse_one("SELECT 1 as a #hello world", DialectType::BigQuery);
1898 assert!(result.is_ok(), "Expected parse to succeed, got: {result:?}");
1899 }
1900}