@@ -58,6 +58,18 @@ def _pl_operation_to_sql(op: str) -> str:
5858 raise NotImplementedError (op )
5959
6060
61+ def _escape_sql_identifier (identifier : str ) -> str :
62+ """
63+ Escape SQL identifiers by doubling any double quotes and wrapping in double quotes.
64+
65+ Example:
66+ >>> _escape_sql_identifier('column"name')
67+ '"column""name"'
68+ """
69+ escaped = identifier .replace ('"' , '""' )
70+ return f'"{ escaped } "'
71+
72+
6173def _pl_tree_to_sql (tree : dict ) -> str :
6274 """
6375 Recursively convert a Polars expression tree (as JSON) to a SQL string.
@@ -96,7 +108,7 @@ def _pl_tree_to_sql(tree: dict) -> str:
96108 if node_type == "Column" :
97109 # A reference to a column name
98110 # Wrap in quotes to handle special characters
99- return f'" { subtree } "'
111+ return _escape_sql_identifier ( subtree )
100112
101113 if node_type in ("Literal" , "Dyn" ):
102114 # Recursively process dynamic or literal values
@@ -197,7 +209,7 @@ def source_generator(
197209 duck_predicate = None
198210 relation_final = relation
199211 if with_columns is not None :
200- cols = "," .join (f'" { col } "' for col in with_columns )
212+ cols = "," .join (map ( _escape_sql_identifier , with_columns ) )
201213 relation_final = relation_final .project (cols )
202214 if n_rows is not None :
203215 relation_final = relation_final .limit (n_rows )
0 commit comments