ext.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. # dialects/postgresql/ext.py
  2. # Copyright (C) 2005-2024 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. from .array import ARRAY
  8. from ... import util
  9. from ...sql import coercions
  10. from ...sql import elements
  11. from ...sql import expression
  12. from ...sql import functions
  13. from ...sql import roles
  14. from ...sql import schema
  15. from ...sql.schema import ColumnCollectionConstraint
  16. from ...sql.visitors import InternalTraversal
  17. class aggregate_order_by(expression.ColumnElement):
  18. """Represent a PostgreSQL aggregate order by expression.
  19. E.g.::
  20. from sqlalchemy.dialects.postgresql import aggregate_order_by
  21. expr = func.array_agg(aggregate_order_by(table.c.a, table.c.b.desc()))
  22. stmt = select(expr)
  23. would represent the expression::
  24. SELECT array_agg(a ORDER BY b DESC) FROM table;
  25. Similarly::
  26. expr = func.string_agg(
  27. table.c.a,
  28. aggregate_order_by(literal_column("','"), table.c.a)
  29. )
  30. stmt = select(expr)
  31. Would represent::
  32. SELECT string_agg(a, ',' ORDER BY a) FROM table;
  33. .. versionadded:: 1.1
  34. .. versionchanged:: 1.2.13 - the ORDER BY argument may be multiple terms
  35. .. seealso::
  36. :class:`_functions.array_agg`
  37. """
  38. __visit_name__ = "aggregate_order_by"
  39. stringify_dialect = "postgresql"
  40. _traverse_internals = [
  41. ("target", InternalTraversal.dp_clauseelement),
  42. ("type", InternalTraversal.dp_type),
  43. ("order_by", InternalTraversal.dp_clauseelement),
  44. ]
  45. def __init__(self, target, *order_by):
  46. self.target = coercions.expect(roles.ExpressionElementRole, target)
  47. self.type = self.target.type
  48. _lob = len(order_by)
  49. if _lob == 0:
  50. raise TypeError("at least one ORDER BY element is required")
  51. elif _lob == 1:
  52. self.order_by = coercions.expect(
  53. roles.ExpressionElementRole, order_by[0]
  54. )
  55. else:
  56. self.order_by = elements.ClauseList(
  57. *order_by, _literal_as_text_role=roles.ExpressionElementRole
  58. )
  59. def self_group(self, against=None):
  60. return self
  61. def get_children(self, **kwargs):
  62. return self.target, self.order_by
  63. def _copy_internals(self, clone=elements._clone, **kw):
  64. self.target = clone(self.target, **kw)
  65. self.order_by = clone(self.order_by, **kw)
  66. @property
  67. def _from_objects(self):
  68. return self.target._from_objects + self.order_by._from_objects
  69. class ExcludeConstraint(ColumnCollectionConstraint):
  70. """A table-level EXCLUDE constraint.
  71. Defines an EXCLUDE constraint as described in the `PostgreSQL
  72. documentation`__.
  73. __ https://www.postgresql.org/docs/current/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
  74. """ # noqa
  75. __visit_name__ = "exclude_constraint"
  76. where = None
  77. inherit_cache = False
  78. create_drop_stringify_dialect = "postgresql"
  79. @elements._document_text_coercion(
  80. "where",
  81. ":class:`.ExcludeConstraint`",
  82. ":paramref:`.ExcludeConstraint.where`",
  83. )
  84. def __init__(self, *elements, **kw):
  85. r"""
  86. Create an :class:`.ExcludeConstraint` object.
  87. E.g.::
  88. const = ExcludeConstraint(
  89. (Column('period'), '&&'),
  90. (Column('group'), '='),
  91. where=(Column('group') != 'some group'),
  92. ops={'group': 'my_operator_class'}
  93. )
  94. The constraint is normally embedded into the :class:`_schema.Table`
  95. construct
  96. directly, or added later using :meth:`.append_constraint`::
  97. some_table = Table(
  98. 'some_table', metadata,
  99. Column('id', Integer, primary_key=True),
  100. Column('period', TSRANGE()),
  101. Column('group', String)
  102. )
  103. some_table.append_constraint(
  104. ExcludeConstraint(
  105. (some_table.c.period, '&&'),
  106. (some_table.c.group, '='),
  107. where=some_table.c.group != 'some group',
  108. name='some_table_excl_const',
  109. ops={'group': 'my_operator_class'}
  110. )
  111. )
  112. :param \*elements:
  113. A sequence of two tuples of the form ``(column, operator)`` where
  114. "column" is a SQL expression element or a raw SQL string, most
  115. typically a :class:`_schema.Column` object,
  116. and "operator" is a string
  117. containing the operator to use. In order to specify a column name
  118. when a :class:`_schema.Column` object is not available,
  119. while ensuring
  120. that any necessary quoting rules take effect, an ad-hoc
  121. :class:`_schema.Column` or :func:`_expression.column`
  122. object should be
  123. used.
  124. :param name:
  125. Optional, the in-database name of this constraint.
  126. :param deferrable:
  127. Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
  128. issuing DDL for this constraint.
  129. :param initially:
  130. Optional string. If set, emit INITIALLY <value> when issuing DDL
  131. for this constraint.
  132. :param using:
  133. Optional string. If set, emit USING <index_method> when issuing DDL
  134. for this constraint. Defaults to 'gist'.
  135. :param where:
  136. Optional SQL expression construct or literal SQL string.
  137. If set, emit WHERE <predicate> when issuing DDL
  138. for this constraint.
  139. :param ops:
  140. Optional dictionary. Used to define operator classes for the
  141. elements; works the same way as that of the
  142. :ref:`postgresql_ops <postgresql_operator_classes>`
  143. parameter specified to the :class:`_schema.Index` construct.
  144. .. versionadded:: 1.3.21
  145. .. seealso::
  146. :ref:`postgresql_operator_classes` - general description of how
  147. PostgreSQL operator classes are specified.
  148. """
  149. columns = []
  150. render_exprs = []
  151. self.operators = {}
  152. expressions, operators = zip(*elements)
  153. for (expr, column, strname, add_element), operator in zip(
  154. coercions.expect_col_expression_collection(
  155. roles.DDLConstraintColumnRole, expressions
  156. ),
  157. operators,
  158. ):
  159. if add_element is not None:
  160. columns.append(add_element)
  161. name = column.name if column is not None else strname
  162. if name is not None:
  163. # backwards compat
  164. self.operators[name] = operator
  165. render_exprs.append((expr, name, operator))
  166. self._render_exprs = render_exprs
  167. ColumnCollectionConstraint.__init__(
  168. self,
  169. *columns,
  170. name=kw.get("name"),
  171. deferrable=kw.get("deferrable"),
  172. initially=kw.get("initially")
  173. )
  174. self.using = kw.get("using", "gist")
  175. where = kw.get("where")
  176. if where is not None:
  177. self.where = coercions.expect(roles.StatementOptionRole, where)
  178. self.ops = kw.get("ops", {})
  179. def _set_parent(self, table, **kw):
  180. super(ExcludeConstraint, self)._set_parent(table)
  181. self._render_exprs = [
  182. (
  183. expr if isinstance(expr, elements.ClauseElement) else colexpr,
  184. name,
  185. operator,
  186. )
  187. for (expr, name, operator), colexpr in util.zip_longest(
  188. self._render_exprs, self.columns
  189. )
  190. ]
  191. def _copy(self, target_table=None, **kw):
  192. elements = [
  193. (
  194. schema._copy_expression(expr, self.parent, target_table),
  195. self.operators[expr.name],
  196. )
  197. for expr in self.columns
  198. ]
  199. c = self.__class__(
  200. *elements,
  201. name=self.name,
  202. deferrable=self.deferrable,
  203. initially=self.initially,
  204. where=self.where,
  205. using=self.using
  206. )
  207. c.dispatch._update(self.dispatch)
  208. return c
  209. def array_agg(*arg, **kw):
  210. """PostgreSQL-specific form of :class:`_functions.array_agg`, ensures
  211. return type is :class:`_postgresql.ARRAY` and not
  212. the plain :class:`_types.ARRAY`, unless an explicit ``type_``
  213. is passed.
  214. .. versionadded:: 1.1
  215. """
  216. kw["_default_array_type"] = ARRAY
  217. return functions.func.array_agg(*arg, **kw)