1# engine/mock.py
2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8from __future__ import annotations
9
10from operator import attrgetter
11import typing
12from typing import Any
13from typing import Callable
14from typing import cast
15from typing import Optional
16from typing import Type
17from typing import Union
18
19from . import url as _url
20from .. import util
21
22
23if typing.TYPE_CHECKING:
24 from .base import Engine
25 from .interfaces import _CoreAnyExecuteParams
26 from .interfaces import CoreExecuteOptionsParameter
27 from .interfaces import Dialect
28 from .url import URL
29 from ..sql.base import Executable
30 from ..sql.ddl import InvokeDDLBase
31 from ..sql.schema import HasSchemaAttr
32 from ..sql.visitors import Visitable
33
34
35class MockConnection:
36 def __init__(self, dialect: Dialect, execute: Callable[..., Any]):
37 self._dialect = dialect
38 self._execute_impl = execute
39
40 engine: Engine = cast(Any, property(lambda s: s))
41 dialect: Dialect = cast(Any, property(attrgetter("_dialect")))
42 name: str = cast(Any, property(lambda s: s._dialect.name))
43
44 def connect(self, **kwargs: Any) -> MockConnection:
45 return self
46
47 def schema_for_object(self, obj: HasSchemaAttr) -> Optional[str]:
48 return obj.schema
49
50 def execution_options(self, **kw: Any) -> MockConnection:
51 return self
52
53 def _run_ddl_visitor(
54 self,
55 visitorcallable: Type[InvokeDDLBase],
56 element: Visitable,
57 **kwargs: Any,
58 ) -> None:
59 kwargs["checkfirst"] = False
60 visitorcallable(
61 dialect=self.dialect, connection=self, **kwargs
62 ).traverse_single(element)
63
64 def execute(
65 self,
66 obj: Executable,
67 parameters: Optional[_CoreAnyExecuteParams] = None,
68 execution_options: Optional[CoreExecuteOptionsParameter] = None,
69 ) -> Any:
70 return self._execute_impl(obj, parameters)
71
72
73def create_mock_engine(
74 url: Union[str, URL], executor: Any, **kw: Any
75) -> MockConnection:
76 """Create a "mock" engine used for echoing DDL.
77
78 This is a utility function used for debugging or storing the output of DDL
79 sequences as generated by :meth:`_schema.MetaData.create_all`
80 and related methods.
81
82 The function accepts a URL which is used only to determine the kind of
83 dialect to be used, as well as an "executor" callable function which
84 will receive a SQL expression object and parameters, which can then be
85 echoed or otherwise printed. The executor's return value is not handled,
86 nor does the engine allow regular string statements to be invoked, and
87 is therefore only useful for DDL that is sent to the database without
88 receiving any results.
89
90 E.g.::
91
92 from sqlalchemy import create_mock_engine
93
94
95 def dump(sql, *multiparams, **params):
96 print(sql.compile(dialect=engine.dialect))
97
98
99 engine = create_mock_engine("postgresql+psycopg2://", dump)
100 metadata.create_all(engine, checkfirst=False)
101
102 :param url: A string URL which typically needs to contain only the
103 database backend name.
104
105 :param executor: a callable which receives the arguments ``sql``,
106 ``*multiparams`` and ``**params``. The ``sql`` parameter is typically
107 an instance of :class:`.ExecutableDDLElement`, which can then be compiled
108 into a string using :meth:`.ExecutableDDLElement.compile`.
109
110 .. versionadded:: 1.4 - the :func:`.create_mock_engine` function replaces
111 the previous "mock" engine strategy used with
112 :func:`_sa.create_engine`.
113
114 .. seealso::
115
116 :ref:`faq_ddl_as_string`
117
118 """
119
120 # create url.URL object
121 u = _url.make_url(url)
122
123 dialect_cls = u.get_dialect()
124
125 dialect_args = {}
126 # consume dialect arguments from kwargs
127 for k in util.get_cls_kwargs(dialect_cls):
128 if k in kw:
129 dialect_args[k] = kw.pop(k)
130
131 # create dialect
132 dialect = dialect_cls(**dialect_args)
133
134 return MockConnection(dialect, executor)