Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/SQLAlchemy-1.3.25.dev0-py3.11-linux-x86_64.egg/sqlalchemy/sql/default_comparator.py: 34%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

133 statements  

1# sql/default_comparator.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 

8"""Default implementation of SQL comparison operations. 

9""" 

10 

11from . import operators 

12from . import type_api 

13from .elements import _clause_element_as_expr 

14from .elements import _const_expr 

15from .elements import _is_literal 

16from .elements import _literal_as_text 

17from .elements import and_ 

18from .elements import BinaryExpression 

19from .elements import BindParameter 

20from .elements import ClauseElement 

21from .elements import ClauseList 

22from .elements import collate 

23from .elements import CollectionAggregate 

24from .elements import ColumnElement 

25from .elements import False_ 

26from .elements import Null 

27from .elements import or_ 

28from .elements import TextClause 

29from .elements import True_ 

30from .elements import Tuple 

31from .elements import UnaryExpression 

32from .elements import Visitable 

33from .selectable import Alias 

34from .selectable import ScalarSelect 

35from .selectable import Selectable 

36from .selectable import SelectBase 

37from .. import exc 

38from .. import util 

39 

40 

41def _boolean_compare( 

42 expr, 

43 op, 

44 obj, 

45 negate=None, 

46 reverse=False, 

47 _python_is_types=(util.NoneType, bool), 

48 result_type=None, 

49 **kwargs 

50): 

51 

52 if result_type is None: 

53 result_type = type_api.BOOLEANTYPE 

54 

55 if isinstance(obj, _python_is_types + (Null, True_, False_)): 

56 

57 # allow x ==/!= True/False to be treated as a literal. 

58 # this comes out to "== / != true/false" or "1/0" if those 

59 # constants aren't supported and works on all platforms 

60 if op in (operators.eq, operators.ne) and isinstance( 

61 obj, (bool, True_, False_) 

62 ): 

63 return BinaryExpression( 

64 expr, 

65 _literal_as_text(obj), 

66 op, 

67 type_=result_type, 

68 negate=negate, 

69 modifiers=kwargs, 

70 ) 

71 elif op in (operators.is_distinct_from, operators.isnot_distinct_from): 

72 return BinaryExpression( 

73 expr, 

74 _literal_as_text(obj), 

75 op, 

76 type_=result_type, 

77 negate=negate, 

78 modifiers=kwargs, 

79 ) 

80 else: 

81 # all other None/True/False uses IS, IS NOT 

82 if op in (operators.eq, operators.is_): 

83 return BinaryExpression( 

84 expr, 

85 _const_expr(obj), 

86 operators.is_, 

87 negate=operators.isnot, 

88 type_=result_type, 

89 ) 

90 elif op in (operators.ne, operators.isnot): 

91 return BinaryExpression( 

92 expr, 

93 _const_expr(obj), 

94 operators.isnot, 

95 negate=operators.is_, 

96 type_=result_type, 

97 ) 

98 else: 

99 raise exc.ArgumentError( 

100 "Only '=', '!=', 'is_()', 'isnot()', " 

101 "'is_distinct_from()', 'isnot_distinct_from()' " 

102 "operators can be used with None/True/False" 

103 ) 

104 else: 

105 obj = _check_literal(expr, op, obj) 

106 

107 if reverse: 

108 return BinaryExpression( 

109 obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs 

110 ) 

111 else: 

112 return BinaryExpression( 

113 expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs 

114 ) 

115 

116 

117def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw): 

118 if result_type is None: 

119 if op.return_type: 

120 result_type = op.return_type 

121 elif op.is_comparison: 

122 result_type = type_api.BOOLEANTYPE 

123 

124 return _binary_operate( 

125 expr, op, obj, reverse=reverse, result_type=result_type, **kw 

126 ) 

127 

128 

129def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw): 

130 obj = _check_literal(expr, op, obj) 

131 

132 if reverse: 

133 left, right = obj, expr 

134 else: 

135 left, right = expr, obj 

136 

137 if result_type is None: 

138 op, result_type = left.comparator._adapt_expression( 

139 op, right.comparator 

140 ) 

141 

142 return BinaryExpression(left, right, op, type_=result_type, modifiers=kw) 

143 

144 

145def _conjunction_operate(expr, op, other, **kw): 

146 if op is operators.and_: 

147 return and_(expr, other) 

148 elif op is operators.or_: 

149 return or_(expr, other) 

150 else: 

151 raise NotImplementedError() 

152 

153 

154def _scalar(expr, op, fn, **kw): 

155 return fn(expr) 

156 

157 

158def _in_impl(expr, op, seq_or_selectable, negate_op, **kw): 

159 seq_or_selectable = _clause_element_as_expr(seq_or_selectable) 

160 

161 if isinstance(seq_or_selectable, ScalarSelect): 

162 return _boolean_compare(expr, op, seq_or_selectable, negate=negate_op) 

163 elif isinstance(seq_or_selectable, SelectBase): 

164 

165 # TODO: if we ever want to support (x, y, z) IN (select x, 

166 # y, z from table), we would need a multi-column version of 

167 # as_scalar() to produce a multi- column selectable that 

168 # does not export itself as a FROM clause 

169 

170 return _boolean_compare( 

171 expr, op, seq_or_selectable.as_scalar(), negate=negate_op, **kw 

172 ) 

173 elif isinstance(seq_or_selectable, (Selectable, TextClause)): 

174 return _boolean_compare( 

175 expr, op, seq_or_selectable, negate=negate_op, **kw 

176 ) 

177 elif isinstance(seq_or_selectable, ClauseElement): 

178 if ( 

179 isinstance(seq_or_selectable, BindParameter) 

180 and seq_or_selectable.expanding 

181 ): 

182 

183 if isinstance(expr, Tuple): 

184 seq_or_selectable = seq_or_selectable._with_expanding_in_types( 

185 [elem.type for elem in expr] 

186 ) 

187 

188 return _boolean_compare( 

189 expr, op, seq_or_selectable, negate=negate_op 

190 ) 

191 else: 

192 raise exc.InvalidRequestError( 

193 "in_() accepts" 

194 " either a list of expressions, " 

195 'a selectable, or an "expanding" bound parameter: %r' 

196 % seq_or_selectable 

197 ) 

198 

199 # Handle non selectable arguments as sequences 

200 args = [] 

201 for o in seq_or_selectable: 

202 if not _is_literal(o): 

203 if not isinstance(o, operators.ColumnOperators): 

204 raise exc.InvalidRequestError( 

205 "in_() accepts" 

206 " either a list of expressions, " 

207 'a selectable, or an "expanding" bound parameter: %r' % o 

208 ) 

209 elif o is None: 

210 o = Null() 

211 else: 

212 o = expr._bind_param(op, o) 

213 args.append(o) 

214 

215 if len(args) == 0: 

216 op, negate_op = ( 

217 (operators.empty_in_op, operators.empty_notin_op) 

218 if op is operators.in_op 

219 else (operators.empty_notin_op, operators.empty_in_op) 

220 ) 

221 

222 return _boolean_compare( 

223 expr, 

224 op, 

225 ClauseList(_tuple_values=isinstance(expr, Tuple), *args).self_group( 

226 against=op 

227 ), 

228 negate=negate_op, 

229 ) 

230 

231 

232def _getitem_impl(expr, op, other, **kw): 

233 if isinstance(expr.type, type_api.INDEXABLE): 

234 other = _check_literal(expr, op, other) 

235 return _binary_operate(expr, op, other, **kw) 

236 else: 

237 _unsupported_impl(expr, op, other, **kw) 

238 

239 

240def _unsupported_impl(expr, op, *arg, **kw): 

241 raise NotImplementedError( 

242 "Operator '%s' is not supported on " "this expression" % op.__name__ 

243 ) 

244 

245 

246def _inv_impl(expr, op, **kw): 

247 """See :meth:`.ColumnOperators.__inv__`.""" 

248 if hasattr(expr, "negation_clause"): 

249 return expr.negation_clause 

250 else: 

251 return expr._negate() 

252 

253 

254def _neg_impl(expr, op, **kw): 

255 """See :meth:`.ColumnOperators.__neg__`.""" 

256 return UnaryExpression(expr, operator=operators.neg, type_=expr.type) 

257 

258 

259def _match_impl(expr, op, other, **kw): 

260 """See :meth:`.ColumnOperators.match`.""" 

261 

262 return _boolean_compare( 

263 expr, 

264 operators.match_op, 

265 _check_literal(expr, operators.match_op, other), 

266 result_type=type_api.MATCHTYPE, 

267 negate=operators.notmatch_op 

268 if op is operators.match_op 

269 else operators.match_op, 

270 **kw 

271 ) 

272 

273 

274def _distinct_impl(expr, op, **kw): 

275 """See :meth:`.ColumnOperators.distinct`.""" 

276 return UnaryExpression( 

277 expr, operator=operators.distinct_op, type_=expr.type 

278 ) 

279 

280 

281def _between_impl(expr, op, cleft, cright, **kw): 

282 """See :meth:`.ColumnOperators.between`.""" 

283 return BinaryExpression( 

284 expr, 

285 ClauseList( 

286 _check_literal(expr, operators.and_, cleft), 

287 _check_literal(expr, operators.and_, cright), 

288 operator=operators.and_, 

289 group=False, 

290 group_contents=False, 

291 ), 

292 op, 

293 negate=operators.notbetween_op 

294 if op is operators.between_op 

295 else operators.between_op, 

296 modifiers=kw, 

297 ) 

298 

299 

300def _collate_impl(expr, op, other, **kw): 

301 return collate(expr, other) 

302 

303 

304# a mapping of operators with the method they use, along with 

305# their negated operator for comparison operators 

306operator_lookup = { 

307 "and_": (_conjunction_operate,), 

308 "or_": (_conjunction_operate,), 

309 "inv": (_inv_impl,), 

310 "add": (_binary_operate,), 

311 "mul": (_binary_operate,), 

312 "sub": (_binary_operate,), 

313 "div": (_binary_operate,), 

314 "mod": (_binary_operate,), 

315 "truediv": (_binary_operate,), 

316 "custom_op": (_custom_op_operate,), 

317 "json_path_getitem_op": (_binary_operate,), 

318 "json_getitem_op": (_binary_operate,), 

319 "concat_op": (_binary_operate,), 

320 "any_op": (_scalar, CollectionAggregate._create_any), 

321 "all_op": (_scalar, CollectionAggregate._create_all), 

322 "lt": (_boolean_compare, operators.ge), 

323 "le": (_boolean_compare, operators.gt), 

324 "ne": (_boolean_compare, operators.eq), 

325 "gt": (_boolean_compare, operators.le), 

326 "ge": (_boolean_compare, operators.lt), 

327 "eq": (_boolean_compare, operators.ne), 

328 "is_distinct_from": (_boolean_compare, operators.isnot_distinct_from), 

329 "isnot_distinct_from": (_boolean_compare, operators.is_distinct_from), 

330 "like_op": (_boolean_compare, operators.notlike_op), 

331 "ilike_op": (_boolean_compare, operators.notilike_op), 

332 "notlike_op": (_boolean_compare, operators.like_op), 

333 "notilike_op": (_boolean_compare, operators.ilike_op), 

334 "contains_op": (_boolean_compare, operators.notcontains_op), 

335 "startswith_op": (_boolean_compare, operators.notstartswith_op), 

336 "endswith_op": (_boolean_compare, operators.notendswith_op), 

337 "desc_op": (_scalar, UnaryExpression._create_desc), 

338 "asc_op": (_scalar, UnaryExpression._create_asc), 

339 "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst), 

340 "nullslast_op": (_scalar, UnaryExpression._create_nullslast), 

341 "in_op": (_in_impl, operators.notin_op), 

342 "notin_op": (_in_impl, operators.in_op), 

343 "is_": (_boolean_compare, operators.is_), 

344 "isnot": (_boolean_compare, operators.isnot), 

345 "collate": (_collate_impl,), 

346 "match_op": (_match_impl,), 

347 "notmatch_op": (_match_impl,), 

348 "distinct_op": (_distinct_impl,), 

349 "between_op": (_between_impl,), 

350 "notbetween_op": (_between_impl,), 

351 "neg": (_neg_impl,), 

352 "getitem": (_getitem_impl,), 

353 "lshift": (_unsupported_impl,), 

354 "rshift": (_unsupported_impl,), 

355 "contains": (_unsupported_impl,), 

356} 

357 

358 

359def _check_literal(expr, operator, other, bindparam_type=None): 

360 if isinstance(other, (ColumnElement, TextClause)): 

361 if isinstance(other, BindParameter) and other.type._isnull: 

362 other = other._clone() 

363 other.type = expr.type 

364 return other 

365 elif hasattr(other, "__clause_element__"): 

366 other = other.__clause_element__() 

367 elif isinstance(other, type_api.TypeEngine.Comparator): 

368 other = other.expr 

369 

370 if isinstance(other, (SelectBase, Alias)): 

371 return other.as_scalar() 

372 elif not isinstance(other, Visitable): 

373 return expr._bind_param(operator, other, type_=bindparam_type) 

374 else: 

375 return other