Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# postgresql/ext.py 

2# Copyright (C) 2005-2020 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 

8from .array import ARRAY 

9from ...sql import elements 

10from ...sql import expression 

11from ...sql import functions 

12from ...sql.schema import ColumnCollectionConstraint 

13 

14 

15class aggregate_order_by(expression.ColumnElement): 

16 """Represent a PostgreSQL aggregate order by expression. 

17 

18 E.g.:: 

19 

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 

24 would represent the expression:: 

25 

26 SELECT array_agg(a ORDER BY b DESC) FROM table; 

27 

28 Similarly:: 

29 

30 expr = func.string_agg( 

31 table.c.a, 

32 aggregate_order_by(literal_column("','"), table.c.a) 

33 ) 

34 stmt = select([expr]) 

35 

36 Would represent:: 

37 

38 SELECT string_agg(a, ',' ORDER BY a) FROM table; 

39 

40 .. versionadded:: 1.1 

41 

42 .. versionchanged:: 1.2.13 - the ORDER BY argument may be multiple terms 

43 

44 .. seealso:: 

45 

46 :class:`_functions.array_agg` 

47 

48 """ 

49 

50 __visit_name__ = "aggregate_order_by" 

51 

52 def __init__(self, target, *order_by): 

53 self.target = elements._literal_as_binds(target) 

54 

55 _lob = len(order_by) 

56 if _lob == 0: 

57 raise TypeError("at least one ORDER BY element is required") 

58 elif _lob == 1: 

59 self.order_by = elements._literal_as_binds(order_by[0]) 

60 else: 

61 self.order_by = elements.ClauseList( 

62 *order_by, _literal_as_text=elements._literal_as_binds 

63 ) 

64 

65 def self_group(self, against=None): 

66 return self 

67 

68 def get_children(self, **kwargs): 

69 return self.target, self.order_by 

70 

71 def _copy_internals(self, clone=elements._clone, **kw): 

72 self.target = clone(self.target, **kw) 

73 self.order_by = clone(self.order_by, **kw) 

74 

75 @property 

76 def _from_objects(self): 

77 return self.target._from_objects + self.order_by._from_objects 

78 

79 

80class ExcludeConstraint(ColumnCollectionConstraint): 

81 """A table-level EXCLUDE constraint. 

82 

83 Defines an EXCLUDE constraint as described in the `postgres 

84 documentation`__. 

85 

86 __ http://www.postgresql.org/docs/9.0/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE 

87 

88 """ # noqa 

89 

90 __visit_name__ = "exclude_constraint" 

91 

92 where = None 

93 

94 @elements._document_text_coercion( 

95 "where", 

96 ":class:`.ExcludeConstraint`", 

97 ":paramref:`.ExcludeConstraint.where`", 

98 ) 

99 def __init__(self, *elements, **kw): 

100 r""" 

101 Create an :class:`.ExcludeConstraint` object. 

102 

103 E.g.:: 

104 

105 const = ExcludeConstraint( 

106 (Column('period'), '&&'), 

107 (Column('group'), '='), 

108 where=(Column('group') != 'some group') 

109 ) 

110 

111 The constraint is normally embedded into the :class:`_schema.Table` 

112 construct 

113 directly, or added later using :meth:`.append_constraint`:: 

114 

115 some_table = Table( 

116 'some_table', metadata, 

117 Column('id', Integer, primary_key=True), 

118 Column('period', TSRANGE()), 

119 Column('group', String) 

120 ) 

121 

122 some_table.append_constraint( 

123 ExcludeConstraint( 

124 (some_table.c.period, '&&'), 

125 (some_table.c.group, '='), 

126 where=some_table.c.group != 'some group', 

127 name='some_table_excl_const' 

128 ) 

129 ) 

130 

131 :param \*elements: 

132 

133 A sequence of two tuples of the form ``(column, operator)`` where 

134 "column" is a SQL expression element or a raw SQL string, most 

135 typically a :class:`_schema.Column` object, 

136 and "operator" is a string 

137 containing the operator to use. In order to specify a column name 

138 when a :class:`_schema.Column` object is not available, 

139 while ensuring 

140 that any necessary quoting rules take effect, an ad-hoc 

141 :class:`_schema.Column` or :func:`_expression.column` 

142 object should be 

143 used. 

144 

145 :param name: 

146 Optional, the in-database name of this constraint. 

147 

148 :param deferrable: 

149 Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when 

150 issuing DDL for this constraint. 

151 

152 :param initially: 

153 Optional string. If set, emit INITIALLY <value> when issuing DDL 

154 for this constraint. 

155 

156 :param using: 

157 Optional string. If set, emit USING <index_method> when issuing DDL 

158 for this constraint. Defaults to 'gist'. 

159 

160 :param where: 

161 Optional SQL expression construct or literal SQL string. 

162 If set, emit WHERE <predicate> when issuing DDL 

163 for this constraint. 

164 

165 """ 

166 columns = [] 

167 render_exprs = [] 

168 self.operators = {} 

169 

170 expressions, operators = zip(*elements) 

171 

172 for (expr, column, strname, add_element), operator in zip( 

173 self._extract_col_expression_collection(expressions), operators 

174 ): 

175 if add_element is not None: 

176 columns.append(add_element) 

177 

178 name = column.name if column is not None else strname 

179 

180 if name is not None: 

181 # backwards compat 

182 self.operators[name] = operator 

183 

184 expr = expression._literal_as_column(expr) 

185 

186 render_exprs.append((expr, name, operator)) 

187 

188 self._render_exprs = render_exprs 

189 

190 ColumnCollectionConstraint.__init__( 

191 self, 

192 *columns, 

193 name=kw.get("name"), 

194 deferrable=kw.get("deferrable"), 

195 initially=kw.get("initially") 

196 ) 

197 self.using = kw.get("using", "gist") 

198 where = kw.get("where") 

199 if where is not None: 

200 self.where = expression._literal_as_text( 

201 where, allow_coercion_to_text=True 

202 ) 

203 

204 def copy(self, **kw): 

205 elements = [(col, self.operators[col]) for col in self.columns.keys()] 

206 c = self.__class__( 

207 *elements, 

208 name=self.name, 

209 deferrable=self.deferrable, 

210 initially=self.initially, 

211 where=self.where, 

212 using=self.using 

213 ) 

214 c.dispatch._update(self.dispatch) 

215 return c 

216 

217 

218def array_agg(*arg, **kw): 

219 """PostgreSQL-specific form of :class:`_functions.array_agg`, ensures 

220 return type is :class:`_postgresql.ARRAY` and not 

221 the plain :class:`_types.ARRAY`, unless an explicit ``type_`` 

222 is passed. 

223 

224 .. versionadded:: 1.1 

225 

226 """ 

227 kw["_default_array_type"] = ARRAY 

228 return functions.func.array_agg(*arg, **kw)