@@ -1209,35 +1209,35 @@ def objectinsert_sql(self, expression: exp.ObjectInsert) -> str:
12091209
12101210 return self .func ("STRUCT_INSERT" , this , kv_sql )
12111211
1212- def startswith_sql (self , expression : exp .StartsWith ) -> str :
1213- this = expression .this
1214- expr = expression .expression
1212+ def _prepare_startswith_arg (self , arg : exp .Expression ) -> None :
1213+ """Prepare argument for STARTS_WITH by converting to VARCHAR.
1214+
1215+ ByteString literals are converted to regular string literals to avoid
1216+ BLOB casting by the generator. Non-VARCHAR types are cast to VARCHAR.
1217+ """
1218+ # Convert ByteString to String literal before generation
1219+ # ByteStrings get typed as UNKNOWN and would be wrapped in CAST(...AS BLOB) by generator
1220+ if isinstance (arg , exp .ByteString ):
1221+ arg .replace (exp .Literal .string (arg .this ))
1222+ # Cast non-VARCHAR types to VARCHAR
1223+ elif arg .type and not arg .is_type (exp .DataType .Type .VARCHAR , exp .DataType .Type .UNKNOWN ):
1224+ arg .replace (exp .cast (arg , exp .DataType .Type .VARCHAR ))
12151225
1216- if not this .type :
1226+ def startswith_sql (self , expression : exp .StartsWith ) -> str :
1227+ # Annotate types if needed for type-based casting
1228+ if not expression .this .type :
12171229 from sqlglot .optimizer .annotate_types import annotate_types
12181230
1219- this = annotate_types (this , dialect = self .dialect )
1231+ annotate_types (expression . this , dialect = self .dialect )
12201232
1221- if not expr .type :
1233+ if not expression . expression .type :
12221234 from sqlglot .optimizer .annotate_types import annotate_types
12231235
1224- expr = annotate_types (expr , dialect = self .dialect )
1236+ annotate_types (expression . expression , dialect = self .dialect )
12251237
1226- if isinstance (expression .this , exp .ByteString ):
1227- expression .this .replace (exp .Literal .string (expression .this .this ))
1228- elif this .type and not this .is_type (
1229- exp .DataType .Type .VARCHAR , exp .DataType .Type .UNKNOWN
1230- ):
1231- expression .this .replace (exp .cast (expression .this , exp .DataType .Type .VARCHAR ))
1232-
1233- if isinstance (expression .expression , exp .ByteString ):
1234- expression .expression .replace (exp .Literal .string (expression .expression .this ))
1235- elif expr .type and not expr .is_type (
1236- exp .DataType .Type .VARCHAR , exp .DataType .Type .UNKNOWN
1237- ):
1238- expression .expression .replace (
1239- exp .cast (expression .expression , exp .DataType .Type .VARCHAR )
1240- )
1238+ # Prepare both arguments for STARTS_WITH
1239+ self ._prepare_startswith_arg (expression .this )
1240+ self ._prepare_startswith_arg (expression .expression )
12411241
12421242 return self .func ("STARTS_WITH" , expression .this , expression .expression )
12431243
0 commit comments