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.quotes.insert("\"".to_string(), "\"".to_string());
39 config.quotes.insert("'''".to_string(), "'''".to_string());
41 config
42 .quotes
43 .insert("\"\"\"".to_string(), "\"\"\"".to_string());
44 config.string_escapes = vec!['\'', '\\'];
46 config.b_prefix_is_byte_string = true;
48 config.hex_number_strings = true;
50 config.hex_string_is_integer_type = true;
52 config.hash_comments = true;
54 config
55 }
56
57 #[cfg(feature = "generate")]
58
59 fn generator_config(&self) -> GeneratorConfig {
60 use crate::generator::{IdentifierQuoteStyle, NormalizeFunctions};
61 GeneratorConfig {
62 identifier_quote: '`',
63 identifier_quote_style: IdentifierQuoteStyle::BACKTICK,
64 dialect: Some(DialectType::BigQuery),
65 normalize_functions: NormalizeFunctions::None,
67 interval_allows_plural_form: false,
69 join_hints: false,
70 query_hints: false,
71 table_hints: false,
72 limit_fetch_style: crate::generator::LimitFetchStyle::Limit,
73 rename_table_with_db: false,
74 nvl2_supported: false,
75 unnest_with_ordinality: false,
76 collate_is_func: true,
77 limit_only_literals: true,
78 supports_table_alias_columns: false,
79 unpivot_aliases_are_identifiers: false,
80 json_key_value_pair_sep: ",",
81 null_ordering_supported: false,
82 ignore_nulls_in_func: true,
83 json_path_single_quote_escape: true,
84 can_implement_array_any: true,
85 supports_to_number: false,
86 named_placeholder_token: "@",
87 hex_func: "TO_HEX",
88 with_properties_prefix: "OPTIONS",
89 supports_exploding_projections: false,
90 except_intersect_support_all_clause: false,
91 supports_unix_seconds: true,
92 try_supported: true,
94 semi_anti_join_with_side: false,
96 ..Default::default()
97 }
98 }
99
100 #[cfg(feature = "transpile")]
101
102 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
103 match expr {
104 Expression::DataType(dt) => self.transform_data_type(dt),
106
107 Expression::Table(mut table)
108 if table.catalog.is_none()
109 && table.alias.is_none()
110 && table.schema.as_ref().map_or(false, |schema| {
111 schema.quoted && schema.name.contains("INFORMATION_SCHEMA")
112 }) =>
113 {
114 let schema = table.schema.take().expect("schema checked above");
115 let old_name = table.name.clone();
116 let alias = old_name.name.clone();
117 table.name = Identifier {
118 name: format!("{}.{}", schema.name, old_name.name),
119 quoted: true,
120 trailing_comments: old_name.trailing_comments,
121 span: old_name.span,
122 };
123 table.alias = Some(Identifier::new(alias));
124 Ok(Expression::Table(table))
125 }
126
127 Expression::IfNull(f) => Ok(Expression::IfNull(f)),
130
131 Expression::Nvl(f) => Ok(Expression::IfNull(f)),
133
134 Expression::Coalesce(f) => Ok(Expression::Coalesce(f)),
136
137 Expression::GroupConcat(f) => Ok(Expression::StringAgg(Box::new(StringAggFunc {
140 this: f.this,
141 separator: f.separator,
142 order_by: f.order_by,
143 distinct: f.distinct,
144 filter: f.filter,
145 limit: None,
146 inferred_type: None,
147 }))),
148
149 Expression::TryCast(c) => {
155 let transformed_type = match self.transform_data_type(c.to)? {
156 Expression::DataType(dt) => dt,
157 _ => return Err(crate::error::Error::parse("Expected DataType", 0, 0, 0, 0)),
158 };
159 Ok(Expression::SafeCast(Box::new(crate::expressions::Cast {
160 this: c.this,
161 to: transformed_type,
162 trailing_comments: c.trailing_comments,
163 double_colon_syntax: c.double_colon_syntax,
164 format: c.format,
165 default: c.default,
166 inferred_type: None,
167 })))
168 }
169
170 Expression::ILike(op) => {
173 let lower_left = Expression::Lower(Box::new(UnaryFunc::new(op.left)));
174 let lower_right = Expression::Lower(Box::new(UnaryFunc::new(op.right)));
175 Ok(Expression::Like(Box::new(LikeOp {
176 left: lower_left,
177 right: lower_right,
178 escape: op.escape,
179 quantifier: op.quantifier,
180 inferred_type: None,
181 })))
182 }
183
184 Expression::RegexpLike(f) => Ok(Expression::Function(Box::new(Function::new(
186 "REGEXP_CONTAINS".to_string(),
187 vec![f.this, f.pattern],
188 )))),
189
190 Expression::Explode(f) => Ok(Expression::Unnest(Box::new(
193 crate::expressions::UnnestFunc {
194 this: f.this,
195 expressions: Vec::new(),
196 with_ordinality: false,
197 alias: None,
198 offset_alias: None,
199 inferred_type: None,
200 },
201 ))),
202
203 Expression::ExplodeOuter(f) => Ok(Expression::Unnest(Box::new(
205 crate::expressions::UnnestFunc {
206 this: f.this,
207 expressions: Vec::new(),
208 with_ordinality: false,
209 alias: None,
210 offset_alias: None,
211 inferred_type: None,
212 },
213 ))),
214
215 Expression::GenerateSeries(f) => {
217 let mut args = Vec::new();
218 if let Some(start) = f.start {
219 args.push(*start);
220 }
221 if let Some(end) = f.end {
222 args.push(*end);
223 }
224 if let Some(step) = f.step {
225 args.push(*step);
226 }
227 Ok(Expression::Function(Box::new(Function::new(
228 "GENERATE_ARRAY".to_string(),
229 args,
230 ))))
231 }
232
233 Expression::BitwiseAndAgg(f) => Ok(Expression::Function(Box::new(Function::new(
236 "BIT_AND".to_string(),
237 vec![f.this],
238 )))),
239
240 Expression::BitwiseOrAgg(f) => Ok(Expression::Function(Box::new(Function::new(
242 "BIT_OR".to_string(),
243 vec![f.this],
244 )))),
245
246 Expression::BitwiseXorAgg(f) => Ok(Expression::Function(Box::new(Function::new(
248 "BIT_XOR".to_string(),
249 vec![f.this],
250 )))),
251
252 Expression::BitwiseCount(f) => Ok(Expression::Function(Box::new(Function::new(
254 "BIT_COUNT".to_string(),
255 vec![f.this],
256 )))),
257
258 Expression::ByteLength(f) => Ok(Expression::Function(Box::new(Function::new(
260 "BYTE_LENGTH".to_string(),
261 vec![f.this],
262 )))),
263
264 Expression::IntDiv(f) => Ok(Expression::Function(Box::new(Function::new(
266 "DIV".to_string(),
267 vec![f.this, f.expression],
268 )))),
269
270 Expression::Int64(f) => Ok(Expression::Function(Box::new(Function::new(
272 "INT64".to_string(),
273 vec![f.this],
274 )))),
275
276 Expression::Random(_) => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
279 seed: None,
280 lower: None,
281 upper: None,
282 }))),
283
284 Expression::Uuid(_) => Ok(Expression::Function(Box::new(Function::new(
287 "GENERATE_UUID".to_string(),
288 vec![],
289 )))),
290
291 Expression::ApproxDistinct(f) => Ok(Expression::Function(Box::new(Function::new(
294 "APPROX_COUNT_DISTINCT".to_string(),
295 vec![f.this],
296 )))),
297
298 Expression::ArgMax(f) => Ok(Expression::Function(Box::new(Function::new(
300 "MAX_BY".to_string(),
301 vec![*f.this, *f.expression],
302 )))),
303
304 Expression::ArgMin(f) => Ok(Expression::Function(Box::new(Function::new(
306 "MIN_BY".to_string(),
307 vec![*f.this, *f.expression],
308 )))),
309
310 Expression::CountIf(f) => Ok(Expression::Function(Box::new(Function::new(
313 "COUNTIF".to_string(),
314 vec![f.this],
315 )))),
316
317 Expression::StringAgg(f) => Ok(Expression::StringAgg(f)),
320
321 Expression::Unhex(f) => Ok(Expression::Function(Box::new(Function::new(
324 "FROM_HEX".to_string(),
325 vec![*f.this],
326 )))),
327
328 Expression::UnixToTime(f) => {
330 let scale = f.scale.unwrap_or(0);
331 match scale {
332 0 => Ok(Expression::Function(Box::new(Function::new(
333 "TIMESTAMP_SECONDS".to_string(),
334 vec![*f.this],
335 )))),
336 3 => Ok(Expression::Function(Box::new(Function::new(
337 "TIMESTAMP_MILLIS".to_string(),
338 vec![*f.this],
339 )))),
340 6 => Ok(Expression::Function(Box::new(Function::new(
341 "TIMESTAMP_MICROS".to_string(),
342 vec![*f.this],
343 )))),
344 _ => {
345 let div_expr =
347 Expression::Div(Box::new(crate::expressions::BinaryOp::new(
348 *f.this,
349 Expression::Function(Box::new(Function::new(
350 "POWER".to_string(),
351 vec![Expression::number(10), Expression::number(scale)],
352 ))),
353 )));
354 let cast_expr = Expression::Cast(Box::new(crate::expressions::Cast {
355 this: div_expr,
356 to: crate::expressions::DataType::Custom {
357 name: "INT64".to_string(),
358 },
359 double_colon_syntax: false,
360 trailing_comments: vec![],
361 format: None,
362 default: None,
363 inferred_type: None,
364 }));
365 Ok(Expression::Function(Box::new(Function::new(
366 "TIMESTAMP_SECONDS".to_string(),
367 vec![cast_expr],
368 ))))
369 }
370 }
371 }
372
373 Expression::DateDiff(f) => {
376 let unit_str = match f.unit {
378 Some(crate::expressions::IntervalUnit::Year) => "YEAR",
379 Some(crate::expressions::IntervalUnit::Quarter) => "QUARTER",
380 Some(crate::expressions::IntervalUnit::Month) => "MONTH",
381 Some(crate::expressions::IntervalUnit::Week) => "WEEK",
382 Some(crate::expressions::IntervalUnit::Day) => "DAY",
383 Some(crate::expressions::IntervalUnit::Hour) => "HOUR",
384 Some(crate::expressions::IntervalUnit::Minute) => "MINUTE",
385 Some(crate::expressions::IntervalUnit::Second) => "SECOND",
386 Some(crate::expressions::IntervalUnit::Millisecond) => "MILLISECOND",
387 Some(crate::expressions::IntervalUnit::Microsecond) => "MICROSECOND",
388 Some(crate::expressions::IntervalUnit::Nanosecond) => "NANOSECOND",
389 None => "DAY",
390 };
391 let unit = Expression::Identifier(crate::expressions::Identifier {
392 name: unit_str.to_string(),
393 quoted: false,
394 trailing_comments: Vec::new(),
395 span: None,
396 });
397 Ok(Expression::Function(Box::new(Function::new(
398 "DATE_DIFF".to_string(),
399 vec![f.this, f.expression, unit],
400 ))))
401 }
402
403 Expression::VarPop(f) => Ok(Expression::Function(Box::new(Function::new(
406 "VAR_POP".to_string(),
407 vec![f.this],
408 )))),
409
410 Expression::SHA(f) => Ok(Expression::Function(Box::new(Function::new(
413 "SHA1".to_string(),
414 vec![f.this],
415 )))),
416
417 Expression::SHA1Digest(f) => Ok(Expression::Function(Box::new(Function::new(
419 "SHA1".to_string(),
420 vec![f.this],
421 )))),
422
423 Expression::MD5Digest(f) => Ok(Expression::Function(Box::new(Function::new(
425 "MD5".to_string(),
426 vec![*f.this],
427 )))),
428
429 Expression::JSONBool(f) => Ok(Expression::Function(Box::new(Function::new(
432 "BOOL".to_string(),
433 vec![f.this],
434 )))),
435
436 Expression::StringFunc(f) => Ok(Expression::Function(Box::new(Function::new(
438 "STRING".to_string(),
439 vec![*f.this],
440 )))),
441
442 Expression::DateFromUnixDate(f) => Ok(Expression::Function(Box::new(Function::new(
445 "DATE_FROM_UNIX_DATE".to_string(),
446 vec![f.this],
447 )))),
448
449 Expression::UnixDate(f) => Ok(Expression::Function(Box::new(Function::new(
451 "UNIX_DATE".to_string(),
452 vec![f.this],
453 )))),
454
455 Expression::TimestampDiff(f) => Ok(Expression::Function(Box::new(Function::new(
457 "TIMESTAMP_DIFF".to_string(),
458 vec![*f.this, *f.expression],
459 )))),
460
461 Expression::FromTimeZone(f) => Ok(Expression::Function(Box::new(Function::new(
463 "DATETIME".to_string(),
464 vec![*f.this],
465 )))),
466
467 Expression::TsOrDsToDatetime(f) => Ok(Expression::Function(Box::new(Function::new(
469 "DATETIME".to_string(),
470 vec![f.this],
471 )))),
472
473 Expression::TsOrDsToTimestamp(f) => Ok(Expression::Function(Box::new(Function::new(
475 "TIMESTAMP".to_string(),
476 vec![f.this],
477 )))),
478
479 Expression::IfFunc(f) => {
481 let mut args = vec![f.condition, f.true_value];
482 if let Some(false_val) = f.false_value {
483 args.push(false_val);
484 } else {
485 args.push(Expression::Null(crate::expressions::Null));
486 }
487 Ok(Expression::Function(Box::new(Function::new(
488 "IF".to_string(),
489 args,
490 ))))
491 }
492
493 Expression::HexStringExpr(f) => Ok(Expression::Function(Box::new(Function::new(
495 "FROM_HEX".to_string(),
496 vec![*f.this],
497 )))),
498
499 Expression::ApproxTopK(f) => {
502 let mut args = vec![*f.this];
503 if let Some(expr) = f.expression {
504 args.push(*expr);
505 }
506 Ok(Expression::Function(Box::new(Function::new(
507 "APPROX_TOP_COUNT".to_string(),
508 args,
509 ))))
510 }
511
512 Expression::SafeDivide(f) => Ok(Expression::Function(Box::new(Function::new(
514 "SAFE_DIVIDE".to_string(),
515 vec![*f.this, *f.expression],
516 )))),
517
518 Expression::JSONKeysAtDepth(f) => Ok(Expression::Function(Box::new(Function::new(
520 "JSON_KEYS".to_string(),
521 vec![*f.this],
522 )))),
523
524 Expression::JSONValueArray(f) => Ok(Expression::Function(Box::new(Function::new(
526 "JSON_VALUE_ARRAY".to_string(),
527 vec![*f.this],
528 )))),
529
530 Expression::DateFromParts(f) => {
532 let mut args = Vec::new();
533 if let Some(y) = f.year {
534 args.push(*y);
535 }
536 if let Some(m) = f.month {
537 args.push(*m);
538 }
539 if let Some(d) = f.day {
540 args.push(*d);
541 }
542 Ok(Expression::Function(Box::new(Function::new(
543 "DATE".to_string(),
544 args,
545 ))))
546 }
547
548 Expression::Split(f) => {
551 let delimiter = match &f.delimiter {
553 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(s) if s.is_empty()) =>
554 {
555 let Literal::String(_) = lit.as_ref() else {
556 unreachable!()
557 };
558 Expression::Literal(Box::new(Literal::String(",".to_string())))
559 }
560 _ => f.delimiter,
561 };
562 Ok(Expression::Split(Box::new(SplitFunc {
563 this: f.this,
564 delimiter,
565 })))
566 }
567
568 Expression::Cast(c) => {
572 use crate::expressions::DataType;
573 let is_json = matches!(c.to, DataType::Json | DataType::JsonB)
575 || matches!(&c.to, DataType::Custom { name } if name.eq_ignore_ascii_case("JSON") || name.eq_ignore_ascii_case("JSONB"));
576 if is_json {
577 return Ok(Expression::ParseJson(Box::new(UnaryFunc::new(c.this))));
578 }
579 if c.format.is_some() {
581 let is_temporal = matches!(
582 c.to,
583 DataType::Date | DataType::Timestamp { .. } | DataType::Time { .. }
584 ) || matches!(&c.to, DataType::Custom { name } if
585 name.eq_ignore_ascii_case("TIMESTAMP") ||
586 name.eq_ignore_ascii_case("DATE") ||
587 name.eq_ignore_ascii_case("DATETIME") ||
588 name.eq_ignore_ascii_case("TIME")
589 );
590 if is_temporal {
591 let format_expr = c.format.as_ref().unwrap().as_ref();
592 let (actual_format, timezone) = match format_expr {
594 Expression::AtTimeZone(ref atz) => {
595 (atz.this.clone(), Some(atz.zone.clone()))
596 }
597 _ => (format_expr.clone(), None),
598 };
599 let strftime_fmt = Self::bq_cast_format_to_strftime(&actual_format);
600 let func_name = match &c.to {
601 DataType::Date => "PARSE_DATE",
602 DataType::Custom { name } if name.eq_ignore_ascii_case("DATE") => {
603 "PARSE_DATE"
604 }
605 DataType::Custom { name } if name.eq_ignore_ascii_case("DATETIME") => {
606 "PARSE_DATETIME"
607 }
608 _ => "PARSE_TIMESTAMP",
609 };
610 let mut func_args = vec![strftime_fmt, c.this];
611 if let Some(tz) = timezone {
612 func_args.push(tz);
613 }
614 return Ok(Expression::Function(Box::new(Function::new(
615 func_name.to_string(),
616 func_args,
617 ))));
618 }
619 }
620 let transformed_type = match self.transform_data_type(c.to)? {
621 Expression::DataType(dt) => dt,
622 _ => return Err(crate::error::Error::parse("Expected DataType", 0, 0, 0, 0)),
623 };
624 Ok(Expression::Cast(Box::new(crate::expressions::Cast {
625 this: c.this,
626 to: transformed_type,
627 trailing_comments: c.trailing_comments,
628 double_colon_syntax: c.double_colon_syntax,
629 format: c.format,
630 default: c.default,
631 inferred_type: None,
632 })))
633 }
634
635 Expression::SafeCast(c) => {
637 let transformed_type = match self.transform_data_type(c.to)? {
638 Expression::DataType(dt) => dt,
639 _ => return Err(crate::error::Error::parse("Expected DataType", 0, 0, 0, 0)),
640 };
641 Ok(Expression::SafeCast(Box::new(crate::expressions::Cast {
642 this: c.this,
643 to: transformed_type,
644 trailing_comments: c.trailing_comments,
645 double_colon_syntax: c.double_colon_syntax,
646 format: c.format,
647 default: c.default,
648 inferred_type: None,
649 })))
650 }
651
652 Expression::Select(mut select) => {
655 if select.group_by.is_some() && select.order_by.is_some() {
656 let aliases: Vec<(Expression, Identifier)> = select
658 .expressions
659 .iter()
660 .filter_map(|e| {
661 if let Expression::Alias(a) = e {
662 Some((a.this.clone(), a.alias.clone()))
663 } else {
664 None
665 }
666 })
667 .collect();
668
669 if let Some(ref mut group_by) = select.group_by {
670 for grouped in group_by.expressions.iter_mut() {
671 if matches!(grouped, Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)))
673 {
674 continue;
675 }
676 for (expr, alias_ident) in &aliases {
678 if grouped == expr {
679 *grouped = Expression::boxed_column(Column {
680 name: alias_ident.clone(),
681 table: None,
682 join_mark: false,
683 trailing_comments: Vec::new(),
684 span: None,
685 inferred_type: None,
686 });
687 break;
688 }
689 }
690 }
691 }
692 }
693 Ok(Expression::Select(select))
694 }
695
696 Expression::ArrayContains(f) => {
698 let array_expr = f.this;
699 let value_expr = f.expression;
700
701 let unnest = Expression::Unnest(Box::new(UnnestFunc {
703 this: array_expr,
704 expressions: Vec::new(),
705 with_ordinality: false,
706 alias: None,
707 offset_alias: None,
708 inferred_type: None,
709 }));
710 let aliased_unnest = Expression::Alias(Box::new(Alias {
711 this: unnest,
712 alias: Identifier::new("_col"),
713 column_aliases: Vec::new(),
714 alias_explicit_as: false,
715 alias_keyword: None,
716 pre_alias_comments: Vec::new(),
717 trailing_comments: Vec::new(),
718 inferred_type: None,
719 }));
720 let col_ref = Expression::boxed_column(Column {
721 name: Identifier::new("_col"),
722 table: None,
723 join_mark: false,
724 trailing_comments: Vec::new(),
725 span: None,
726 inferred_type: None,
727 });
728 let where_clause = Where {
729 this: Expression::Eq(Box::new(BinaryOp {
730 left: col_ref,
731 right: value_expr,
732 left_comments: Vec::new(),
733 operator_comments: Vec::new(),
734 trailing_comments: Vec::new(),
735 inferred_type: None,
736 })),
737 };
738 let inner_select = Expression::Select(Box::new(Select {
739 expressions: vec![Expression::Literal(Box::new(Literal::Number(
740 "1".to_string(),
741 )))],
742 from: Some(From {
743 expressions: vec![aliased_unnest],
744 }),
745 where_clause: Some(where_clause),
746 ..Default::default()
747 }));
748 Ok(Expression::Exists(Box::new(Exists {
749 this: inner_select,
750 not: false,
751 })))
752 }
753
754 Expression::JsonObject(mut f) => {
757 if f.pairs.len() == 1 {
758 let keys_exprs = match &f.pairs[0].0 {
760 Expression::Array(arr) => Some(&arr.expressions),
761 Expression::ArrayFunc(arr) => Some(&arr.expressions),
762 _ => None,
763 };
764 let vals_exprs = match &f.pairs[0].1 {
765 Expression::Array(arr) => Some(&arr.expressions),
766 Expression::ArrayFunc(arr) => Some(&arr.expressions),
767 _ => None,
768 };
769 if let (Some(keys), Some(vals)) = (keys_exprs, vals_exprs) {
770 if keys.len() == vals.len() {
771 let new_pairs: Vec<(Expression, Expression)> = keys
772 .iter()
773 .zip(vals.iter())
774 .map(|(k, v)| (k.clone(), v.clone()))
775 .collect();
776 f.pairs = new_pairs;
777 }
778 }
779 }
780 Ok(Expression::JsonObject(f))
781 }
782
783 Expression::ModFunc(mut f) => {
786 if let Expression::Paren(paren) = f.this {
788 f.this = paren.this;
789 }
790 Ok(Expression::ModFunc(f))
791 }
792
793 Expression::JSONExtract(e) if e.variant_extract.is_some() => {
795 let path = match *e.expression {
796 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
797 let Literal::String(s) = lit.as_ref() else {
798 unreachable!()
799 };
800 let normalized = if s.starts_with('$') {
801 s.clone()
802 } else if s.starts_with('[') {
803 format!("${}", s)
804 } else {
805 format!("$.{}", s)
806 };
807 Expression::Literal(Box::new(Literal::String(normalized)))
808 }
809 other => other,
810 };
811 Ok(Expression::Function(Box::new(Function::new(
812 "JSON_EXTRACT".to_string(),
813 vec![*e.this, path],
814 ))))
815 }
816
817 Expression::Function(f) => self.transform_function(*f),
819
820 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
822
823 Expression::MethodCall(mc) => self.transform_method_call(*mc),
826
827 Expression::CreateFunction(mut cf) => {
830 if let Some(ref mut rtb) = cf.returns_table_body {
831 if rtb.starts_with("TABLE (") || rtb.starts_with("TABLE(") {
832 let inner = if rtb.starts_with("TABLE (") {
834 &rtb["TABLE (".len()..rtb.len() - 1]
835 } else {
836 &rtb["TABLE(".len()..rtb.len() - 1]
837 };
838 let converted = inner
840 .replace(" INT,", " INT64,")
841 .replace(" INT)", " INT64)")
842 .replace(" INTEGER,", " INT64,")
843 .replace(" INTEGER)", " INT64)")
844 .replace(" FLOAT,", " FLOAT64,")
845 .replace(" FLOAT)", " FLOAT64)")
846 .replace(" BOOLEAN,", " BOOL,")
847 .replace(" BOOLEAN)", " BOOL)")
848 .replace(" VARCHAR", " STRING")
849 .replace(" TEXT", " STRING");
850 let converted = if converted.ends_with(" INT") {
852 format!("{}{}", &converted[..converted.len() - 4], " INT64")
853 } else {
854 converted
855 };
856 *rtb = format!("TABLE <{}>", converted);
857 cf.is_table_function = true;
858 }
859 }
860 if cf.is_table_function {
862 if let Some(ref body) = cf.body {
863 if matches!(body, FunctionBody::StringLiteral(_)) {
864 if let Some(FunctionBody::StringLiteral(sql)) = cf.body.take() {
865 if let Ok(parsed) = crate::parser::Parser::parse_sql(&sql) {
867 if let Some(stmt) = parsed.into_iter().next() {
868 cf.body = Some(FunctionBody::Expression(stmt));
869 } else {
870 cf.body = Some(FunctionBody::StringLiteral(sql));
871 }
872 } else {
873 cf.body = Some(FunctionBody::StringLiteral(sql));
874 }
875 }
876 }
877 }
878 }
879 Ok(Expression::CreateFunction(cf))
880 }
881
882 _ => Ok(expr),
884 }
885 }
886}
887
888#[cfg(feature = "transpile")]
889impl BigQueryDialect {
890 fn transform_data_type(&self, dt: crate::expressions::DataType) -> Result<Expression> {
892 use crate::expressions::DataType;
893 let transformed = match dt {
894 DataType::BigInt { .. } => DataType::Custom {
896 name: "INT64".to_string(),
897 },
898 DataType::Int { .. } => DataType::Custom {
900 name: "INT64".to_string(),
901 },
902 DataType::SmallInt { .. } => DataType::Custom {
904 name: "INT64".to_string(),
905 },
906 DataType::TinyInt { .. } => DataType::Custom {
908 name: "INT64".to_string(),
909 },
910 DataType::Float { .. } => DataType::Custom {
912 name: "FLOAT64".to_string(),
913 },
914 DataType::Double { .. } => DataType::Custom {
916 name: "FLOAT64".to_string(),
917 },
918 DataType::Boolean => DataType::Custom {
920 name: "BOOL".to_string(),
921 },
922 DataType::Char { .. } => DataType::Custom {
924 name: "STRING".to_string(),
925 },
926 DataType::VarChar { .. } => DataType::Custom {
928 name: "STRING".to_string(),
929 },
930 DataType::Text => DataType::Custom {
932 name: "STRING".to_string(),
933 },
934 DataType::String { .. } => DataType::Custom {
936 name: "STRING".to_string(),
937 },
938 DataType::Binary { .. } => DataType::Custom {
940 name: "BYTES".to_string(),
941 },
942 DataType::VarBinary { .. } => DataType::Custom {
944 name: "BYTES".to_string(),
945 },
946 DataType::Blob => DataType::Custom {
948 name: "BYTES".to_string(),
949 },
950 DataType::Decimal { .. } => DataType::Custom {
952 name: "NUMERIC".to_string(),
953 },
954 DataType::Timestamp {
958 timezone: false, ..
959 } => DataType::Custom {
960 name: "TIMESTAMP".to_string(),
961 },
962 DataType::Timestamp { timezone: true, .. } => DataType::Custom {
963 name: "TIMESTAMP".to_string(),
964 },
965 DataType::Uuid => DataType::Custom {
967 name: "STRING".to_string(),
968 },
969 DataType::Custom { ref name } if name.eq_ignore_ascii_case("RECORD") => {
971 DataType::Custom {
972 name: "STRUCT".to_string(),
973 }
974 }
975 DataType::Custom { ref name } if name.eq_ignore_ascii_case("TIMESTAMPTZ") => {
977 DataType::Custom {
978 name: "TIMESTAMP".to_string(),
979 }
980 }
981 DataType::Custom { ref name } if name.eq_ignore_ascii_case("BYTEINT") => {
983 DataType::Custom {
984 name: "INT64".to_string(),
985 }
986 }
987 other => other,
989 };
990 Ok(Expression::DataType(transformed))
991 }
992
993 fn transform_function(&self, f: Function) -> Result<Expression> {
994 let name_upper = f.name.to_uppercase();
995 match name_upper.as_str() {
996 "IFNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
998 original_name: None,
999 expressions: f.args,
1000 inferred_type: None,
1001 }))),
1002
1003 "NVL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1005 original_name: None,
1006 expressions: f.args,
1007 inferred_type: None,
1008 }))),
1009
1010 "ISNULL" if f.args.len() == 2 => Ok(Expression::Coalesce(Box::new(VarArgFunc {
1012 original_name: None,
1013 expressions: f.args,
1014 inferred_type: None,
1015 }))),
1016
1017 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1019 Function::new("STRING_AGG".to_string(), f.args),
1020 ))),
1021
1022 "SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1024 "SUBSTRING".to_string(),
1025 f.args,
1026 )))),
1027
1028 "RANDOM" => Ok(Expression::Rand(Box::new(crate::expressions::Rand {
1030 seed: None,
1031 lower: None,
1032 upper: None,
1033 }))),
1034
1035 "CURRENT_DATE" if f.args.is_empty() => {
1038 Ok(Expression::CurrentDate(crate::expressions::CurrentDate))
1039 }
1040 "CURRENT_DATE" => Ok(Expression::Function(Box::new(Function {
1041 name: "CURRENT_DATE".to_string(),
1042 args: f.args,
1043 distinct: false,
1044 trailing_comments: Vec::new(),
1045 use_bracket_syntax: false,
1046 no_parens: false,
1047 quoted: false,
1048 span: None,
1049 inferred_type: None,
1050 }))),
1051
1052 "NOW" => Ok(Expression::CurrentTimestamp(
1054 crate::expressions::CurrentTimestamp {
1055 precision: None,
1056 sysdate: false,
1057 },
1058 )),
1059
1060 "TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
1062 "PARSE_DATE".to_string(),
1063 f.args,
1064 )))),
1065
1066 "TO_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
1068 "PARSE_TIMESTAMP".to_string(),
1069 f.args,
1070 )))),
1071
1072 "TO_TIME" if f.args.len() == 1 => Ok(Expression::Function(Box::new(Function::new(
1074 "TIME".to_string(),
1075 f.args,
1076 )))),
1077
1078 "DATE_FORMAT" => Ok(Expression::Function(Box::new(Function::new(
1080 "FORMAT_DATE".to_string(),
1081 f.args,
1082 )))),
1083
1084 "POSITION" if f.args.len() == 2 => {
1087 let mut args = f.args;
1088 let first = args.remove(0);
1090 let second = args.remove(0);
1091 Ok(Expression::Function(Box::new(Function::new(
1092 "STRPOS".to_string(),
1093 vec![second, first],
1094 ))))
1095 }
1096
1097 "LEN" if f.args.len() == 1 => Ok(Expression::Length(Box::new(UnaryFunc::new(
1099 f.args.into_iter().next().unwrap(),
1100 )))),
1101
1102 "CEILING" if f.args.len() == 1 => Ok(Expression::Ceil(Box::new(CeilFunc {
1104 this: f.args.into_iter().next().unwrap(),
1105 decimals: None,
1106 to: None,
1107 }))),
1108
1109 "GETDATE" => Ok(Expression::CurrentTimestamp(
1111 crate::expressions::CurrentTimestamp {
1112 precision: None,
1113 sysdate: false,
1114 },
1115 )),
1116
1117 "CARDINALITY" if f.args.len() == 1 => Ok(Expression::ArrayLength(Box::new(
1120 UnaryFunc::new(f.args.into_iter().next().unwrap()),
1121 ))),
1122
1123 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(Function::new(
1127 "GENERATE_ARRAY".to_string(),
1128 f.args,
1129 )))),
1130
1131 "APPROX_DISTINCT" => Ok(Expression::Function(Box::new(Function::new(
1134 "APPROX_COUNT_DISTINCT".to_string(),
1135 f.args,
1136 )))),
1137
1138 "COUNT_IF" => Ok(Expression::Function(Box::new(Function::new(
1140 "COUNTIF".to_string(),
1141 f.args,
1142 )))),
1143
1144 "SHA" => Ok(Expression::Function(Box::new(Function::new(
1146 "SHA1".to_string(),
1147 f.args,
1148 )))),
1149
1150 "SHA2" => Ok(Expression::Function(Box::new(Function::new(
1152 "SHA256".to_string(),
1153 f.args,
1154 )))),
1155
1156 "MD5" => Ok(Expression::Function(Box::new(Function::new(
1159 "MD5".to_string(),
1160 f.args,
1161 )))),
1162
1163 "DATEADD" if f.args.len() == 3 => {
1168 let mut args = f.args;
1169 let unit_expr = args.remove(0);
1170 let amount = args.remove(0);
1171 let date = args.remove(0);
1172 let unit_name = match &unit_expr {
1174 Expression::Identifier(id) => id.name.to_uppercase(),
1175 Expression::Var(v) => v.this.to_uppercase(),
1176 Expression::Column(col) if col.table.is_none() => col.name.name.to_uppercase(),
1177 _ => "DAY".to_string(),
1178 };
1179 let unit = match unit_name.as_str() {
1180 "YEAR" | "YEARS" | "YY" | "YYYY" => crate::expressions::IntervalUnit::Year,
1181 "QUARTER" | "QUARTERS" | "QQ" | "Q" => {
1182 crate::expressions::IntervalUnit::Quarter
1183 }
1184 "MONTH" | "MONTHS" | "MM" | "M" => crate::expressions::IntervalUnit::Month,
1185 "WEEK" | "WEEKS" | "WK" | "WW" => crate::expressions::IntervalUnit::Week,
1186 "DAY" | "DAYS" | "DD" | "D" | "DAYOFMONTH" => {
1187 crate::expressions::IntervalUnit::Day
1188 }
1189 "HOUR" | "HOURS" | "HH" => crate::expressions::IntervalUnit::Hour,
1190 "MINUTE" | "MINUTES" | "MI" | "N" => crate::expressions::IntervalUnit::Minute,
1191 "SECOND" | "SECONDS" | "SS" | "S" => crate::expressions::IntervalUnit::Second,
1192 "MILLISECOND" | "MILLISECONDS" | "MS" => {
1193 crate::expressions::IntervalUnit::Millisecond
1194 }
1195 "MICROSECOND" | "MICROSECONDS" | "US" => {
1196 crate::expressions::IntervalUnit::Microsecond
1197 }
1198 _ => crate::expressions::IntervalUnit::Day,
1199 };
1200 Ok(Expression::DateAdd(Box::new(
1201 crate::expressions::DateAddFunc {
1202 this: date,
1203 interval: amount,
1204 unit,
1205 },
1206 )))
1207 }
1208 "DATE_ADD" => Ok(Expression::Function(Box::new(Function::new(
1209 "DATE_ADD".to_string(),
1210 f.args,
1211 )))),
1212
1213 "DATEDIFF" => Ok(Expression::Function(Box::new(Function::new(
1215 "DATE_DIFF".to_string(),
1216 f.args,
1217 )))),
1218
1219 "TIMESTAMPDIFF" => Ok(Expression::Function(Box::new(Function::new(
1221 "TIMESTAMP_DIFF".to_string(),
1222 f.args,
1223 )))),
1224
1225 "NEWID" | "UUID" => Ok(Expression::Function(Box::new(Function::new(
1232 "GENERATE_UUID".to_string(),
1233 vec![],
1234 )))),
1235
1236 "LEVENSHTEIN" => Ok(Expression::Function(Box::new(Function::new(
1238 "EDIT_DISTANCE".to_string(),
1239 f.args,
1240 )))),
1241
1242 "UNIX_TIMESTAMP" => Ok(Expression::Function(Box::new(Function::new(
1244 "UNIX_SECONDS".to_string(),
1245 f.args,
1246 )))),
1247
1248 "FROM_UNIXTIME" => Ok(Expression::Function(Box::new(Function::new(
1250 "TIMESTAMP_SECONDS".to_string(),
1251 f.args,
1252 )))),
1253
1254 "CHAR_LENGTH" | "CHARACTER_LENGTH" => Ok(Expression::Function(Box::new(
1256 Function::new("LENGTH".to_string(), f.args),
1257 ))),
1258
1259 "OCTET_LENGTH" => Ok(Expression::Function(Box::new(Function::new(
1261 "BYTE_LENGTH".to_string(),
1262 f.args,
1263 )))),
1264
1265 "JSON_EXTRACT_STRING_ARRAY" => Ok(Expression::Function(Box::new(Function::new(
1267 "JSON_VALUE_ARRAY".to_string(),
1268 f.args,
1269 )))),
1270
1271 "SPLIT" if f.args.len() == 1 => {
1276 let mut args = f.args;
1277 args.push(Expression::Literal(Box::new(Literal::String(
1278 ",".to_string(),
1279 ))));
1280 Ok(Expression::Split(Box::new(SplitFunc {
1281 this: args.remove(0),
1282 delimiter: args.remove(0),
1283 })))
1284 }
1285
1286 "SPLIT" if f.args.len() == 2 => {
1288 let mut args = f.args;
1289 Ok(Expression::Split(Box::new(SplitFunc {
1290 this: args.remove(0),
1291 delimiter: args.remove(0),
1292 })))
1293 }
1294
1295 "REGEXP_SUBSTR" if f.args.len() >= 2 => {
1297 let args = if f.args.len() > 4 {
1299 f.args[..4].to_vec()
1300 } else {
1301 f.args
1302 };
1303 Ok(Expression::Function(Box::new(Function::new(
1304 "REGEXP_EXTRACT".to_string(),
1305 args,
1306 ))))
1307 }
1308 "REGEXP_SUBSTR" => Ok(Expression::Function(Box::new(Function::new(
1309 "REGEXP_EXTRACT".to_string(),
1310 f.args,
1311 )))),
1312
1313 "REGEXP_REPLACE" if f.args.len() > 3 => {
1315 let args = f.args[..3].to_vec();
1316 Ok(Expression::Function(Box::new(Function::new(
1317 "REGEXP_REPLACE".to_string(),
1318 args,
1319 ))))
1320 }
1321
1322 "OBJECT_CONSTRUCT_KEEP_NULL" => Ok(Expression::Function(Box::new(Function::new(
1324 "JSON_OBJECT".to_string(),
1325 f.args,
1326 )))),
1327
1328 "EDITDISTANCE" if f.args.len() == 3 => {
1330 let col1 = f.args[0].clone();
1331 let col2 = f.args[1].clone();
1332 let max_dist = f.args[2].clone();
1333 Ok(Expression::Function(Box::new(Function::new(
1334 "EDIT_DISTANCE".to_string(),
1335 vec![
1336 col1,
1337 col2,
1338 Expression::NamedArgument(Box::new(crate::expressions::NamedArgument {
1339 name: crate::expressions::Identifier::new("max_distance".to_string()),
1340 value: max_dist,
1341 separator: crate::expressions::NamedArgSeparator::DArrow,
1342 })),
1343 ],
1344 ))))
1345 }
1346 "EDITDISTANCE" if f.args.len() == 2 => Ok(Expression::Function(Box::new(
1347 Function::new("EDIT_DISTANCE".to_string(), f.args),
1348 ))),
1349
1350 "HEX_DECODE_BINARY" => Ok(Expression::Function(Box::new(Function::new(
1352 "FROM_HEX".to_string(),
1353 f.args,
1354 )))),
1355
1356 "PARSE_DATE"
1359 | "PARSE_DATETIME"
1360 | "PARSE_TIMESTAMP"
1361 | "SAFE.PARSE_DATE"
1362 | "SAFE.PARSE_DATETIME"
1363 | "SAFE.PARSE_TIMESTAMP" => {
1364 let args = self.normalize_time_format_args(f.args);
1365 Ok(Expression::Function(Box::new(Function {
1366 name: f.name,
1367 args,
1368 distinct: f.distinct,
1369 no_parens: f.no_parens,
1370 trailing_comments: f.trailing_comments,
1371 quoted: f.quoted,
1372 use_bracket_syntax: f.use_bracket_syntax,
1373 span: None,
1374 inferred_type: None,
1375 })))
1376 }
1377
1378 "GET_PATH" if f.args.len() == 2 => {
1380 let mut args = f.args;
1381 let this = args.remove(0);
1382 let path = args.remove(0);
1383 let json_path = match &path {
1384 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::String(_)) => {
1385 let Literal::String(s) = lit.as_ref() else {
1386 unreachable!()
1387 };
1388 let normalized = if s.starts_with('$') {
1389 s.clone()
1390 } else if s.starts_with('[') {
1391 format!("${}", s)
1392 } else {
1393 format!("$.{}", s)
1394 };
1395 Expression::Literal(Box::new(Literal::String(normalized)))
1396 }
1397 _ => path,
1398 };
1399 Ok(Expression::JsonExtract(Box::new(JsonExtractFunc {
1400 this,
1401 path: json_path,
1402 returning: None,
1403 arrow_syntax: false,
1404 hash_arrow_syntax: false,
1405 wrapper_option: None,
1406 quotes_option: None,
1407 on_scalar_string: false,
1408 on_error: None,
1409 })))
1410 }
1411
1412 _ => Ok(Expression::Function(Box::new(f))),
1414 }
1415 }
1416
1417 fn transform_aggregate_function(
1418 &self,
1419 f: Box<crate::expressions::AggregateFunction>,
1420 ) -> Result<Expression> {
1421 let name_upper = f.name.to_uppercase();
1422 match name_upper.as_str() {
1423 "GROUP_CONCAT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
1425 Function::new("STRING_AGG".to_string(), f.args),
1426 ))),
1427
1428 _ => Ok(Expression::AggregateFunction(f)),
1430 }
1431 }
1432
1433 fn transform_method_call(&self, mc: crate::expressions::MethodCall) -> Result<Expression> {
1436 use crate::expressions::MethodCall;
1437
1438 if let Expression::Column(ref col) = mc.this {
1440 if col.name.name.eq_ignore_ascii_case("SAFE") {
1441 let method_upper = mc.method.name.to_uppercase();
1442 if method_upper == "PARSE_DATE"
1443 || method_upper == "PARSE_DATETIME"
1444 || method_upper == "PARSE_TIMESTAMP"
1445 {
1446 let args = self.normalize_time_format_args(mc.args);
1448 return Ok(Expression::MethodCall(Box::new(MethodCall {
1449 this: mc.this,
1450 method: mc.method,
1451 args,
1452 })));
1453 }
1454 }
1455 }
1456
1457 Ok(Expression::MethodCall(Box::new(mc)))
1459 }
1460
1461 fn normalize_time_format_args(&self, args: Vec<Expression>) -> Vec<Expression> {
1464 args.into_iter()
1465 .enumerate()
1466 .map(|(i, arg)| {
1467 if i == 0 {
1469 if let Expression::Literal(ref lit) = arg {
1470 if let Literal::String(s) = lit.as_ref() {
1471 let normalized = self.normalize_time_format(&s);
1472 return Expression::Literal(Box::new(Literal::String(normalized)));
1473 }
1474 }
1475 }
1476 arg
1477 })
1478 .collect()
1479 }
1480
1481 fn normalize_time_format(&self, format: &str) -> String {
1485 format.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T")
1486 }
1487
1488 fn bq_cast_format_to_strftime(format_expr: &Expression) -> Expression {
1491 use crate::expressions::Literal;
1492 if let Expression::Literal(lit) = format_expr {
1493 if let Literal::String(s) = lit.as_ref() {
1494 let result = s
1495 .replace("YYYYMMDD", "%Y%m%d")
1496 .replace("YYYY", "%Y")
1497 .replace("YY", "%y")
1498 .replace("MONTH", "%B")
1499 .replace("MON", "%b")
1500 .replace("MM", "%m")
1501 .replace("DD", "%d")
1502 .replace("HH24", "%H")
1503 .replace("HH12", "%I")
1504 .replace("HH", "%I")
1505 .replace("MI", "%M")
1506 .replace("SSTZH", "%S%z")
1507 .replace("SS", "%S")
1508 .replace("TZH", "%z");
1509 let normalized = result.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T");
1511 return Expression::Literal(Box::new(Literal::String(normalized)));
1512 }
1513 }
1514 format_expr.clone()
1515 }
1516}
1517
1518#[cfg(test)]
1519mod tests {
1520 use super::*;
1521 use crate::dialects::Dialect;
1522 use crate::parse_one;
1523
1524 fn transpile_to_bigquery(sql: &str) -> String {
1525 let dialect = Dialect::get(DialectType::Generic);
1526 let result = dialect
1527 .transpile(sql, DialectType::BigQuery)
1528 .expect("Transpile failed");
1529 result[0].clone()
1530 }
1531
1532 #[test]
1533 fn test_ifnull_identity() {
1534 let result = transpile_to_bigquery("SELECT IFNULL(a, b)");
1536 assert!(
1537 result.contains("COALESCE"),
1538 "Expected COALESCE, got: {}",
1539 result
1540 );
1541 }
1542
1543 #[test]
1544 fn test_nvl_to_ifnull() {
1545 let result = transpile_to_bigquery("SELECT NVL(a, b)");
1547 assert!(
1548 result.contains("IFNULL"),
1549 "Expected IFNULL, got: {}",
1550 result
1551 );
1552 }
1553
1554 #[test]
1555 fn test_try_cast_to_safe_cast() {
1556 let result = transpile_to_bigquery("SELECT TRY_CAST(a AS INT)");
1557 assert!(
1558 result.contains("SAFE_CAST"),
1559 "Expected SAFE_CAST, got: {}",
1560 result
1561 );
1562 }
1563
1564 #[test]
1565 fn test_random_to_rand() {
1566 let result = transpile_to_bigquery("SELECT RANDOM()");
1567 assert!(result.contains("RAND"), "Expected RAND, got: {}", result);
1568 }
1569
1570 #[test]
1571 fn test_basic_select() {
1572 let result = transpile_to_bigquery("SELECT a, b FROM users WHERE id = 1");
1573 assert!(result.contains("SELECT"));
1574 assert!(result.contains("FROM users"));
1575 }
1576
1577 #[test]
1578 fn test_group_concat_to_string_agg() {
1579 let result = transpile_to_bigquery("SELECT GROUP_CONCAT(name)");
1580 assert!(
1581 result.contains("STRING_AGG"),
1582 "Expected STRING_AGG, got: {}",
1583 result
1584 );
1585 }
1586
1587 #[test]
1588 fn test_generate_series_to_generate_array() {
1589 let result = transpile_to_bigquery("SELECT GENERATE_SERIES(1, 10)");
1590 assert!(
1591 result.contains("GENERATE_ARRAY"),
1592 "Expected GENERATE_ARRAY, got: {}",
1593 result
1594 );
1595 }
1596
1597 #[test]
1598 fn test_backtick_identifiers() {
1599 let dialect = BigQueryDialect;
1601 let config = dialect.generator_config();
1602 assert_eq!(config.identifier_quote, '`');
1603 }
1604
1605 fn bigquery_identity(sql: &str, expected: &str) {
1606 let dialect = Dialect::get(DialectType::BigQuery);
1607 let ast = dialect.parse(sql).expect("Parse failed");
1608 let transformed = dialect.transform(ast[0].clone()).expect("Transform failed");
1609 let result = dialect.generate(&transformed).expect("Generate failed");
1610 assert_eq!(result, expected, "SQL: {}", sql);
1611 }
1612
1613 #[test]
1614 fn test_safe_namespace_parses_as_function() {
1615 let expr = parse_one(
1616 "SELECT SAFE.PARSE_JSON(data) AS json_data FROM t",
1617 DialectType::BigQuery,
1618 )
1619 .expect("parse");
1620
1621 let Expression::Select(select) = expr else {
1622 panic!("expected SELECT");
1623 };
1624 let Expression::Alias(alias) = &select.expressions[0] else {
1625 panic!("expected alias");
1626 };
1627 let Expression::Function(function) = &alias.this else {
1628 panic!("expected SAFE namespace call to parse as Function");
1629 };
1630
1631 assert_eq!(function.name, "SAFE.PARSE_JSON");
1632 assert_eq!(function.args.len(), 1);
1633 }
1634
1635 #[test]
1636 fn test_safe_namespace_identity() {
1637 bigquery_identity("SAFE.PARSE_JSON(data)", "SAFE.PARSE_JSON(data)");
1638 bigquery_identity(
1639 "SAFE.PARSE_DATE('%Y-%m-%d', date_col)",
1640 "SAFE.PARSE_DATE('%F', date_col)",
1641 );
1642 bigquery_identity("SAFE.DIVIDE(a, b)", "SAFE.DIVIDE(a, b)");
1643 }
1644
1645 #[test]
1646 fn test_cast_char_to_string() {
1647 bigquery_identity("CAST(x AS CHAR)", "CAST(x AS STRING)");
1648 }
1649
1650 #[test]
1651 fn test_cast_varchar_to_string() {
1652 bigquery_identity("CAST(x AS VARCHAR)", "CAST(x AS STRING)");
1653 }
1654
1655 #[test]
1656 fn test_cast_nchar_to_string() {
1657 bigquery_identity("CAST(x AS NCHAR)", "CAST(x AS STRING)");
1658 }
1659
1660 #[test]
1661 fn test_cast_nvarchar_to_string() {
1662 bigquery_identity("CAST(x AS NVARCHAR)", "CAST(x AS STRING)");
1663 }
1664
1665 #[test]
1666 fn test_cast_timestamptz_to_timestamp() {
1667 bigquery_identity("CAST(x AS TIMESTAMPTZ)", "CAST(x AS TIMESTAMP)");
1668 }
1669
1670 #[test]
1671 fn test_cast_record_to_struct() {
1672 bigquery_identity("CAST(x AS RECORD)", "CAST(x AS STRUCT)");
1673 }
1674
1675 #[test]
1676 fn test_json_literal_to_parse_json() {
1677 bigquery_identity(
1679 "SELECT JSON '\"foo\"' AS json_data",
1680 "SELECT PARSE_JSON('\"foo\"') AS json_data",
1681 );
1682 }
1683
1684 #[test]
1685 fn test_grant_as_alias_not_quoted() {
1686 bigquery_identity(
1688 "SELECT GRANT FROM (SELECT 'input' AS GRANT)",
1689 "SELECT GRANT FROM (SELECT 'input' AS GRANT)",
1690 );
1691 }
1692
1693 #[test]
1694 fn test_timestamp_literal_to_cast() {
1695 bigquery_identity(
1697 "CREATE VIEW `d.v` OPTIONS (expiration_timestamp=TIMESTAMP '2020-01-02T04:05:06.007Z') AS SELECT 1 AS c",
1698 "CREATE VIEW `d.v` OPTIONS (expiration_timestamp=CAST('2020-01-02T04:05:06.007Z' AS TIMESTAMP)) AS SELECT 1 AS c"
1699 );
1700 }
1701
1702 #[test]
1703 fn test_date_literal_to_cast_in_extract() {
1704 bigquery_identity(
1706 "EXTRACT(WEEK(THURSDAY) FROM DATE '2013-12-25')",
1707 "EXTRACT(WEEK(THURSDAY) FROM CAST('2013-12-25' AS DATE))",
1708 );
1709 }
1710
1711 #[test]
1712 fn test_json_object_with_json_literals() {
1713 bigquery_identity(
1715 "SELECT JSON_OBJECT('a', JSON '10') AS json_data",
1716 "SELECT JSON_OBJECT('a', PARSE_JSON('10')) AS json_data",
1717 );
1718 }
1719
1720 #[test]
1725 fn test_safe_parse_date_format_normalization() {
1726 bigquery_identity(
1728 "SAFE.PARSE_DATE('%Y-%m-%d', '2024-01-15')",
1729 "SAFE.PARSE_DATE('%F', '2024-01-15')",
1730 );
1731 }
1732
1733 #[test]
1734 fn test_safe_parse_datetime_format_normalization() {
1735 bigquery_identity(
1737 "SAFE.PARSE_DATETIME('%Y-%m-%d %H:%M:%S', '2024-01-15 10:30:00')",
1738 "SAFE.PARSE_DATETIME('%F %T', '2024-01-15 10:30:00')",
1739 );
1740 }
1741
1742 #[test]
1743 fn test_safe_parse_timestamp_format_normalization() {
1744 bigquery_identity(
1746 "SAFE.PARSE_TIMESTAMP('%Y-%m-%d %H:%M:%S', '2024-01-15 10:30:00')",
1747 "SAFE.PARSE_TIMESTAMP('%F %T', '2024-01-15 10:30:00')",
1748 );
1749 }
1750
1751 #[test]
1752 fn test_datetime_literal_to_cast() {
1753 bigquery_identity(
1755 "LAST_DAY(DATETIME '2008-11-10 15:30:00', WEEK(SUNDAY))",
1756 "LAST_DAY(CAST('2008-11-10 15:30:00' AS DATETIME), WEEK)",
1757 );
1758 }
1759
1760 #[test]
1761 fn test_last_day_week_modifier_stripped() {
1762 bigquery_identity("LAST_DAY(col, WEEK(MONDAY))", "LAST_DAY(col, WEEK)");
1764 }
1765
1766 #[test]
1767 fn test_hash_line_comment_parses() {
1768 let result = parse_one("SELECT 1 as a #hello world", DialectType::BigQuery);
1771 assert!(result.is_ok(), "Expected parse to succeed, got: {result:?}");
1772 }
1773}