You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
8.2KB

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