coercions.py 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  1. # sql/coercions.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. import numbers
  8. import re
  9. from . import operators
  10. from . import roles
  11. from . import visitors
  12. from .base import ExecutableOption
  13. from .base import Options
  14. from .traversals import HasCacheKey
  15. from .visitors import Visitable
  16. from .. import exc
  17. from .. import inspection
  18. from .. import util
  19. from ..util import collections_abc
  20. elements = None
  21. lambdas = None
  22. schema = None
  23. selectable = None
  24. sqltypes = None
  25. traversals = None
  26. def _is_literal(element):
  27. """Return whether or not the element is a "literal" in the context
  28. of a SQL expression construct.
  29. """
  30. return (
  31. not isinstance(
  32. element,
  33. (Visitable, schema.SchemaEventTarget),
  34. )
  35. and not hasattr(element, "__clause_element__")
  36. )
  37. def _deep_is_literal(element):
  38. """Return whether or not the element is a "literal" in the context
  39. of a SQL expression construct.
  40. does a deeper more esoteric check than _is_literal. is used
  41. for lambda elements that have to distinguish values that would
  42. be bound vs. not without any context.
  43. """
  44. if isinstance(element, collections_abc.Sequence) and not isinstance(
  45. element, str
  46. ):
  47. for elem in element:
  48. if not _deep_is_literal(elem):
  49. return False
  50. else:
  51. return True
  52. return (
  53. not isinstance(
  54. element,
  55. (
  56. Visitable,
  57. schema.SchemaEventTarget,
  58. HasCacheKey,
  59. Options,
  60. util.langhelpers._symbol,
  61. ),
  62. )
  63. and not hasattr(element, "__clause_element__")
  64. and (
  65. not isinstance(element, type)
  66. or not issubclass(element, HasCacheKey)
  67. )
  68. )
  69. def _document_text_coercion(paramname, meth_rst, param_rst):
  70. return util.add_parameter_text(
  71. paramname,
  72. (
  73. ".. warning:: "
  74. "The %s argument to %s can be passed as a Python string argument, "
  75. "which will be treated "
  76. "as **trusted SQL text** and rendered as given. **DO NOT PASS "
  77. "UNTRUSTED INPUT TO THIS PARAMETER**."
  78. )
  79. % (param_rst, meth_rst),
  80. )
  81. def _expression_collection_was_a_list(attrname, fnname, args):
  82. if args and isinstance(args[0], (list, set, dict)) and len(args) == 1:
  83. if isinstance(args[0], list):
  84. util.warn_deprecated_20(
  85. 'The "%s" argument to %s(), when referring to a sequence '
  86. "of items, is now passed as a series of positional "
  87. "elements, rather than as a list. " % (attrname, fnname)
  88. )
  89. return args[0]
  90. else:
  91. return args
  92. def expect(
  93. role,
  94. element,
  95. apply_propagate_attrs=None,
  96. argname=None,
  97. post_inspect=False,
  98. **kw
  99. ):
  100. if (
  101. role.allows_lambda
  102. # note callable() will not invoke a __getattr__() method, whereas
  103. # hasattr(obj, "__call__") will. by keeping the callable() check here
  104. # we prevent most needless calls to hasattr() and therefore
  105. # __getattr__(), which is present on ColumnElement.
  106. and callable(element)
  107. and hasattr(element, "__code__")
  108. ):
  109. return lambdas.LambdaElement(
  110. element,
  111. role,
  112. lambdas.LambdaOptions(**kw),
  113. apply_propagate_attrs=apply_propagate_attrs,
  114. )
  115. # major case is that we are given a ClauseElement already, skip more
  116. # elaborate logic up front if possible
  117. impl = _impl_lookup[role]
  118. original_element = element
  119. if not isinstance(
  120. element,
  121. (
  122. elements.ClauseElement,
  123. schema.SchemaItem,
  124. schema.FetchedValue,
  125. lambdas.PyWrapper,
  126. ),
  127. ):
  128. resolved = None
  129. if impl._resolve_literal_only:
  130. resolved = impl._literal_coercion(element, **kw)
  131. else:
  132. original_element = element
  133. is_clause_element = False
  134. # this is a special performance optimization for ORM
  135. # joins used by JoinTargetImpl that we don't go through the
  136. # work of creating __clause_element__() when we only need the
  137. # original QueryableAttribute, as the former will do clause
  138. # adaption and all that which is just thrown away here.
  139. if (
  140. impl._skip_clauseelement_for_target_match
  141. and isinstance(element, role)
  142. and hasattr(element, "__clause_element__")
  143. ):
  144. is_clause_element = True
  145. else:
  146. while hasattr(element, "__clause_element__"):
  147. is_clause_element = True
  148. if not getattr(element, "is_clause_element", False):
  149. element = element.__clause_element__()
  150. else:
  151. break
  152. if not is_clause_element:
  153. if impl._use_inspection:
  154. insp = inspection.inspect(element, raiseerr=False)
  155. if insp is not None:
  156. if post_inspect:
  157. insp._post_inspect
  158. try:
  159. resolved = insp.__clause_element__()
  160. except AttributeError:
  161. impl._raise_for_expected(original_element, argname)
  162. if resolved is None:
  163. resolved = impl._literal_coercion(
  164. element, argname=argname, **kw
  165. )
  166. else:
  167. resolved = element
  168. elif isinstance(element, lambdas.PyWrapper):
  169. resolved = element._sa__py_wrapper_literal(**kw)
  170. else:
  171. resolved = element
  172. if (
  173. apply_propagate_attrs is not None
  174. and not apply_propagate_attrs._propagate_attrs
  175. and resolved._propagate_attrs
  176. ):
  177. apply_propagate_attrs._propagate_attrs = resolved._propagate_attrs
  178. if impl._role_class in resolved.__class__.__mro__:
  179. if impl._post_coercion:
  180. resolved = impl._post_coercion(
  181. resolved,
  182. argname=argname,
  183. original_element=original_element,
  184. **kw
  185. )
  186. return resolved
  187. else:
  188. return impl._implicit_coercions(
  189. original_element, resolved, argname=argname, **kw
  190. )
  191. def expect_as_key(role, element, **kw):
  192. kw["as_key"] = True
  193. return expect(role, element, **kw)
  194. def expect_col_expression_collection(role, expressions):
  195. for expr in expressions:
  196. strname = None
  197. column = None
  198. resolved = expect(role, expr)
  199. if isinstance(resolved, util.string_types):
  200. strname = resolved = expr
  201. else:
  202. cols = []
  203. visitors.traverse(resolved, {}, {"column": cols.append})
  204. if cols:
  205. column = cols[0]
  206. add_element = column if column is not None else strname
  207. yield resolved, column, strname, add_element
  208. class RoleImpl(object):
  209. __slots__ = ("_role_class", "name", "_use_inspection")
  210. def _literal_coercion(self, element, **kw):
  211. raise NotImplementedError()
  212. _post_coercion = None
  213. _resolve_literal_only = False
  214. _skip_clauseelement_for_target_match = False
  215. def __init__(self, role_class):
  216. self._role_class = role_class
  217. self.name = role_class._role_name
  218. self._use_inspection = issubclass(role_class, roles.UsesInspection)
  219. def _implicit_coercions(self, element, resolved, argname=None, **kw):
  220. self._raise_for_expected(element, argname, resolved)
  221. def _raise_for_expected(
  222. self,
  223. element,
  224. argname=None,
  225. resolved=None,
  226. advice=None,
  227. code=None,
  228. err=None,
  229. ):
  230. if resolved is not None and resolved is not element:
  231. got = "%r object resolved from %r object" % (resolved, element)
  232. else:
  233. got = repr(element)
  234. if argname:
  235. msg = "%s expected for argument %r; got %s." % (
  236. self.name,
  237. argname,
  238. got,
  239. )
  240. else:
  241. msg = "%s expected, got %s." % (self.name, got)
  242. if advice:
  243. msg += " " + advice
  244. util.raise_(exc.ArgumentError(msg, code=code), replace_context=err)
  245. class _Deannotate(object):
  246. __slots__ = ()
  247. def _post_coercion(self, resolved, **kw):
  248. from .util import _deep_deannotate
  249. return _deep_deannotate(resolved)
  250. class _StringOnly(object):
  251. __slots__ = ()
  252. _resolve_literal_only = True
  253. class _ReturnsStringKey(object):
  254. __slots__ = ()
  255. def _implicit_coercions(
  256. self, original_element, resolved, argname=None, **kw
  257. ):
  258. if isinstance(original_element, util.string_types):
  259. return original_element
  260. else:
  261. self._raise_for_expected(original_element, argname, resolved)
  262. def _literal_coercion(self, element, **kw):
  263. return element
  264. class _ColumnCoercions(object):
  265. __slots__ = ()
  266. def _warn_for_scalar_subquery_coercion(self):
  267. util.warn(
  268. "implicitly coercing SELECT object to scalar subquery; "
  269. "please use the .scalar_subquery() method to produce a scalar "
  270. "subquery.",
  271. )
  272. def _implicit_coercions(
  273. self, original_element, resolved, argname=None, **kw
  274. ):
  275. if not getattr(resolved, "is_clause_element", False):
  276. self._raise_for_expected(original_element, argname, resolved)
  277. elif resolved._is_select_statement:
  278. self._warn_for_scalar_subquery_coercion()
  279. return resolved.scalar_subquery()
  280. elif resolved._is_from_clause and isinstance(
  281. resolved, selectable.Subquery
  282. ):
  283. self._warn_for_scalar_subquery_coercion()
  284. return resolved.element.scalar_subquery()
  285. elif self._role_class.allows_lambda and resolved._is_lambda_element:
  286. return resolved
  287. else:
  288. self._raise_for_expected(original_element, argname, resolved)
  289. def _no_text_coercion(
  290. element, argname=None, exc_cls=exc.ArgumentError, extra=None, err=None
  291. ):
  292. util.raise_(
  293. exc_cls(
  294. "%(extra)sTextual SQL expression %(expr)r %(argname)sshould be "
  295. "explicitly declared as text(%(expr)r)"
  296. % {
  297. "expr": util.ellipses_string(element),
  298. "argname": "for argument %s" % (argname,) if argname else "",
  299. "extra": "%s " % extra if extra else "",
  300. }
  301. ),
  302. replace_context=err,
  303. )
  304. class _NoTextCoercion(object):
  305. __slots__ = ()
  306. def _literal_coercion(self, element, argname=None, **kw):
  307. if isinstance(element, util.string_types) and issubclass(
  308. elements.TextClause, self._role_class
  309. ):
  310. _no_text_coercion(element, argname)
  311. else:
  312. self._raise_for_expected(element, argname)
  313. class _CoerceLiterals(object):
  314. __slots__ = ()
  315. _coerce_consts = False
  316. _coerce_star = False
  317. _coerce_numerics = False
  318. def _text_coercion(self, element, argname=None):
  319. return _no_text_coercion(element, argname)
  320. def _literal_coercion(self, element, argname=None, **kw):
  321. if isinstance(element, util.string_types):
  322. if self._coerce_star and element == "*":
  323. return elements.ColumnClause("*", is_literal=True)
  324. else:
  325. return self._text_coercion(element, argname, **kw)
  326. if self._coerce_consts:
  327. if element is None:
  328. return elements.Null()
  329. elif element is False:
  330. return elements.False_()
  331. elif element is True:
  332. return elements.True_()
  333. if self._coerce_numerics and isinstance(element, (numbers.Number)):
  334. return elements.ColumnClause(str(element), is_literal=True)
  335. self._raise_for_expected(element, argname)
  336. class LiteralValueImpl(RoleImpl):
  337. _resolve_literal_only = True
  338. def _implicit_coercions(
  339. self, element, resolved, argname, type_=None, **kw
  340. ):
  341. if not _is_literal(resolved):
  342. self._raise_for_expected(
  343. element, resolved=resolved, argname=argname, **kw
  344. )
  345. return elements.BindParameter(None, element, type_=type_, unique=True)
  346. def _literal_coercion(self, element, argname=None, type_=None, **kw):
  347. return element
  348. class _SelectIsNotFrom(object):
  349. __slots__ = ()
  350. def _raise_for_expected(self, element, argname=None, resolved=None, **kw):
  351. if isinstance(element, roles.SelectStatementRole) or isinstance(
  352. resolved, roles.SelectStatementRole
  353. ):
  354. advice = (
  355. "To create a "
  356. "FROM clause from a %s object, use the .subquery() method."
  357. % (resolved.__class__ if resolved is not None else element,)
  358. )
  359. code = "89ve"
  360. else:
  361. advice = code = None
  362. return super(_SelectIsNotFrom, self)._raise_for_expected(
  363. element,
  364. argname=argname,
  365. resolved=resolved,
  366. advice=advice,
  367. code=code,
  368. **kw
  369. )
  370. class HasCacheKeyImpl(RoleImpl):
  371. __slots__ = ()
  372. def _implicit_coercions(
  373. self, original_element, resolved, argname=None, **kw
  374. ):
  375. if isinstance(original_element, traversals.HasCacheKey):
  376. return original_element
  377. else:
  378. self._raise_for_expected(original_element, argname, resolved)
  379. def _literal_coercion(self, element, **kw):
  380. return element
  381. class ExecutableOptionImpl(RoleImpl):
  382. __slots__ = ()
  383. def _implicit_coercions(
  384. self, original_element, resolved, argname=None, **kw
  385. ):
  386. if isinstance(original_element, ExecutableOption):
  387. return original_element
  388. else:
  389. self._raise_for_expected(original_element, argname, resolved)
  390. def _literal_coercion(self, element, **kw):
  391. return element
  392. class ExpressionElementImpl(_ColumnCoercions, RoleImpl):
  393. __slots__ = ()
  394. def _literal_coercion(
  395. self, element, name=None, type_=None, argname=None, is_crud=False, **kw
  396. ):
  397. if (
  398. element is None
  399. and not is_crud
  400. and (type_ is None or not type_.should_evaluate_none)
  401. ):
  402. # TODO: there's no test coverage now for the
  403. # "should_evaluate_none" part of this, as outside of "crud" this
  404. # codepath is not normally used except in some special cases
  405. return elements.Null()
  406. else:
  407. try:
  408. return elements.BindParameter(
  409. name, element, type_, unique=True, _is_crud=is_crud
  410. )
  411. except exc.ArgumentError as err:
  412. self._raise_for_expected(element, err=err)
  413. def _raise_for_expected(self, element, argname=None, resolved=None, **kw):
  414. if isinstance(element, roles.AnonymizedFromClauseRole):
  415. advice = (
  416. "To create a "
  417. "column expression from a FROM clause row "
  418. "as a whole, use the .table_valued() method."
  419. )
  420. else:
  421. advice = None
  422. return super(ExpressionElementImpl, self)._raise_for_expected(
  423. element, argname=argname, resolved=resolved, advice=advice, **kw
  424. )
  425. class BinaryElementImpl(ExpressionElementImpl, RoleImpl):
  426. __slots__ = ()
  427. def _literal_coercion(
  428. self, element, expr, operator, bindparam_type=None, argname=None, **kw
  429. ):
  430. try:
  431. return expr._bind_param(operator, element, type_=bindparam_type)
  432. except exc.ArgumentError as err:
  433. self._raise_for_expected(element, err=err)
  434. def _post_coercion(self, resolved, expr, bindparam_type=None, **kw):
  435. if resolved.type._isnull and not expr.type._isnull:
  436. resolved = resolved._with_binary_element_type(
  437. bindparam_type if bindparam_type is not None else expr.type
  438. )
  439. return resolved
  440. class InElementImpl(RoleImpl):
  441. __slots__ = ()
  442. def _implicit_coercions(
  443. self, original_element, resolved, argname=None, **kw
  444. ):
  445. if resolved._is_from_clause:
  446. if (
  447. isinstance(resolved, selectable.Alias)
  448. and resolved.element._is_select_statement
  449. ):
  450. self._warn_for_implicit_coercion(resolved)
  451. return self._post_coercion(resolved.element, **kw)
  452. else:
  453. self._warn_for_implicit_coercion(resolved)
  454. return self._post_coercion(resolved.select(), **kw)
  455. else:
  456. self._raise_for_expected(original_element, argname, resolved)
  457. def _warn_for_implicit_coercion(self, elem):
  458. util.warn(
  459. "Coercing %s object into a select() for use in IN(); "
  460. "please pass a select() construct explicitly"
  461. % (elem.__class__.__name__)
  462. )
  463. def _literal_coercion(self, element, expr, operator, **kw):
  464. if isinstance(element, collections_abc.Iterable) and not isinstance(
  465. element, util.string_types
  466. ):
  467. non_literal_expressions = {}
  468. element = list(element)
  469. for o in element:
  470. if not _is_literal(o):
  471. if not isinstance(o, operators.ColumnOperators):
  472. self._raise_for_expected(element, **kw)
  473. else:
  474. non_literal_expressions[o] = o
  475. elif o is None:
  476. non_literal_expressions[o] = elements.Null()
  477. if non_literal_expressions:
  478. return elements.ClauseList(
  479. *[
  480. non_literal_expressions[o]
  481. if o in non_literal_expressions
  482. else expr._bind_param(operator, o)
  483. for o in element
  484. ]
  485. )
  486. else:
  487. return expr._bind_param(operator, element, expanding=True)
  488. else:
  489. self._raise_for_expected(element, **kw)
  490. def _post_coercion(self, element, expr, operator, **kw):
  491. if element._is_select_statement:
  492. # for IN, we are doing scalar_subquery() coercion without
  493. # a warning
  494. return element.scalar_subquery()
  495. elif isinstance(element, elements.ClauseList):
  496. assert not len(element.clauses) == 0
  497. return element.self_group(against=operator)
  498. elif isinstance(element, elements.BindParameter):
  499. element = element._clone(maintain_key=True)
  500. element.expanding = True
  501. element.expand_op = operator
  502. return element
  503. else:
  504. return element
  505. class OnClauseImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl):
  506. __slots__ = ()
  507. _coerce_consts = True
  508. def _implicit_coercions(
  509. self, original_element, resolved, argname=None, legacy=False, **kw
  510. ):
  511. if legacy and isinstance(resolved, str):
  512. return resolved
  513. else:
  514. return super(OnClauseImpl, self)._implicit_coercions(
  515. original_element,
  516. resolved,
  517. argname=argname,
  518. legacy=legacy,
  519. **kw
  520. )
  521. def _text_coercion(self, element, argname=None, legacy=False):
  522. if legacy and isinstance(element, str):
  523. util.warn_deprecated_20(
  524. "Using strings to indicate relationship names in "
  525. "Query.join() is deprecated and will be removed in "
  526. "SQLAlchemy 2.0. Please use the class-bound attribute "
  527. "directly."
  528. )
  529. return element
  530. return super(OnClauseImpl, self)._text_coercion(element, argname)
  531. def _post_coercion(self, resolved, original_element=None, **kw):
  532. # this is a hack right now as we want to use coercion on an
  533. # ORM InstrumentedAttribute, but we want to return the object
  534. # itself if it is one, not its clause element.
  535. # ORM context _join and _legacy_join() would need to be improved
  536. # to look for annotations in a clause element form.
  537. if isinstance(original_element, roles.JoinTargetRole):
  538. return original_element
  539. return resolved
  540. class WhereHavingImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl):
  541. __slots__ = ()
  542. _coerce_consts = True
  543. def _text_coercion(self, element, argname=None):
  544. return _no_text_coercion(element, argname)
  545. class StatementOptionImpl(_CoerceLiterals, RoleImpl):
  546. __slots__ = ()
  547. _coerce_consts = True
  548. def _text_coercion(self, element, argname=None):
  549. return elements.TextClause(element)
  550. class ColumnArgumentImpl(_NoTextCoercion, RoleImpl):
  551. __slots__ = ()
  552. class ColumnArgumentOrKeyImpl(_ReturnsStringKey, RoleImpl):
  553. __slots__ = ()
  554. class StrAsPlainColumnImpl(_CoerceLiterals, RoleImpl):
  555. __slots__ = ()
  556. def _text_coercion(self, element, argname=None):
  557. return elements.ColumnClause(element)
  558. class ByOfImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl, roles.ByOfRole):
  559. __slots__ = ()
  560. _coerce_consts = True
  561. def _text_coercion(self, element, argname=None):
  562. return elements._textual_label_reference(element)
  563. class OrderByImpl(ByOfImpl, RoleImpl):
  564. __slots__ = ()
  565. def _post_coercion(self, resolved, **kw):
  566. if (
  567. isinstance(resolved, self._role_class)
  568. and resolved._order_by_label_element is not None
  569. ):
  570. return elements._label_reference(resolved)
  571. else:
  572. return resolved
  573. class GroupByImpl(ByOfImpl, RoleImpl):
  574. __slots__ = ()
  575. def _implicit_coercions(
  576. self, original_element, resolved, argname=None, **kw
  577. ):
  578. if isinstance(resolved, roles.StrictFromClauseRole):
  579. return elements.ClauseList(*resolved.c)
  580. else:
  581. return resolved
  582. class DMLColumnImpl(_ReturnsStringKey, RoleImpl):
  583. __slots__ = ()
  584. def _post_coercion(self, element, as_key=False, **kw):
  585. if as_key:
  586. return element.key
  587. else:
  588. return element
  589. class ConstExprImpl(RoleImpl):
  590. __slots__ = ()
  591. def _literal_coercion(self, element, argname=None, **kw):
  592. if element is None:
  593. return elements.Null()
  594. elif element is False:
  595. return elements.False_()
  596. elif element is True:
  597. return elements.True_()
  598. else:
  599. self._raise_for_expected(element, argname)
  600. class TruncatedLabelImpl(_StringOnly, RoleImpl):
  601. __slots__ = ()
  602. def _implicit_coercions(
  603. self, original_element, resolved, argname=None, **kw
  604. ):
  605. if isinstance(original_element, util.string_types):
  606. return resolved
  607. else:
  608. self._raise_for_expected(original_element, argname, resolved)
  609. def _literal_coercion(self, element, argname=None, **kw):
  610. """coerce the given value to :class:`._truncated_label`.
  611. Existing :class:`._truncated_label` and
  612. :class:`._anonymous_label` objects are passed
  613. unchanged.
  614. """
  615. if isinstance(element, elements._truncated_label):
  616. return element
  617. else:
  618. return elements._truncated_label(element)
  619. class DDLExpressionImpl(_Deannotate, _CoerceLiterals, RoleImpl):
  620. __slots__ = ()
  621. _coerce_consts = True
  622. def _text_coercion(self, element, argname=None):
  623. # see #5754 for why we can't easily deprecate this coercion.
  624. # essentially expressions like postgresql_where would have to be
  625. # text() as they come back from reflection and we don't want to
  626. # have text() elements wired into the inspection dictionaries.
  627. return elements.TextClause(element)
  628. class DDLConstraintColumnImpl(_Deannotate, _ReturnsStringKey, RoleImpl):
  629. __slots__ = ()
  630. class DDLReferredColumnImpl(DDLConstraintColumnImpl):
  631. __slots__ = ()
  632. class LimitOffsetImpl(RoleImpl):
  633. __slots__ = ()
  634. def _implicit_coercions(self, element, resolved, argname=None, **kw):
  635. if resolved is None:
  636. return None
  637. else:
  638. self._raise_for_expected(element, argname, resolved)
  639. def _literal_coercion(self, element, name, type_, **kw):
  640. if element is None:
  641. return None
  642. else:
  643. value = util.asint(element)
  644. return selectable._OffsetLimitParam(
  645. name, value, type_=type_, unique=True
  646. )
  647. class LabeledColumnExprImpl(ExpressionElementImpl):
  648. __slots__ = ()
  649. def _implicit_coercions(
  650. self, original_element, resolved, argname=None, **kw
  651. ):
  652. if isinstance(resolved, roles.ExpressionElementRole):
  653. return resolved.label(None)
  654. else:
  655. new = super(LabeledColumnExprImpl, self)._implicit_coercions(
  656. original_element, resolved, argname=argname, **kw
  657. )
  658. if isinstance(new, roles.ExpressionElementRole):
  659. return new.label(None)
  660. else:
  661. self._raise_for_expected(original_element, argname, resolved)
  662. class ColumnsClauseImpl(_SelectIsNotFrom, _CoerceLiterals, RoleImpl):
  663. __slots__ = ()
  664. _coerce_consts = True
  665. _coerce_numerics = True
  666. _coerce_star = True
  667. _guess_straight_column = re.compile(r"^\w\S*$", re.I)
  668. def _text_coercion(self, element, argname=None):
  669. element = str(element)
  670. guess_is_literal = not self._guess_straight_column.match(element)
  671. raise exc.ArgumentError(
  672. "Textual column expression %(column)r %(argname)sshould be "
  673. "explicitly declared with text(%(column)r), "
  674. "or use %(literal_column)s(%(column)r) "
  675. "for more specificity"
  676. % {
  677. "column": util.ellipses_string(element),
  678. "argname": "for argument %s" % (argname,) if argname else "",
  679. "literal_column": "literal_column"
  680. if guess_is_literal
  681. else "column",
  682. }
  683. )
  684. class ReturnsRowsImpl(RoleImpl):
  685. __slots__ = ()
  686. class StatementImpl(_CoerceLiterals, RoleImpl):
  687. __slots__ = ()
  688. def _post_coercion(self, resolved, original_element, argname=None, **kw):
  689. if resolved is not original_element and not isinstance(
  690. original_element, util.string_types
  691. ):
  692. # use same method as Connection uses; this will later raise
  693. # ObjectNotExecutableError
  694. try:
  695. original_element._execute_on_connection
  696. except AttributeError:
  697. util.warn_deprecated(
  698. "Object %r should not be used directly in a SQL statement "
  699. "context, such as passing to methods such as "
  700. "session.execute(). This usage will be disallowed in a "
  701. "future release. "
  702. "Please use Core select() / update() / delete() etc. "
  703. "with Session.execute() and other statement execution "
  704. "methods." % original_element,
  705. "1.4",
  706. )
  707. return resolved
  708. def _implicit_coercions(
  709. self, original_element, resolved, argname=None, **kw
  710. ):
  711. if resolved._is_lambda_element:
  712. return resolved
  713. else:
  714. return super(StatementImpl, self)._implicit_coercions(
  715. original_element, resolved, argname=argname, **kw
  716. )
  717. def _text_coercion(self, element, argname=None):
  718. util.warn_deprecated_20(
  719. "Using plain strings to indicate SQL statements without using "
  720. "the text() construct is "
  721. "deprecated and will be removed in version 2.0. Ensure plain "
  722. "SQL statements are passed using the text() construct."
  723. )
  724. return elements.TextClause(element)
  725. class SelectStatementImpl(_NoTextCoercion, RoleImpl):
  726. __slots__ = ()
  727. def _implicit_coercions(
  728. self, original_element, resolved, argname=None, **kw
  729. ):
  730. if resolved._is_text_clause:
  731. return resolved.columns()
  732. else:
  733. self._raise_for_expected(original_element, argname, resolved)
  734. class HasCTEImpl(ReturnsRowsImpl):
  735. __slots__ = ()
  736. class IsCTEImpl(RoleImpl):
  737. __slots__ = ()
  738. class JoinTargetImpl(RoleImpl):
  739. __slots__ = ()
  740. _skip_clauseelement_for_target_match = True
  741. def _literal_coercion(self, element, legacy=False, **kw):
  742. if isinstance(element, str):
  743. return element
  744. def _implicit_coercions(
  745. self, original_element, resolved, argname=None, legacy=False, **kw
  746. ):
  747. if isinstance(original_element, roles.JoinTargetRole):
  748. # note that this codepath no longer occurs as of
  749. # #6550, unless JoinTargetImpl._skip_clauseelement_for_target_match
  750. # were set to False.
  751. return original_element
  752. elif legacy and isinstance(resolved, str):
  753. util.warn_deprecated_20(
  754. "Using strings to indicate relationship names in "
  755. "Query.join() is deprecated and will be removed in "
  756. "SQLAlchemy 2.0. Please use the class-bound attribute "
  757. "directly."
  758. )
  759. return resolved
  760. elif legacy and isinstance(resolved, roles.WhereHavingRole):
  761. return resolved
  762. elif legacy and resolved._is_select_statement:
  763. util.warn_deprecated(
  764. "Implicit coercion of SELECT and textual SELECT "
  765. "constructs into FROM clauses is deprecated; please call "
  766. ".subquery() on any Core select or ORM Query object in "
  767. "order to produce a subquery object.",
  768. version="1.4",
  769. )
  770. # TODO: doing _implicit_subquery here causes tests to fail,
  771. # how was this working before? probably that ORM
  772. # join logic treated it as a select and subquery would happen
  773. # in _ORMJoin->Join
  774. return resolved
  775. else:
  776. self._raise_for_expected(original_element, argname, resolved)
  777. class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl):
  778. __slots__ = ()
  779. def _implicit_coercions(
  780. self,
  781. original_element,
  782. resolved,
  783. argname=None,
  784. explicit_subquery=False,
  785. allow_select=True,
  786. **kw
  787. ):
  788. if resolved._is_select_statement:
  789. if explicit_subquery:
  790. return resolved.subquery()
  791. elif allow_select:
  792. util.warn_deprecated(
  793. "Implicit coercion of SELECT and textual SELECT "
  794. "constructs into FROM clauses is deprecated; please call "
  795. ".subquery() on any Core select or ORM Query object in "
  796. "order to produce a subquery object.",
  797. version="1.4",
  798. )
  799. return resolved._implicit_subquery
  800. elif resolved._is_text_clause:
  801. return resolved
  802. else:
  803. self._raise_for_expected(original_element, argname, resolved)
  804. def _post_coercion(self, element, deannotate=False, **kw):
  805. if deannotate:
  806. return element._deannotate()
  807. else:
  808. return element
  809. class StrictFromClauseImpl(FromClauseImpl):
  810. __slots__ = ()
  811. def _implicit_coercions(
  812. self,
  813. original_element,
  814. resolved,
  815. argname=None,
  816. allow_select=False,
  817. **kw
  818. ):
  819. if resolved._is_select_statement and allow_select:
  820. util.warn_deprecated(
  821. "Implicit coercion of SELECT and textual SELECT constructs "
  822. "into FROM clauses is deprecated; please call .subquery() "
  823. "on any Core select or ORM Query object in order to produce a "
  824. "subquery object.",
  825. version="1.4",
  826. )
  827. return resolved._implicit_subquery
  828. else:
  829. self._raise_for_expected(original_element, argname, resolved)
  830. class AnonymizedFromClauseImpl(StrictFromClauseImpl):
  831. __slots__ = ()
  832. def _post_coercion(self, element, flat=False, name=None, **kw):
  833. assert name is None
  834. return element._anonymous_fromclause(flat=flat)
  835. class DMLTableImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl):
  836. __slots__ = ()
  837. def _post_coercion(self, element, **kw):
  838. if "dml_table" in element._annotations:
  839. return element._annotations["dml_table"]
  840. else:
  841. return element
  842. class DMLSelectImpl(_NoTextCoercion, RoleImpl):
  843. __slots__ = ()
  844. def _implicit_coercions(
  845. self, original_element, resolved, argname=None, **kw
  846. ):
  847. if resolved._is_from_clause:
  848. if (
  849. isinstance(resolved, selectable.Alias)
  850. and resolved.element._is_select_statement
  851. ):
  852. return resolved.element
  853. else:
  854. return resolved.select()
  855. else:
  856. self._raise_for_expected(original_element, argname, resolved)
  857. class CompoundElementImpl(_NoTextCoercion, RoleImpl):
  858. __slots__ = ()
  859. def _raise_for_expected(self, element, argname=None, resolved=None, **kw):
  860. if isinstance(element, roles.FromClauseRole):
  861. if element._is_subquery:
  862. advice = (
  863. "Use the plain select() object without "
  864. "calling .subquery() or .alias()."
  865. )
  866. else:
  867. advice = (
  868. "To SELECT from any FROM clause, use the .select() method."
  869. )
  870. else:
  871. advice = None
  872. return super(CompoundElementImpl, self)._raise_for_expected(
  873. element, argname=argname, resolved=resolved, advice=advice, **kw
  874. )
  875. _impl_lookup = {}
  876. for name in dir(roles):
  877. cls = getattr(roles, name)
  878. if name.endswith("Role"):
  879. name = name.replace("Role", "Impl")
  880. if name in globals():
  881. impl = globals()[name](cls)
  882. _impl_lookup[cls] = impl