@@ -642,34 +642,47 @@ def _prepare_bitshift_for_duckdb(expression: exp.Expression) -> exp.Expression:
642642 """
643643 Transform bitwise shift expressions for DuckDB by injecting INT128 casts.
644644
645- Modifies the AST in-place to:
646- 1. Unwrap BINARY/BLOB casts and replace with INT128 cast
647- 2. Mark HexString literals as integers (sets is_integer flag for proper rendering)
648- 3. Apply INT128 cast if requires_int128=True (from Snowflake)
645+ DuckDB's bitwise shift operators don't work with BLOB/BINARY types, so we cast
646+ them to INT128 for integer arithmetic.
649647
650- After transformation, falls back to the base binary generator .
648+ Note: Assumes type annotation has been applied with the source dialect .
651649 """
652- needs_int128 = False
653-
654- # Check if left operand is a BINARY/BLOB cast - unwrap it
650+ # Unwrap BINARY/VARBINARY casts that DuckDB can't handle
655651 if isinstance (expression .this , exp .Cast ) and _is_binary (expression .this ):
656- # Replace Cast(value, BINARY) with just the inner value
657652 expression .this .replace (expression .this .this )
658- needs_int128 = True
659-
660- # Mark HexString as integer so DuckDB renders it as decimal instead of BLOB
661- if isinstance (expression .this , exp .HexString ):
662- expression .this .set ("is_integer" , True )
663- needs_int128 = True
664653
665- # Apply INT128 cast if needed
666- if needs_int128 or expression .args .get ("requires_int128" ):
654+ # Check if the input is a BLOB/BINARY type (using is_type) or requires INT128
655+ # If so, cast to INT128 for integer arithmetic
656+ if _is_binary (expression .this ) or expression .args .get ("requires_int128" ):
667657 expression .this .replace (exp .cast (expression .this , exp .DataType .Type .INT128 ))
668658
659+ # Wrap in parentheses if parent is a bitwise operator to "fix" DuckDB precedence issue
660+ # DuckDB parses: a << b | c << d as (a << b | c) << d
661+ if isinstance (expression .parent , (exp .BitwiseAnd , exp .BitwiseOr , exp .BitwiseXor )):
662+ return exp .paren (expression , copy = False )
663+
669664 return expression
670665
671666
672- def _floor_sql (self : DuckDB .Generator , expression : exp .Floor ) -> str :
667+ def _scale_rounding_sql (
668+ self : DuckDB .Generator ,
669+ expression : exp .Expression ,
670+ rounding_func : type [exp .Expression ],
671+ ) -> str | None :
672+ """
673+ Handle scale parameter transformation for rounding functions.
674+
675+ DuckDB doesn't support the scale parameter for certain functions (e.g., FLOOR, CEIL),
676+ so we transform: FUNC(x, n) to ROUND(FUNC(x * 10^n) / 10^n, n)
677+
678+ Args:
679+ self: The DuckDB generator instance
680+ expression: The expression to transform (must have 'this', 'decimals', and 'to' args)
681+ rounding_func: The rounding function class to use in the transformation
682+
683+ Returns:
684+ The transformed SQL string if decimals parameter exists, None otherwise
685+ """
673686 decimals = expression .args .get ("decimals" )
674687
675688 if decimals is None or expression .args .get ("to" ) is not None :
@@ -1190,8 +1203,10 @@ class Generator(generator.Generator):
11901203 ),
11911204 exp .BitwiseAnd : lambda self , e : self ._bitwise_op (e , "&" ),
11921205 exp .BitwiseAndAgg : _bitwise_agg_sql ,
1206+ exp .BitwiseLeftShift : transforms .preprocess ([_prepare_bitshift_for_duckdb ]),
11931207 exp .BitwiseOr : lambda self , e : self ._bitwise_op (e , "|" ),
11941208 exp .BitwiseOrAgg : _bitwise_agg_sql ,
1209+ exp .BitwiseRightShift : transforms .preprocess ([_prepare_bitshift_for_duckdb ]),
11951210 exp .BitwiseXorAgg : _bitwise_agg_sql ,
11961211 exp .CommentColumnConstraint : no_comment_column_constraint_sql ,
11971212 exp .CosineDistance : rename_func ("LIST_COSINE_DISTANCE" ),
0 commit comments