@@ -128,6 +128,10 @@ def visit_argument(self, exp: Argument) -> TVisited:
128128 def visit_lambda (self , exp : Lambda ) -> TVisited :
129129 raise NotImplementedError
130130
131+ @abstractmethod
132+ def visit_dangerous_raw_sql (self , exp : DangerousRawSQL ) -> TVisited :
133+ raise NotImplementedError
134+
131135
132136class NoopVisitor (ExpressionVisitor [None ]):
133137 """A noop visitor that will traverse every node but will not
@@ -159,6 +163,9 @@ def visit_argument(self, exp: Argument) -> None:
159163 def visit_lambda (self , exp : Lambda ) -> None :
160164 return exp .transformation .accept (self )
161165
166+ def visit_dangerous_raw_sql (self , exp : DangerousRawSQL ) -> None :
167+ return None
168+
162169
163170class StringifyVisitor (ExpressionVisitor [str ]):
164171 """Visitor implementation to turn an expression into a string format
@@ -204,9 +211,7 @@ def visit_literal(self, exp: Literal) -> str:
204211
205212 def visit_column (self , exp : Column ) -> str :
206213 column_str = (
207- f"{ exp .table_name } .{ exp .column_name } "
208- if exp .table_name
209- else f"{ exp .column_name } "
214+ f"{ exp .table_name } .{ exp .column_name } " if exp .table_name else f"{ exp .column_name } "
210215 )
211216 return f"{ self ._get_line_prefix ()} { column_str } { self ._get_alias_str (exp )} "
212217
@@ -256,6 +261,10 @@ def visit_lambda(self, exp: Lambda) -> str:
256261 self .__level -= 1
257262 return f"{ self ._get_line_prefix ()} ({ params_str } ) ->\n { transformation_str } \n { self ._get_line_prefix ()} { self ._get_alias_str (exp )} "
258263
264+ def visit_dangerous_raw_sql (self , exp : DangerousRawSQL ) -> str :
265+ sql_repr = repr (exp .sql )
266+ return f"{ self ._get_line_prefix ()} DangerousRawSQL({ sql_repr } ){ self ._get_alias_str (exp )} "
267+
259268
260269class ColumnVisitor (ExpressionVisitor [set [str ]]):
261270 def __init__ (self ) -> None :
@@ -287,6 +296,9 @@ def visit_argument(self, exp: Argument) -> set[str]:
287296 def visit_lambda (self , exp : Lambda ) -> set [str ]:
288297 return exp .transformation .accept (self )
289298
299+ def visit_dangerous_raw_sql (self , exp : DangerousRawSQL ) -> set [str ]:
300+ return self .columns
301+
290302
291303OptionalScalarType = Union [None , bool , str , float , int , date , datetime ]
292304
@@ -335,10 +347,7 @@ def accept(self, visitor: ExpressionVisitor[TVisited]) -> TVisited:
335347 def functional_eq (self , other : Expression ) -> bool :
336348 if not isinstance (other , self .__class__ ):
337349 return False
338- return (
339- self .table_name == other .table_name
340- and self .column_name == other .column_name
341- )
350+ return self .table_name == other .table_name and self .column_name == other .column_name
342351
343352
344353@dataclass (frozen = True , repr = _AUTO_REPR )
@@ -382,9 +391,7 @@ def __iter__(self) -> Iterator[Expression]:
382391 def functional_eq (self , other : Expression ) -> bool :
383392 if not isinstance (other , self .__class__ ):
384393 return False
385- return self .column .functional_eq (other .column ) and self .key .functional_eq (
386- other .key
387- )
394+ return self .column .functional_eq (other .column ) and self .key .functional_eq (other .key )
388395
389396
390397@dataclass (frozen = True , repr = _AUTO_REPR )
@@ -572,3 +579,31 @@ def functional_eq(self, other: Expression) -> bool:
572579 if not self .transformation .functional_eq (other .transformation ):
573580 return False
574581 return True
582+
583+
584+ @dataclass (frozen = True , repr = _AUTO_REPR )
585+ class DangerousRawSQL (Expression ):
586+ """
587+ Represents raw SQL that should be passed through directly to ClickHouse
588+ without any escaping or validation. This is intended for query optimization
589+ scenarios where the SQL is generated programmatically and already safe.
590+
591+ WARNING: This expression type bypasses all safety checks. Only use when
592+ the SQL content is guaranteed to be safe and properly formatted.
593+ """
594+
595+ sql : str
596+
597+ def transform (self , func : Callable [[Expression ], Expression ]) -> Expression :
598+ return func (self )
599+
600+ def __iter__ (self ) -> Iterator [Expression ]:
601+ yield self
602+
603+ def accept (self , visitor : ExpressionVisitor [TVisited ]) -> TVisited :
604+ return visitor .visit_dangerous_raw_sql (self )
605+
606+ def functional_eq (self , other : Expression ) -> bool :
607+ if not isinstance (other , self .__class__ ):
608+ return False
609+ return self .sql == other .sql
0 commit comments