dml.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
  2. # <see AUTHORS file>
  3. #
  4. # This module is part of SQLAlchemy and is released under
  5. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  6. from ... import util
  7. from ...sql import coercions
  8. from ...sql import roles
  9. from ...sql.base import _exclusive_against
  10. from ...sql.base import _generative
  11. from ...sql.base import ColumnCollection
  12. from ...sql.dml import Insert as StandardInsert
  13. from ...sql.elements import ClauseElement
  14. from ...sql.expression import alias
  15. from ...util.langhelpers import public_factory
  16. __all__ = ("Insert", "insert")
  17. class Insert(StandardInsert):
  18. """SQLite-specific implementation of INSERT.
  19. Adds methods for SQLite-specific syntaxes such as ON CONFLICT.
  20. The :class:`_sqlite.Insert` object is created using the
  21. :func:`sqlalchemy.dialects.sqlite.insert` function.
  22. .. versionadded:: 1.4
  23. .. seealso::
  24. :ref:`sqlite_on_conflict_insert`
  25. """
  26. stringify_dialect = "sqlite"
  27. inherit_cache = False
  28. @util.memoized_property
  29. def excluded(self):
  30. """Provide the ``excluded`` namespace for an ON CONFLICT statement
  31. SQLite's ON CONFLICT clause allows reference to the row that would
  32. be inserted, known as ``excluded``. This attribute provides
  33. all columns in this row to be referenceable.
  34. .. tip:: The :attr:`_sqlite.Insert.excluded` attribute is an instance
  35. of :class:`_expression.ColumnCollection`, which provides an
  36. interface the same as that of the :attr:`_schema.Table.c`
  37. collection described at :ref:`metadata_tables_and_columns`.
  38. With this collection, ordinary names are accessible like attributes
  39. (e.g. ``stmt.excluded.some_column``), but special names and
  40. dictionary method names should be accessed using indexed access,
  41. such as ``stmt.excluded["column name"]`` or
  42. ``stmt.excluded["values"]``. See the docstring for
  43. :class:`_expression.ColumnCollection` for further examples.
  44. """
  45. return alias(self.table, name="excluded").columns
  46. _on_conflict_exclusive = _exclusive_against(
  47. "_post_values_clause",
  48. msgs={
  49. "_post_values_clause": "This Insert construct already has "
  50. "an ON CONFLICT clause established"
  51. },
  52. )
  53. @_generative
  54. @_on_conflict_exclusive
  55. def on_conflict_do_update(
  56. self,
  57. index_elements=None,
  58. index_where=None,
  59. set_=None,
  60. where=None,
  61. ):
  62. r"""
  63. Specifies a DO UPDATE SET action for ON CONFLICT clause.
  64. :param index_elements:
  65. A sequence consisting of string column names, :class:`_schema.Column`
  66. objects, or other column expression objects that will be used
  67. to infer a target index or unique constraint.
  68. :param index_where:
  69. Additional WHERE criterion that can be used to infer a
  70. conditional target index.
  71. :param set\_:
  72. A dictionary or other mapping object
  73. where the keys are either names of columns in the target table,
  74. or :class:`_schema.Column` objects or other ORM-mapped columns
  75. matching that of the target table, and expressions or literals
  76. as values, specifying the ``SET`` actions to take.
  77. .. versionadded:: 1.4 The
  78. :paramref:`_sqlite.Insert.on_conflict_do_update.set_`
  79. parameter supports :class:`_schema.Column` objects from the target
  80. :class:`_schema.Table` as keys.
  81. .. warning:: This dictionary does **not** take into account
  82. Python-specified default UPDATE values or generation functions,
  83. e.g. those specified using :paramref:`_schema.Column.onupdate`.
  84. These values will not be exercised for an ON CONFLICT style of
  85. UPDATE, unless they are manually specified in the
  86. :paramref:`.Insert.on_conflict_do_update.set_` dictionary.
  87. :param where:
  88. Optional argument. If present, can be a literal SQL
  89. string or an acceptable expression for a ``WHERE`` clause
  90. that restricts the rows affected by ``DO UPDATE SET``. Rows
  91. not meeting the ``WHERE`` condition will not be updated
  92. (effectively a ``DO NOTHING`` for those rows).
  93. """
  94. self._post_values_clause = OnConflictDoUpdate(
  95. index_elements, index_where, set_, where
  96. )
  97. @_generative
  98. @_on_conflict_exclusive
  99. def on_conflict_do_nothing(self, index_elements=None, index_where=None):
  100. """
  101. Specifies a DO NOTHING action for ON CONFLICT clause.
  102. :param index_elements:
  103. A sequence consisting of string column names, :class:`_schema.Column`
  104. objects, or other column expression objects that will be used
  105. to infer a target index or unique constraint.
  106. :param index_where:
  107. Additional WHERE criterion that can be used to infer a
  108. conditional target index.
  109. """
  110. self._post_values_clause = OnConflictDoNothing(
  111. index_elements, index_where
  112. )
  113. insert = public_factory(
  114. Insert, ".dialects.sqlite.insert", ".dialects.sqlite.Insert"
  115. )
  116. class OnConflictClause(ClauseElement):
  117. stringify_dialect = "sqlite"
  118. def __init__(self, index_elements=None, index_where=None):
  119. if index_elements is not None:
  120. self.constraint_target = None
  121. self.inferred_target_elements = index_elements
  122. self.inferred_target_whereclause = index_where
  123. else:
  124. self.constraint_target = (
  125. self.inferred_target_elements
  126. ) = self.inferred_target_whereclause = None
  127. class OnConflictDoNothing(OnConflictClause):
  128. __visit_name__ = "on_conflict_do_nothing"
  129. class OnConflictDoUpdate(OnConflictClause):
  130. __visit_name__ = "on_conflict_do_update"
  131. def __init__(
  132. self,
  133. index_elements=None,
  134. index_where=None,
  135. set_=None,
  136. where=None,
  137. ):
  138. super(OnConflictDoUpdate, self).__init__(
  139. index_elements=index_elements,
  140. index_where=index_where,
  141. )
  142. if isinstance(set_, dict):
  143. if not set_:
  144. raise ValueError("set parameter dictionary must not be empty")
  145. elif isinstance(set_, ColumnCollection):
  146. set_ = dict(set_)
  147. else:
  148. raise ValueError(
  149. "set parameter must be a non-empty dictionary "
  150. "or a ColumnCollection such as the `.c.` collection "
  151. "of a Table object"
  152. )
  153. self.update_values_to_set = [
  154. (coercions.expect(roles.DMLColumnRole, key), value)
  155. for key, value in set_.items()
  156. ]
  157. self.update_whereclause = where