22
33from __future__ import annotations
44
5- import re
65import warnings
76from typing import TYPE_CHECKING , Any , Literal , cast
87from urllib .parse import unquote as _url_unquote
@@ -79,8 +78,48 @@ def _after_drop(
7978
8079
8180# Register DDL events globally for all Table objects
82- event .listen (Table , "after_create" , _after_create )
83- event .listen (Table , "after_drop" , _after_drop )
81+ event .listen (Table , "after_create" , _after_create , propagate = False )
82+ event .listen (Table , "after_drop" , _after_drop , propagate = False )
83+
84+
85+ def _normalize_statement_whitespace_quote_aware (statement : str ) -> str :
86+ out : list [str ] = []
87+ in_quote = False
88+ quote_char = ""
89+ i = 0
90+ length = len (statement )
91+
92+ while i < length :
93+ ch = statement [i ]
94+
95+ if in_quote :
96+ out .append (ch )
97+ if ch == quote_char :
98+ if i + 1 < length and statement [i + 1 ] == quote_char :
99+ out .append (statement [i + 1 ])
100+ i += 1
101+ else :
102+ in_quote = False
103+ i += 1
104+ continue
105+
106+ if ch in ("'" , '"' ):
107+ in_quote = True
108+ quote_char = ch
109+ out .append (ch )
110+ i += 1
111+ continue
112+
113+ if ch .isspace ():
114+ if out and out [- 1 ] != " " :
115+ out .append (" " )
116+ i += 1
117+ continue
118+
119+ out .append (ch )
120+ i += 1
121+
122+ return "" .join (out ).strip ()
84123
85124
86125class ExcelDialect ( # type: ignore[misc] # pyright: ignore[reportIncompatibleMethodOverride]
@@ -153,7 +192,7 @@ def create_connect_args(self, url: URL) -> ConnectArgsType:
153192 excel:////absolute/path.xlsx → file_path="/absolute/path.xlsx"
154193 """
155194 # url.database contains the path after the third slash
156- file_path = url .database
195+ file_path = _url_unquote ( url .database ) if url . database else None
157196 if not file_path :
158197 raise ValueError ("No file path in URL. Use excel:///path/to/file.xlsx" )
159198
@@ -193,7 +232,7 @@ def do_execute(
193232 context : Any = None ,
194233 ) -> None :
195234 """Execute a statement, normalizing whitespace for excel-dbapi."""
196- normalized = re . sub ( r"\s+" , " " , statement ). strip ( )
235+ normalized = _normalize_statement_whitespace_quote_aware ( statement )
197236 cursor .execute (normalized , parameters )
198237 self ._sync_alter_table_metadata (cursor , normalized )
199238
@@ -204,7 +243,7 @@ def do_execute_no_params(
204243 context : Any = None ,
205244 ) -> None :
206245 """Execute a statement with no parameters."""
207- normalized = re . sub ( r"\s+" , " " , statement ). strip ( )
246+ normalized = _normalize_statement_whitespace_quote_aware ( statement )
208247 cursor .execute (normalized , None )
209248 self ._sync_alter_table_metadata (cursor , normalized )
210249
0 commit comments