4141 # Nullable types are problematic
4242 "clickhouse" ,
4343}
44- MOTHERDUCK_TOKEN_REGEX = re .compile (r"(\?motherduck_token=)(\S*)" )
44+ MOTHERDUCK_TOKEN_REGEX = re .compile (r"(\?|\&)( motherduck_token=)(\S*)" )
4545
4646
4747class ConnectionConfig (abc .ABC , BaseConfig ):
@@ -134,7 +134,6 @@ class DuckDBAttachOptions(BaseConfig):
134134 type : str
135135 path : str
136136 read_only : bool = False
137- token : t .Optional [str ] = None
138137
139138 def to_sql (self , alias : str ) -> str :
140139 options = []
@@ -144,14 +143,18 @@ def to_sql(self, alias: str) -> str:
144143 options .append (f"TYPE { self .type .upper ()} " )
145144 if self .read_only :
146145 options .append ("READ_ONLY" )
146+ options_sql = f" ({ ', ' .join (options )} )" if options else ""
147+ alias_sql = ""
147148 # TODO: Add support for Postgres schema. Currently adding it blocks access to the information_schema
148- alias_sql = (
149+ if self . type == "motherduck" :
149150 # MotherDuck does not support aliasing
150- f" AS { alias } " if not (self .type == "motherduck" or self .path .startswith ("md:" )) else ""
151- )
152- options_sql = f" ({ ', ' .join (options )} )" if options else ""
153- token_sql = "?motherduck_token=" + self .token if self .token else ""
154- return f"ATTACH '{ self .path } { token_sql } '{ alias_sql } { options_sql } "
151+ if (md_db := self .path .replace ("md:" , "" )) != alias .replace ('"' , "" ):
152+ raise ConfigError (
153+ f"MotherDuck does not support assigning an alias different from the database name { md_db } ."
154+ )
155+ else :
156+ alias_sql += f" AS { alias } "
157+ return f"ATTACH '{ self .path } '{ alias_sql } { options_sql } "
155158
156159
157160class BaseDuckDBConnectionConfig (ConnectionConfig ):
@@ -293,16 +296,20 @@ def init(cursor: duckdb.DuckDBPyConnection) -> None:
293296 query = f"ATTACH '{ path_options } '"
294297 if not path_options .startswith ("md:" ):
295298 query += f" AS { alias } "
296- elif self .token :
297- query += f"?motherduck_token={ self .token } "
298299 cursor .execute (query )
299300 except BinderException as e :
300301 # If a user tries to create a catalog pointing at `:memory:` and with the name `memory`
301302 # then we don't want to raise since this happens by default. They are just doing this to
302303 # set it as the default catalog.
303- if not (
304- 'database with name "memory" already exists' in str (e )
305- and path_options == ":memory:"
304+ # If a user tried to attach a MotherDuck database/share which has already by attached via
305+ # `ATTACH 'md:'`, then we don't want to raise since this is expected.
306+ if (
307+ not (
308+ 'database with name "memory" already exists' in str (e )
309+ and path_options == ":memory:"
310+ )
311+ and f"""database with name "{ path_options .path .replace ('md:' , '' )} " already exists"""
312+ not in str (e )
306313 ):
307314 raise e
308315 if i == 0 and not getattr (self , "database" , None ):
@@ -355,7 +362,9 @@ def get_catalog(self) -> t.Optional[str]:
355362 return None
356363
357364 def _mask_motherduck_token (self , string : str ) -> str :
358- return MOTHERDUCK_TOKEN_REGEX .sub (lambda m : f"{ m .group (1 )} { '*' * len (m .group (2 ))} " , string )
365+ return MOTHERDUCK_TOKEN_REGEX .sub (
366+ lambda m : f"{ m .group (1 )} { m .group (2 )} { '*' * len (m .group (3 ))} " , string
367+ )
359368
360369
361370class MotherDuckConnectionConfig (BaseDuckDBConnectionConfig ):
@@ -373,11 +382,12 @@ def _static_connection_kwargs(self) -> t.Dict[str, t.Any]:
373382 from sqlmesh import __version__
374383
375384 custom_user_agent_config = {"custom_user_agent" : f"SQLMesh/{ __version__ } " }
376- if not self .database :
377- return {"config" : custom_user_agent_config }
378- connection_str = f"md:{ self .database or '' } "
385+ connection_str = "md:"
386+ if self .database :
387+ # Attach single MD database instead of all databases on the account
388+ connection_str += f"{ self .database } ?attach_mode=single"
379389 if self .token :
380- connection_str += f"? motherduck_token={ self .token } "
390+ connection_str += f"{ '&' if self . database else '?' } motherduck_token={ self .token } "
381391 return {"database" : connection_str , "config" : custom_user_agent_config }
382392
383393
0 commit comments