ddl.py 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. # sql/ddl.py
  2. # Copyright (C) 2009-2017 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: http://www.opensource.org/licenses/mit-license.php
  7. """
  8. Provides the hierarchy of DDL-defining schema items as well as routines
  9. to invoke them for a create/drop call.
  10. """
  11. from .. import util
  12. from .elements import ClauseElement
  13. from .base import Executable, _generative, SchemaVisitor, _bind_or_error
  14. from ..util import topological
  15. from .. import event
  16. from .. import exc
  17. class _DDLCompiles(ClauseElement):
  18. def _compiler(self, dialect, **kw):
  19. """Return a compiler appropriate for this ClauseElement, given a
  20. Dialect."""
  21. return dialect.ddl_compiler(dialect, self, **kw)
  22. class DDLElement(Executable, _DDLCompiles):
  23. """Base class for DDL expression constructs.
  24. This class is the base for the general purpose :class:`.DDL` class,
  25. as well as the various create/drop clause constructs such as
  26. :class:`.CreateTable`, :class:`.DropTable`, :class:`.AddConstraint`,
  27. etc.
  28. :class:`.DDLElement` integrates closely with SQLAlchemy events,
  29. introduced in :ref:`event_toplevel`. An instance of one is
  30. itself an event receiving callable::
  31. event.listen(
  32. users,
  33. 'after_create',
  34. AddConstraint(constraint).execute_if(dialect='postgresql')
  35. )
  36. .. seealso::
  37. :class:`.DDL`
  38. :class:`.DDLEvents`
  39. :ref:`event_toplevel`
  40. :ref:`schema_ddl_sequences`
  41. """
  42. _execution_options = Executable.\
  43. _execution_options.union({'autocommit': True})
  44. target = None
  45. on = None
  46. dialect = None
  47. callable_ = None
  48. def _execute_on_connection(self, connection, multiparams, params):
  49. return connection._execute_ddl(self, multiparams, params)
  50. def execute(self, bind=None, target=None):
  51. """Execute this DDL immediately.
  52. Executes the DDL statement in isolation using the supplied
  53. :class:`.Connectable` or
  54. :class:`.Connectable` assigned to the ``.bind``
  55. property, if not supplied. If the DDL has a conditional ``on``
  56. criteria, it will be invoked with None as the event.
  57. :param bind:
  58. Optional, an ``Engine`` or ``Connection``. If not supplied, a valid
  59. :class:`.Connectable` must be present in the
  60. ``.bind`` property.
  61. :param target:
  62. Optional, defaults to None. The target SchemaItem for the
  63. execute call. Will be passed to the ``on`` callable if any,
  64. and may also provide string expansion data for the
  65. statement. See ``execute_at`` for more information.
  66. """
  67. if bind is None:
  68. bind = _bind_or_error(self)
  69. if self._should_execute(target, bind):
  70. return bind.execute(self.against(target))
  71. else:
  72. bind.engine.logger.info(
  73. "DDL execution skipped, criteria not met.")
  74. @util.deprecated("0.7", "See :class:`.DDLEvents`, as well as "
  75. ":meth:`.DDLElement.execute_if`.")
  76. def execute_at(self, event_name, target):
  77. """Link execution of this DDL to the DDL lifecycle of a SchemaItem.
  78. Links this ``DDLElement`` to a ``Table`` or ``MetaData`` instance,
  79. executing it when that schema item is created or dropped. The DDL
  80. statement will be executed using the same Connection and transactional
  81. context as the Table create/drop itself. The ``.bind`` property of
  82. this statement is ignored.
  83. :param event:
  84. One of the events defined in the schema item's ``.ddl_events``;
  85. e.g. 'before-create', 'after-create', 'before-drop' or 'after-drop'
  86. :param target:
  87. The Table or MetaData instance for which this DDLElement will
  88. be associated with.
  89. A DDLElement instance can be linked to any number of schema items.
  90. ``execute_at`` builds on the ``append_ddl_listener`` interface of
  91. :class:`.MetaData` and :class:`.Table` objects.
  92. Caveat: Creating or dropping a Table in isolation will also trigger
  93. any DDL set to ``execute_at`` that Table's MetaData. This may change
  94. in a future release.
  95. """
  96. def call_event(target, connection, **kw):
  97. if self._should_execute_deprecated(event_name,
  98. target, connection, **kw):
  99. return connection.execute(self.against(target))
  100. event.listen(target, "" + event_name.replace('-', '_'), call_event)
  101. @_generative
  102. def against(self, target):
  103. """Return a copy of this DDL against a specific schema item."""
  104. self.target = target
  105. @_generative
  106. def execute_if(self, dialect=None, callable_=None, state=None):
  107. r"""Return a callable that will execute this
  108. DDLElement conditionally.
  109. Used to provide a wrapper for event listening::
  110. event.listen(
  111. metadata,
  112. 'before_create',
  113. DDL("my_ddl").execute_if(dialect='postgresql')
  114. )
  115. :param dialect: May be a string, tuple or a callable
  116. predicate. If a string, it will be compared to the name of the
  117. executing database dialect::
  118. DDL('something').execute_if(dialect='postgresql')
  119. If a tuple, specifies multiple dialect names::
  120. DDL('something').execute_if(dialect=('postgresql', 'mysql'))
  121. :param callable_: A callable, which will be invoked with
  122. four positional arguments as well as optional keyword
  123. arguments:
  124. :ddl:
  125. This DDL element.
  126. :target:
  127. The :class:`.Table` or :class:`.MetaData` object which is the
  128. target of this event. May be None if the DDL is executed
  129. explicitly.
  130. :bind:
  131. The :class:`.Connection` being used for DDL execution
  132. :tables:
  133. Optional keyword argument - a list of Table objects which are to
  134. be created/ dropped within a MetaData.create_all() or drop_all()
  135. method call.
  136. :state:
  137. Optional keyword argument - will be the ``state`` argument
  138. passed to this function.
  139. :checkfirst:
  140. Keyword argument, will be True if the 'checkfirst' flag was
  141. set during the call to ``create()``, ``create_all()``,
  142. ``drop()``, ``drop_all()``.
  143. If the callable returns a true value, the DDL statement will be
  144. executed.
  145. :param state: any value which will be passed to the callable\_
  146. as the ``state`` keyword argument.
  147. .. seealso::
  148. :class:`.DDLEvents`
  149. :ref:`event_toplevel`
  150. """
  151. self.dialect = dialect
  152. self.callable_ = callable_
  153. self.state = state
  154. def _should_execute(self, target, bind, **kw):
  155. if self.on is not None and \
  156. not self._should_execute_deprecated(None, target, bind, **kw):
  157. return False
  158. if isinstance(self.dialect, util.string_types):
  159. if self.dialect != bind.engine.name:
  160. return False
  161. elif isinstance(self.dialect, (tuple, list, set)):
  162. if bind.engine.name not in self.dialect:
  163. return False
  164. if (self.callable_ is not None and
  165. not self.callable_(self, target, bind,
  166. state=self.state, **kw)):
  167. return False
  168. return True
  169. def _should_execute_deprecated(self, event, target, bind, **kw):
  170. if self.on is None:
  171. return True
  172. elif isinstance(self.on, util.string_types):
  173. return self.on == bind.engine.name
  174. elif isinstance(self.on, (tuple, list, set)):
  175. return bind.engine.name in self.on
  176. else:
  177. return self.on(self, event, target, bind, **kw)
  178. def __call__(self, target, bind, **kw):
  179. """Execute the DDL as a ddl_listener."""
  180. if self._should_execute(target, bind, **kw):
  181. return bind.execute(self.against(target))
  182. def _check_ddl_on(self, on):
  183. if (on is not None and
  184. (not isinstance(on, util.string_types + (tuple, list, set)) and
  185. not util.callable(on))):
  186. raise exc.ArgumentError(
  187. "Expected the name of a database dialect, a tuple "
  188. "of names, or a callable for "
  189. "'on' criteria, got type '%s'." % type(on).__name__)
  190. def bind(self):
  191. if self._bind:
  192. return self._bind
  193. def _set_bind(self, bind):
  194. self._bind = bind
  195. bind = property(bind, _set_bind)
  196. def _generate(self):
  197. s = self.__class__.__new__(self.__class__)
  198. s.__dict__ = self.__dict__.copy()
  199. return s
  200. class DDL(DDLElement):
  201. """A literal DDL statement.
  202. Specifies literal SQL DDL to be executed by the database. DDL objects
  203. function as DDL event listeners, and can be subscribed to those events
  204. listed in :class:`.DDLEvents`, using either :class:`.Table` or
  205. :class:`.MetaData` objects as targets. Basic templating support allows
  206. a single DDL instance to handle repetitive tasks for multiple tables.
  207. Examples::
  208. from sqlalchemy import event, DDL
  209. tbl = Table('users', metadata, Column('uid', Integer))
  210. event.listen(tbl, 'before_create', DDL('DROP TRIGGER users_trigger'))
  211. spow = DDL('ALTER TABLE %(table)s SET secretpowers TRUE')
  212. event.listen(tbl, 'after_create', spow.execute_if(dialect='somedb'))
  213. drop_spow = DDL('ALTER TABLE users SET secretpowers FALSE')
  214. connection.execute(drop_spow)
  215. When operating on Table events, the following ``statement``
  216. string substitions are available::
  217. %(table)s - the Table name, with any required quoting applied
  218. %(schema)s - the schema name, with any required quoting applied
  219. %(fullname)s - the Table name including schema, quoted if needed
  220. The DDL's "context", if any, will be combined with the standard
  221. substitutions noted above. Keys present in the context will override
  222. the standard substitutions.
  223. """
  224. __visit_name__ = "ddl"
  225. def __init__(self, statement, on=None, context=None, bind=None):
  226. """Create a DDL statement.
  227. :param statement:
  228. A string or unicode string to be executed. Statements will be
  229. processed with Python's string formatting operator. See the
  230. ``context`` argument and the ``execute_at`` method.
  231. A literal '%' in a statement must be escaped as '%%'.
  232. SQL bind parameters are not available in DDL statements.
  233. :param on:
  234. .. deprecated:: 0.7
  235. See :meth:`.DDLElement.execute_if`.
  236. Optional filtering criteria. May be a string, tuple or a callable
  237. predicate. If a string, it will be compared to the name of the
  238. executing database dialect::
  239. DDL('something', on='postgresql')
  240. If a tuple, specifies multiple dialect names::
  241. DDL('something', on=('postgresql', 'mysql'))
  242. If a callable, it will be invoked with four positional arguments
  243. as well as optional keyword arguments:
  244. :ddl:
  245. This DDL element.
  246. :event:
  247. The name of the event that has triggered this DDL, such as
  248. 'after-create' Will be None if the DDL is executed explicitly.
  249. :target:
  250. The ``Table`` or ``MetaData`` object which is the target of
  251. this event. May be None if the DDL is executed explicitly.
  252. :connection:
  253. The ``Connection`` being used for DDL execution
  254. :tables:
  255. Optional keyword argument - a list of Table objects which are to
  256. be created/ dropped within a MetaData.create_all() or drop_all()
  257. method call.
  258. If the callable returns a true value, the DDL statement will be
  259. executed.
  260. :param context:
  261. Optional dictionary, defaults to None. These values will be
  262. available for use in string substitutions on the DDL statement.
  263. :param bind:
  264. Optional. A :class:`.Connectable`, used by
  265. default when ``execute()`` is invoked without a bind argument.
  266. .. seealso::
  267. :class:`.DDLEvents`
  268. :ref:`event_toplevel`
  269. """
  270. if not isinstance(statement, util.string_types):
  271. raise exc.ArgumentError(
  272. "Expected a string or unicode SQL statement, got '%r'" %
  273. statement)
  274. self.statement = statement
  275. self.context = context or {}
  276. self._check_ddl_on(on)
  277. self.on = on
  278. self._bind = bind
  279. def __repr__(self):
  280. return '<%s@%s; %s>' % (
  281. type(self).__name__, id(self),
  282. ', '.join([repr(self.statement)] +
  283. ['%s=%r' % (key, getattr(self, key))
  284. for key in ('on', 'context')
  285. if getattr(self, key)]))
  286. class _CreateDropBase(DDLElement):
  287. """Base class for DDL constructs that represent CREATE and DROP or
  288. equivalents.
  289. The common theme of _CreateDropBase is a single
  290. ``element`` attribute which refers to the element
  291. to be created or dropped.
  292. """
  293. def __init__(self, element, on=None, bind=None):
  294. self.element = element
  295. self._check_ddl_on(on)
  296. self.on = on
  297. self.bind = bind
  298. def _create_rule_disable(self, compiler):
  299. """Allow disable of _create_rule using a callable.
  300. Pass to _create_rule using
  301. util.portable_instancemethod(self._create_rule_disable)
  302. to retain serializability.
  303. """
  304. return False
  305. class CreateSchema(_CreateDropBase):
  306. """Represent a CREATE SCHEMA statement.
  307. .. versionadded:: 0.7.4
  308. The argument here is the string name of the schema.
  309. """
  310. __visit_name__ = "create_schema"
  311. def __init__(self, name, quote=None, **kw):
  312. """Create a new :class:`.CreateSchema` construct."""
  313. self.quote = quote
  314. super(CreateSchema, self).__init__(name, **kw)
  315. class DropSchema(_CreateDropBase):
  316. """Represent a DROP SCHEMA statement.
  317. The argument here is the string name of the schema.
  318. .. versionadded:: 0.7.4
  319. """
  320. __visit_name__ = "drop_schema"
  321. def __init__(self, name, quote=None, cascade=False, **kw):
  322. """Create a new :class:`.DropSchema` construct."""
  323. self.quote = quote
  324. self.cascade = cascade
  325. super(DropSchema, self).__init__(name, **kw)
  326. class CreateTable(_CreateDropBase):
  327. """Represent a CREATE TABLE statement."""
  328. __visit_name__ = "create_table"
  329. def __init__(
  330. self, element, on=None, bind=None,
  331. include_foreign_key_constraints=None):
  332. """Create a :class:`.CreateTable` construct.
  333. :param element: a :class:`.Table` that's the subject
  334. of the CREATE
  335. :param on: See the description for 'on' in :class:`.DDL`.
  336. :param bind: See the description for 'bind' in :class:`.DDL`.
  337. :param include_foreign_key_constraints: optional sequence of
  338. :class:`.ForeignKeyConstraint` objects that will be included
  339. inline within the CREATE construct; if omitted, all foreign key
  340. constraints that do not specify use_alter=True are included.
  341. .. versionadded:: 1.0.0
  342. """
  343. super(CreateTable, self).__init__(element, on=on, bind=bind)
  344. self.columns = [CreateColumn(column)
  345. for column in element.columns
  346. ]
  347. self.include_foreign_key_constraints = include_foreign_key_constraints
  348. class _DropView(_CreateDropBase):
  349. """Semi-public 'DROP VIEW' construct.
  350. Used by the test suite for dialect-agnostic drops of views.
  351. This object will eventually be part of a public "view" API.
  352. """
  353. __visit_name__ = "drop_view"
  354. class CreateColumn(_DDLCompiles):
  355. """Represent a :class:`.Column` as rendered in a CREATE TABLE statement,
  356. via the :class:`.CreateTable` construct.
  357. This is provided to support custom column DDL within the generation
  358. of CREATE TABLE statements, by using the
  359. compiler extension documented in :ref:`sqlalchemy.ext.compiler_toplevel`
  360. to extend :class:`.CreateColumn`.
  361. Typical integration is to examine the incoming :class:`.Column`
  362. object, and to redirect compilation if a particular flag or condition
  363. is found::
  364. from sqlalchemy import schema
  365. from sqlalchemy.ext.compiler import compiles
  366. @compiles(schema.CreateColumn)
  367. def compile(element, compiler, **kw):
  368. column = element.element
  369. if "special" not in column.info:
  370. return compiler.visit_create_column(element, **kw)
  371. text = "%s SPECIAL DIRECTIVE %s" % (
  372. column.name,
  373. compiler.type_compiler.process(column.type)
  374. )
  375. default = compiler.get_column_default_string(column)
  376. if default is not None:
  377. text += " DEFAULT " + default
  378. if not column.nullable:
  379. text += " NOT NULL"
  380. if column.constraints:
  381. text += " ".join(
  382. compiler.process(const)
  383. for const in column.constraints)
  384. return text
  385. The above construct can be applied to a :class:`.Table` as follows::
  386. from sqlalchemy import Table, Metadata, Column, Integer, String
  387. from sqlalchemy import schema
  388. metadata = MetaData()
  389. table = Table('mytable', MetaData(),
  390. Column('x', Integer, info={"special":True}, primary_key=True),
  391. Column('y', String(50)),
  392. Column('z', String(20), info={"special":True})
  393. )
  394. metadata.create_all(conn)
  395. Above, the directives we've added to the :attr:`.Column.info` collection
  396. will be detected by our custom compilation scheme::
  397. CREATE TABLE mytable (
  398. x SPECIAL DIRECTIVE INTEGER NOT NULL,
  399. y VARCHAR(50),
  400. z SPECIAL DIRECTIVE VARCHAR(20),
  401. PRIMARY KEY (x)
  402. )
  403. The :class:`.CreateColumn` construct can also be used to skip certain
  404. columns when producing a ``CREATE TABLE``. This is accomplished by
  405. creating a compilation rule that conditionally returns ``None``.
  406. This is essentially how to produce the same effect as using the
  407. ``system=True`` argument on :class:`.Column`, which marks a column
  408. as an implicitly-present "system" column.
  409. For example, suppose we wish to produce a :class:`.Table` which skips
  410. rendering of the PostgreSQL ``xmin`` column against the PostgreSQL
  411. backend, but on other backends does render it, in anticipation of a
  412. triggered rule. A conditional compilation rule could skip this name only
  413. on PostgreSQL::
  414. from sqlalchemy.schema import CreateColumn
  415. @compiles(CreateColumn, "postgresql")
  416. def skip_xmin(element, compiler, **kw):
  417. if element.element.name == 'xmin':
  418. return None
  419. else:
  420. return compiler.visit_create_column(element, **kw)
  421. my_table = Table('mytable', metadata,
  422. Column('id', Integer, primary_key=True),
  423. Column('xmin', Integer)
  424. )
  425. Above, a :class:`.CreateTable` construct will generate a ``CREATE TABLE``
  426. which only includes the ``id`` column in the string; the ``xmin`` column
  427. will be omitted, but only against the PostgreSQL backend.
  428. .. versionadded:: 0.8.3 The :class:`.CreateColumn` construct supports
  429. skipping of columns by returning ``None`` from a custom compilation
  430. rule.
  431. .. versionadded:: 0.8 The :class:`.CreateColumn` construct was added
  432. to support custom column creation styles.
  433. """
  434. __visit_name__ = 'create_column'
  435. def __init__(self, element):
  436. self.element = element
  437. class DropTable(_CreateDropBase):
  438. """Represent a DROP TABLE statement."""
  439. __visit_name__ = "drop_table"
  440. class CreateSequence(_CreateDropBase):
  441. """Represent a CREATE SEQUENCE statement."""
  442. __visit_name__ = "create_sequence"
  443. class DropSequence(_CreateDropBase):
  444. """Represent a DROP SEQUENCE statement."""
  445. __visit_name__ = "drop_sequence"
  446. class CreateIndex(_CreateDropBase):
  447. """Represent a CREATE INDEX statement."""
  448. __visit_name__ = "create_index"
  449. class DropIndex(_CreateDropBase):
  450. """Represent a DROP INDEX statement."""
  451. __visit_name__ = "drop_index"
  452. class AddConstraint(_CreateDropBase):
  453. """Represent an ALTER TABLE ADD CONSTRAINT statement."""
  454. __visit_name__ = "add_constraint"
  455. def __init__(self, element, *args, **kw):
  456. super(AddConstraint, self).__init__(element, *args, **kw)
  457. element._create_rule = util.portable_instancemethod(
  458. self._create_rule_disable)
  459. class DropConstraint(_CreateDropBase):
  460. """Represent an ALTER TABLE DROP CONSTRAINT statement."""
  461. __visit_name__ = "drop_constraint"
  462. def __init__(self, element, cascade=False, **kw):
  463. self.cascade = cascade
  464. super(DropConstraint, self).__init__(element, **kw)
  465. element._create_rule = util.portable_instancemethod(
  466. self._create_rule_disable)
  467. class DDLBase(SchemaVisitor):
  468. def __init__(self, connection):
  469. self.connection = connection
  470. class SchemaGenerator(DDLBase):
  471. def __init__(self, dialect, connection, checkfirst=False,
  472. tables=None, **kwargs):
  473. super(SchemaGenerator, self).__init__(connection, **kwargs)
  474. self.checkfirst = checkfirst
  475. self.tables = tables
  476. self.preparer = dialect.identifier_preparer
  477. self.dialect = dialect
  478. self.memo = {}
  479. def _can_create_table(self, table):
  480. self.dialect.validate_identifier(table.name)
  481. effective_schema = self.connection.schema_for_object(table)
  482. if effective_schema:
  483. self.dialect.validate_identifier(effective_schema)
  484. return not self.checkfirst or \
  485. not self.dialect.has_table(self.connection,
  486. table.name, schema=effective_schema)
  487. def _can_create_sequence(self, sequence):
  488. effective_schema = self.connection.schema_for_object(sequence)
  489. return self.dialect.supports_sequences and \
  490. (
  491. (not self.dialect.sequences_optional or
  492. not sequence.optional) and
  493. (
  494. not self.checkfirst or
  495. not self.dialect.has_sequence(
  496. self.connection,
  497. sequence.name,
  498. schema=effective_schema)
  499. )
  500. )
  501. def visit_metadata(self, metadata):
  502. if self.tables is not None:
  503. tables = self.tables
  504. else:
  505. tables = list(metadata.tables.values())
  506. collection = sort_tables_and_constraints(
  507. [t for t in tables if self._can_create_table(t)])
  508. seq_coll = [s for s in metadata._sequences.values()
  509. if s.column is None and self._can_create_sequence(s)]
  510. event_collection = [
  511. t for (t, fks) in collection if t is not None
  512. ]
  513. metadata.dispatch.before_create(metadata, self.connection,
  514. tables=event_collection,
  515. checkfirst=self.checkfirst,
  516. _ddl_runner=self)
  517. for seq in seq_coll:
  518. self.traverse_single(seq, create_ok=True)
  519. for table, fkcs in collection:
  520. if table is not None:
  521. self.traverse_single(
  522. table, create_ok=True,
  523. include_foreign_key_constraints=fkcs,
  524. _is_metadata_operation=True)
  525. else:
  526. for fkc in fkcs:
  527. self.traverse_single(fkc)
  528. metadata.dispatch.after_create(metadata, self.connection,
  529. tables=event_collection,
  530. checkfirst=self.checkfirst,
  531. _ddl_runner=self)
  532. def visit_table(
  533. self, table, create_ok=False,
  534. include_foreign_key_constraints=None,
  535. _is_metadata_operation=False):
  536. if not create_ok and not self._can_create_table(table):
  537. return
  538. table.dispatch.before_create(
  539. table, self.connection,
  540. checkfirst=self.checkfirst,
  541. _ddl_runner=self,
  542. _is_metadata_operation=_is_metadata_operation)
  543. for column in table.columns:
  544. if column.default is not None:
  545. self.traverse_single(column.default)
  546. if not self.dialect.supports_alter:
  547. # e.g., don't omit any foreign key constraints
  548. include_foreign_key_constraints = None
  549. self.connection.execute(
  550. CreateTable(
  551. table,
  552. include_foreign_key_constraints=include_foreign_key_constraints
  553. ))
  554. if hasattr(table, 'indexes'):
  555. for index in table.indexes:
  556. self.traverse_single(index)
  557. table.dispatch.after_create(
  558. table, self.connection,
  559. checkfirst=self.checkfirst,
  560. _ddl_runner=self,
  561. _is_metadata_operation=_is_metadata_operation)
  562. def visit_foreign_key_constraint(self, constraint):
  563. if not self.dialect.supports_alter:
  564. return
  565. self.connection.execute(AddConstraint(constraint))
  566. def visit_sequence(self, sequence, create_ok=False):
  567. if not create_ok and not self._can_create_sequence(sequence):
  568. return
  569. self.connection.execute(CreateSequence(sequence))
  570. def visit_index(self, index):
  571. self.connection.execute(CreateIndex(index))
  572. class SchemaDropper(DDLBase):
  573. def __init__(self, dialect, connection, checkfirst=False,
  574. tables=None, **kwargs):
  575. super(SchemaDropper, self).__init__(connection, **kwargs)
  576. self.checkfirst = checkfirst
  577. self.tables = tables
  578. self.preparer = dialect.identifier_preparer
  579. self.dialect = dialect
  580. self.memo = {}
  581. def visit_metadata(self, metadata):
  582. if self.tables is not None:
  583. tables = self.tables
  584. else:
  585. tables = list(metadata.tables.values())
  586. try:
  587. unsorted_tables = [t for t in tables if self._can_drop_table(t)]
  588. collection = list(reversed(
  589. sort_tables_and_constraints(
  590. unsorted_tables,
  591. filter_fn=lambda constraint: False
  592. if not self.dialect.supports_alter
  593. or constraint.name is None
  594. else None
  595. )
  596. ))
  597. except exc.CircularDependencyError as err2:
  598. if not self.dialect.supports_alter:
  599. util.warn(
  600. "Can't sort tables for DROP; an "
  601. "unresolvable foreign key "
  602. "dependency exists between tables: %s, and backend does "
  603. "not support ALTER. To restore at least a partial sort, "
  604. "apply use_alter=True to ForeignKey and "
  605. "ForeignKeyConstraint "
  606. "objects involved in the cycle to mark these as known "
  607. "cycles that will be ignored."
  608. % (
  609. ", ".join(sorted([t.fullname for t in err2.cycles]))
  610. )
  611. )
  612. collection = [(t, ()) for t in unsorted_tables]
  613. else:
  614. util.raise_from_cause(
  615. exc.CircularDependencyError(
  616. err2.args[0],
  617. err2.cycles, err2.edges,
  618. msg="Can't sort tables for DROP; an "
  619. "unresolvable foreign key "
  620. "dependency exists between tables: %s. Please ensure "
  621. "that the ForeignKey and ForeignKeyConstraint objects "
  622. "involved in the cycle have "
  623. "names so that they can be dropped using "
  624. "DROP CONSTRAINT."
  625. % (
  626. ", ".join(sorted([t.fullname for t in err2.cycles]))
  627. )
  628. )
  629. )
  630. seq_coll = [
  631. s
  632. for s in metadata._sequences.values()
  633. if s.column is None and self._can_drop_sequence(s)
  634. ]
  635. event_collection = [
  636. t for (t, fks) in collection if t is not None
  637. ]
  638. metadata.dispatch.before_drop(
  639. metadata, self.connection, tables=event_collection,
  640. checkfirst=self.checkfirst, _ddl_runner=self)
  641. for table, fkcs in collection:
  642. if table is not None:
  643. self.traverse_single(
  644. table, drop_ok=True, _is_metadata_operation=True)
  645. else:
  646. for fkc in fkcs:
  647. self.traverse_single(fkc)
  648. for seq in seq_coll:
  649. self.traverse_single(seq, drop_ok=True)
  650. metadata.dispatch.after_drop(
  651. metadata, self.connection, tables=event_collection,
  652. checkfirst=self.checkfirst, _ddl_runner=self)
  653. def _can_drop_table(self, table):
  654. self.dialect.validate_identifier(table.name)
  655. effective_schema = self.connection.schema_for_object(table)
  656. if effective_schema:
  657. self.dialect.validate_identifier(effective_schema)
  658. return not self.checkfirst or self.dialect.has_table(
  659. self.connection, table.name, schema=effective_schema)
  660. def _can_drop_sequence(self, sequence):
  661. effective_schema = self.connection.schema_for_object(sequence)
  662. return self.dialect.supports_sequences and \
  663. ((not self.dialect.sequences_optional or
  664. not sequence.optional) and
  665. (not self.checkfirst or
  666. self.dialect.has_sequence(
  667. self.connection,
  668. sequence.name,
  669. schema=effective_schema))
  670. )
  671. def visit_index(self, index):
  672. self.connection.execute(DropIndex(index))
  673. def visit_table(self, table, drop_ok=False, _is_metadata_operation=False):
  674. if not drop_ok and not self._can_drop_table(table):
  675. return
  676. table.dispatch.before_drop(
  677. table, self.connection,
  678. checkfirst=self.checkfirst,
  679. _ddl_runner=self,
  680. _is_metadata_operation=_is_metadata_operation)
  681. for column in table.columns:
  682. if column.default is not None:
  683. self.traverse_single(column.default)
  684. self.connection.execute(DropTable(table))
  685. table.dispatch.after_drop(
  686. table, self.connection,
  687. checkfirst=self.checkfirst,
  688. _ddl_runner=self,
  689. _is_metadata_operation=_is_metadata_operation)
  690. def visit_foreign_key_constraint(self, constraint):
  691. if not self.dialect.supports_alter:
  692. return
  693. self.connection.execute(DropConstraint(constraint))
  694. def visit_sequence(self, sequence, drop_ok=False):
  695. if not drop_ok and not self._can_drop_sequence(sequence):
  696. return
  697. self.connection.execute(DropSequence(sequence))
  698. def sort_tables(tables, skip_fn=None, extra_dependencies=None):
  699. """sort a collection of :class:`.Table` objects based on dependency.
  700. This is a dependency-ordered sort which will emit :class:`.Table`
  701. objects such that they will follow their dependent :class:`.Table` objects.
  702. Tables are dependent on another based on the presence of
  703. :class:`.ForeignKeyConstraint` objects as well as explicit dependencies
  704. added by :meth:`.Table.add_is_dependent_on`.
  705. .. warning::
  706. The :func:`.sort_tables` function cannot by itself accommodate
  707. automatic resolution of dependency cycles between tables, which
  708. are usually caused by mutually dependent foreign key constraints.
  709. To resolve these cycles, either the
  710. :paramref:`.ForeignKeyConstraint.use_alter` parameter may be appled
  711. to those constraints, or use the
  712. :func:`.sql.sort_tables_and_constraints` function which will break
  713. out foreign key constraints involved in cycles separately.
  714. :param tables: a sequence of :class:`.Table` objects.
  715. :param skip_fn: optional callable which will be passed a
  716. :class:`.ForeignKey` object; if it returns True, this
  717. constraint will not be considered as a dependency. Note this is
  718. **different** from the same parameter in
  719. :func:`.sort_tables_and_constraints`, which is
  720. instead passed the owning :class:`.ForeignKeyConstraint` object.
  721. :param extra_dependencies: a sequence of 2-tuples of tables which will
  722. also be considered as dependent on each other.
  723. .. seealso::
  724. :func:`.sort_tables_and_constraints`
  725. :meth:`.MetaData.sorted_tables` - uses this function to sort
  726. """
  727. if skip_fn is not None:
  728. def _skip_fn(fkc):
  729. for fk in fkc.elements:
  730. if skip_fn(fk):
  731. return True
  732. else:
  733. return None
  734. else:
  735. _skip_fn = None
  736. return [
  737. t for (t, fkcs) in
  738. sort_tables_and_constraints(
  739. tables, filter_fn=_skip_fn, extra_dependencies=extra_dependencies)
  740. if t is not None
  741. ]
  742. def sort_tables_and_constraints(
  743. tables, filter_fn=None, extra_dependencies=None):
  744. """sort a collection of :class:`.Table` / :class:`.ForeignKeyConstraint`
  745. objects.
  746. This is a dependency-ordered sort which will emit tuples of
  747. ``(Table, [ForeignKeyConstraint, ...])`` such that each
  748. :class:`.Table` follows its dependent :class:`.Table` objects.
  749. Remaining :class:`.ForeignKeyConstraint` objects that are separate due to
  750. dependency rules not satisfied by the sort are emitted afterwards
  751. as ``(None, [ForeignKeyConstraint ...])``.
  752. Tables are dependent on another based on the presence of
  753. :class:`.ForeignKeyConstraint` objects, explicit dependencies
  754. added by :meth:`.Table.add_is_dependent_on`, as well as dependencies
  755. stated here using the :paramref:`~.sort_tables_and_constraints.skip_fn`
  756. and/or :paramref:`~.sort_tables_and_constraints.extra_dependencies`
  757. parameters.
  758. :param tables: a sequence of :class:`.Table` objects.
  759. :param filter_fn: optional callable which will be passed a
  760. :class:`.ForeignKeyConstraint` object, and returns a value based on
  761. whether this constraint should definitely be included or excluded as
  762. an inline constraint, or neither. If it returns False, the constraint
  763. will definitely be included as a dependency that cannot be subject
  764. to ALTER; if True, it will **only** be included as an ALTER result at
  765. the end. Returning None means the constraint is included in the
  766. table-based result unless it is detected as part of a dependency cycle.
  767. :param extra_dependencies: a sequence of 2-tuples of tables which will
  768. also be considered as dependent on each other.
  769. .. versionadded:: 1.0.0
  770. .. seealso::
  771. :func:`.sort_tables`
  772. """
  773. fixed_dependencies = set()
  774. mutable_dependencies = set()
  775. if extra_dependencies is not None:
  776. fixed_dependencies.update(extra_dependencies)
  777. remaining_fkcs = set()
  778. for table in tables:
  779. for fkc in table.foreign_key_constraints:
  780. if fkc.use_alter is True:
  781. remaining_fkcs.add(fkc)
  782. continue
  783. if filter_fn:
  784. filtered = filter_fn(fkc)
  785. if filtered is True:
  786. remaining_fkcs.add(fkc)
  787. continue
  788. dependent_on = fkc.referred_table
  789. if dependent_on is not table:
  790. mutable_dependencies.add((dependent_on, table))
  791. fixed_dependencies.update(
  792. (parent, table) for parent in table._extra_dependencies
  793. )
  794. try:
  795. candidate_sort = list(
  796. topological.sort(
  797. fixed_dependencies.union(mutable_dependencies), tables,
  798. deterministic_order=True
  799. )
  800. )
  801. except exc.CircularDependencyError as err:
  802. for edge in err.edges:
  803. if edge in mutable_dependencies:
  804. table = edge[1]
  805. can_remove = [
  806. fkc for fkc in table.foreign_key_constraints
  807. if filter_fn is None or filter_fn(fkc) is not False]
  808. remaining_fkcs.update(can_remove)
  809. for fkc in can_remove:
  810. dependent_on = fkc.referred_table
  811. if dependent_on is not table:
  812. mutable_dependencies.discard((dependent_on, table))
  813. candidate_sort = list(
  814. topological.sort(
  815. fixed_dependencies.union(mutable_dependencies), tables,
  816. deterministic_order=True
  817. )
  818. )
  819. return [
  820. (table, table.foreign_key_constraints.difference(remaining_fkcs))
  821. for table in candidate_sort
  822. ] + [(None, list(remaining_fkcs))]