ext.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # postgresql/ext.py
  2. # Copyright (C) 2005-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. from ...sql import expression
  8. from ...sql import elements
  9. from ...sql import functions
  10. from ...sql.schema import ColumnCollectionConstraint
  11. from .array import ARRAY
  12. class aggregate_order_by(expression.ColumnElement):
  13. """Represent a PostgreSQL aggregate order by expression.
  14. E.g.::
  15. from sqlalchemy.dialects.postgresql import aggregate_order_by
  16. expr = func.array_agg(aggregate_order_by(table.c.a, table.c.b.desc()))
  17. stmt = select([expr])
  18. would represent the expression::
  19. SELECT array_agg(a ORDER BY b DESC) FROM table;
  20. Similarly::
  21. expr = func.string_agg(
  22. table.c.a,
  23. aggregate_order_by(literal_column("','"), table.c.a)
  24. )
  25. stmt = select([expr])
  26. Would represent::
  27. SELECT string_agg(a, ',' ORDER BY a) FROM table;
  28. .. versionadded:: 1.1
  29. .. seealso::
  30. :class:`.array_agg`
  31. """
  32. __visit_name__ = 'aggregate_order_by'
  33. def __init__(self, target, order_by):
  34. self.target = elements._literal_as_binds(target)
  35. self.order_by = elements._literal_as_binds(order_by)
  36. def self_group(self, against=None):
  37. return self
  38. def get_children(self, **kwargs):
  39. return self.target, self.order_by
  40. def _copy_internals(self, clone=elements._clone, **kw):
  41. self.target = clone(self.target, **kw)
  42. self.order_by = clone(self.order_by, **kw)
  43. @property
  44. def _from_objects(self):
  45. return self.target._from_objects + self.order_by._from_objects
  46. class ExcludeConstraint(ColumnCollectionConstraint):
  47. """A table-level EXCLUDE constraint.
  48. Defines an EXCLUDE constraint as described in the `postgres
  49. documentation`__.
  50. __ http://www.postgresql.org/docs/9.0/\
  51. static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE
  52. """
  53. __visit_name__ = 'exclude_constraint'
  54. where = None
  55. def __init__(self, *elements, **kw):
  56. r"""
  57. Create an :class:`.ExcludeConstraint` object.
  58. E.g.::
  59. const = ExcludeConstraint(
  60. (Column('period'), '&&'),
  61. (Column('group'), '='),
  62. where=(Column('group') != 'some group')
  63. )
  64. The constraint is normally embedded into the :class:`.Table` construct
  65. directly, or added later using :meth:`.append_constraint`::
  66. some_table = Table(
  67. 'some_table', metadata,
  68. Column('id', Integer, primary_key=True),
  69. Column('period', TSRANGE()),
  70. Column('group', String)
  71. )
  72. some_table.append_constraint(
  73. ExcludeConstraint(
  74. (some_table.c.period, '&&'),
  75. (some_table.c.group, '='),
  76. where=some_table.c.group != 'some group',
  77. name='some_table_excl_const'
  78. )
  79. )
  80. :param \*elements:
  81. A sequence of two tuples of the form ``(column, operator)`` where
  82. "column" is a SQL expression element or a raw SQL string, most
  83. typically a :class:`.Column` object,
  84. and "operator" is a string containing the operator to use.
  85. .. note::
  86. A plain string passed for the value of "column" is interpreted
  87. as an arbitrary SQL expression; when passing a plain string,
  88. any necessary quoting and escaping syntaxes must be applied
  89. manually. In order to specify a column name when a
  90. :class:`.Column` object is not available, while ensuring that
  91. any necessary quoting rules take effect, an ad-hoc
  92. :class:`.Column` or :func:`.sql.expression.column` object may
  93. be used.
  94. :param name:
  95. Optional, the in-database name of this constraint.
  96. :param deferrable:
  97. Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when
  98. issuing DDL for this constraint.
  99. :param initially:
  100. Optional string. If set, emit INITIALLY <value> when issuing DDL
  101. for this constraint.
  102. :param using:
  103. Optional string. If set, emit USING <index_method> when issuing DDL
  104. for this constraint. Defaults to 'gist'.
  105. :param where:
  106. Optional SQL expression construct or literal SQL string.
  107. If set, emit WHERE <predicate> when issuing DDL
  108. for this constraint.
  109. .. note::
  110. A plain string passed here is interpreted as an arbitrary SQL
  111. expression; when passing a plain string, any necessary quoting
  112. and escaping syntaxes must be applied manually.
  113. """
  114. columns = []
  115. render_exprs = []
  116. self.operators = {}
  117. expressions, operators = zip(*elements)
  118. for (expr, column, strname, add_element), operator in zip(
  119. self._extract_col_expression_collection(expressions),
  120. operators
  121. ):
  122. if add_element is not None:
  123. columns.append(add_element)
  124. name = column.name if column is not None else strname
  125. if name is not None:
  126. # backwards compat
  127. self.operators[name] = operator
  128. expr = expression._literal_as_text(expr)
  129. render_exprs.append(
  130. (expr, name, operator)
  131. )
  132. self._render_exprs = render_exprs
  133. ColumnCollectionConstraint.__init__(
  134. self,
  135. *columns,
  136. name=kw.get('name'),
  137. deferrable=kw.get('deferrable'),
  138. initially=kw.get('initially')
  139. )
  140. self.using = kw.get('using', 'gist')
  141. where = kw.get('where')
  142. if where is not None:
  143. self.where = expression._literal_as_text(where)
  144. def copy(self, **kw):
  145. elements = [(col, self.operators[col])
  146. for col in self.columns.keys()]
  147. c = self.__class__(*elements,
  148. name=self.name,
  149. deferrable=self.deferrable,
  150. initially=self.initially,
  151. where=self.where,
  152. using=self.using)
  153. c.dispatch._update(self.dispatch)
  154. return c
  155. def array_agg(*arg, **kw):
  156. """PostgreSQL-specific form of :class:`.array_agg`, ensures
  157. return type is :class:`.postgresql.ARRAY` and not
  158. the plain :class:`.types.ARRAY`.
  159. .. versionadded:: 1.1
  160. """
  161. kw['type_'] = ARRAY(functions._type_from_args(arg))
  162. return functions.func.array_agg(*arg, **kw)