crud.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. # sql/crud.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. """Functions used by compiler.py to determine the parameters rendered
  8. within INSERT and UPDATE statements.
  9. """
  10. from .. import util
  11. from .. import exc
  12. from . import dml
  13. from . import elements
  14. import operator
  15. REQUIRED = util.symbol('REQUIRED', """
  16. Placeholder for the value within a :class:`.BindParameter`
  17. which is required to be present when the statement is passed
  18. to :meth:`.Connection.execute`.
  19. This symbol is typically used when a :func:`.expression.insert`
  20. or :func:`.expression.update` statement is compiled without parameter
  21. values present.
  22. """)
  23. ISINSERT = util.symbol('ISINSERT')
  24. ISUPDATE = util.symbol('ISUPDATE')
  25. ISDELETE = util.symbol('ISDELETE')
  26. def _setup_crud_params(compiler, stmt, local_stmt_type, **kw):
  27. restore_isinsert = compiler.isinsert
  28. restore_isupdate = compiler.isupdate
  29. restore_isdelete = compiler.isdelete
  30. should_restore = (
  31. restore_isinsert or restore_isupdate or restore_isdelete
  32. ) or len(compiler.stack) > 1
  33. if local_stmt_type is ISINSERT:
  34. compiler.isupdate = False
  35. compiler.isinsert = True
  36. elif local_stmt_type is ISUPDATE:
  37. compiler.isupdate = True
  38. compiler.isinsert = False
  39. elif local_stmt_type is ISDELETE:
  40. if not should_restore:
  41. compiler.isdelete = True
  42. else:
  43. assert False, "ISINSERT, ISUPDATE, or ISDELETE expected"
  44. try:
  45. if local_stmt_type in (ISINSERT, ISUPDATE):
  46. return _get_crud_params(compiler, stmt, **kw)
  47. finally:
  48. if should_restore:
  49. compiler.isinsert = restore_isinsert
  50. compiler.isupdate = restore_isupdate
  51. compiler.isdelete = restore_isdelete
  52. def _get_crud_params(compiler, stmt, **kw):
  53. """create a set of tuples representing column/string pairs for use
  54. in an INSERT or UPDATE statement.
  55. Also generates the Compiled object's postfetch, prefetch, and
  56. returning column collections, used for default handling and ultimately
  57. populating the ResultProxy's prefetch_cols() and postfetch_cols()
  58. collections.
  59. """
  60. compiler.postfetch = []
  61. compiler.insert_prefetch = []
  62. compiler.update_prefetch = []
  63. compiler.returning = []
  64. # no parameters in the statement, no parameters in the
  65. # compiled params - return binds for all columns
  66. if compiler.column_keys is None and stmt.parameters is None:
  67. return [
  68. (c, _create_bind_param(
  69. compiler, c, None, required=True))
  70. for c in stmt.table.columns
  71. ]
  72. if stmt._has_multi_parameters:
  73. stmt_parameters = stmt.parameters[0]
  74. else:
  75. stmt_parameters = stmt.parameters
  76. # getters - these are normally just column.key,
  77. # but in the case of mysql multi-table update, the rules for
  78. # .key must conditionally take tablename into account
  79. _column_as_key, _getattr_col_key, _col_bind_name = \
  80. _key_getters_for_crud_column(compiler, stmt)
  81. # if we have statement parameters - set defaults in the
  82. # compiled params
  83. if compiler.column_keys is None:
  84. parameters = {}
  85. else:
  86. parameters = dict((_column_as_key(key), REQUIRED)
  87. for key in compiler.column_keys
  88. if not stmt_parameters or
  89. key not in stmt_parameters)
  90. # create a list of column assignment clauses as tuples
  91. values = []
  92. if stmt_parameters is not None:
  93. _get_stmt_parameters_params(
  94. compiler,
  95. parameters, stmt_parameters, _column_as_key, values, kw)
  96. check_columns = {}
  97. # special logic that only occurs for multi-table UPDATE
  98. # statements
  99. if compiler.isupdate and stmt._extra_froms and stmt_parameters:
  100. _get_multitable_params(
  101. compiler, stmt, stmt_parameters, check_columns,
  102. _col_bind_name, _getattr_col_key, values, kw)
  103. if compiler.isinsert and stmt.select_names:
  104. _scan_insert_from_select_cols(
  105. compiler, stmt, parameters,
  106. _getattr_col_key, _column_as_key,
  107. _col_bind_name, check_columns, values, kw)
  108. else:
  109. _scan_cols(
  110. compiler, stmt, parameters,
  111. _getattr_col_key, _column_as_key,
  112. _col_bind_name, check_columns, values, kw)
  113. if parameters and stmt_parameters:
  114. check = set(parameters).intersection(
  115. _column_as_key(k) for k in stmt_parameters
  116. ).difference(check_columns)
  117. if check:
  118. raise exc.CompileError(
  119. "Unconsumed column names: %s" %
  120. (", ".join("%s" % c for c in check))
  121. )
  122. if stmt._has_multi_parameters:
  123. values = _extend_values_for_multiparams(compiler, stmt, values, kw)
  124. return values
  125. def _create_bind_param(
  126. compiler, col, value, process=True,
  127. required=False, name=None, **kw):
  128. if name is None:
  129. name = col.key
  130. bindparam = elements.BindParameter(
  131. name, value, type_=col.type, required=required)
  132. bindparam._is_crud = True
  133. if process:
  134. bindparam = bindparam._compiler_dispatch(compiler, **kw)
  135. return bindparam
  136. def _key_getters_for_crud_column(compiler, stmt):
  137. if compiler.isupdate and stmt._extra_froms:
  138. # when extra tables are present, refer to the columns
  139. # in those extra tables as table-qualified, including in
  140. # dictionaries and when rendering bind param names.
  141. # the "main" table of the statement remains unqualified,
  142. # allowing the most compatibility with a non-multi-table
  143. # statement.
  144. _et = set(stmt._extra_froms)
  145. def _column_as_key(key):
  146. str_key = elements._column_as_key(key)
  147. if hasattr(key, 'table') and key.table in _et:
  148. return (key.table.name, str_key)
  149. else:
  150. return str_key
  151. def _getattr_col_key(col):
  152. if col.table in _et:
  153. return (col.table.name, col.key)
  154. else:
  155. return col.key
  156. def _col_bind_name(col):
  157. if col.table in _et:
  158. return "%s_%s" % (col.table.name, col.key)
  159. else:
  160. return col.key
  161. else:
  162. _column_as_key = elements._column_as_key
  163. _getattr_col_key = _col_bind_name = operator.attrgetter("key")
  164. return _column_as_key, _getattr_col_key, _col_bind_name
  165. def _scan_insert_from_select_cols(
  166. compiler, stmt, parameters, _getattr_col_key,
  167. _column_as_key, _col_bind_name, check_columns, values, kw):
  168. need_pks, implicit_returning, \
  169. implicit_return_defaults, postfetch_lastrowid = \
  170. _get_returning_modifiers(compiler, stmt)
  171. cols = [stmt.table.c[_column_as_key(name)]
  172. for name in stmt.select_names]
  173. compiler._insert_from_select = stmt.select
  174. add_select_cols = []
  175. if stmt.include_insert_from_select_defaults:
  176. col_set = set(cols)
  177. for col in stmt.table.columns:
  178. if col not in col_set and col.default:
  179. cols.append(col)
  180. for c in cols:
  181. col_key = _getattr_col_key(c)
  182. if col_key in parameters and col_key not in check_columns:
  183. parameters.pop(col_key)
  184. values.append((c, None))
  185. else:
  186. _append_param_insert_select_hasdefault(
  187. compiler, stmt, c, add_select_cols, kw)
  188. if add_select_cols:
  189. values.extend(add_select_cols)
  190. compiler._insert_from_select = compiler._insert_from_select._generate()
  191. compiler._insert_from_select._raw_columns = \
  192. tuple(compiler._insert_from_select._raw_columns) + tuple(
  193. expr for col, expr in add_select_cols)
  194. def _scan_cols(
  195. compiler, stmt, parameters, _getattr_col_key,
  196. _column_as_key, _col_bind_name, check_columns, values, kw):
  197. need_pks, implicit_returning, \
  198. implicit_return_defaults, postfetch_lastrowid = \
  199. _get_returning_modifiers(compiler, stmt)
  200. if stmt._parameter_ordering:
  201. parameter_ordering = [
  202. _column_as_key(key) for key in stmt._parameter_ordering
  203. ]
  204. ordered_keys = set(parameter_ordering)
  205. cols = [
  206. stmt.table.c[key] for key in parameter_ordering
  207. ] + [
  208. c for c in stmt.table.c if c.key not in ordered_keys
  209. ]
  210. else:
  211. cols = stmt.table.columns
  212. for c in cols:
  213. col_key = _getattr_col_key(c)
  214. if col_key in parameters and col_key not in check_columns:
  215. _append_param_parameter(
  216. compiler, stmt, c, col_key, parameters, _col_bind_name,
  217. implicit_returning, implicit_return_defaults, values, kw)
  218. elif compiler.isinsert:
  219. if c.primary_key and \
  220. need_pks and \
  221. (
  222. implicit_returning or
  223. not postfetch_lastrowid or
  224. c is not stmt.table._autoincrement_column
  225. ):
  226. if implicit_returning:
  227. _append_param_insert_pk_returning(
  228. compiler, stmt, c, values, kw)
  229. else:
  230. _append_param_insert_pk(compiler, stmt, c, values, kw)
  231. elif c.default is not None:
  232. _append_param_insert_hasdefault(
  233. compiler, stmt, c, implicit_return_defaults,
  234. values, kw)
  235. elif c.server_default is not None:
  236. if implicit_return_defaults and \
  237. c in implicit_return_defaults:
  238. compiler.returning.append(c)
  239. elif not c.primary_key:
  240. compiler.postfetch.append(c)
  241. elif implicit_return_defaults and \
  242. c in implicit_return_defaults:
  243. compiler.returning.append(c)
  244. elif c.primary_key and \
  245. c is not stmt.table._autoincrement_column and \
  246. not c.nullable:
  247. _warn_pk_with_no_anticipated_value(c)
  248. elif compiler.isupdate:
  249. _append_param_update(
  250. compiler, stmt, c, implicit_return_defaults, values, kw)
  251. def _append_param_parameter(
  252. compiler, stmt, c, col_key, parameters, _col_bind_name,
  253. implicit_returning, implicit_return_defaults, values, kw):
  254. value = parameters.pop(col_key)
  255. if elements._is_literal(value):
  256. value = _create_bind_param(
  257. compiler, c, value, required=value is REQUIRED,
  258. name=_col_bind_name(c)
  259. if not stmt._has_multi_parameters
  260. else "%s_m0" % _col_bind_name(c),
  261. **kw
  262. )
  263. else:
  264. if isinstance(value, elements.BindParameter) and \
  265. value.type._isnull:
  266. value = value._clone()
  267. value.type = c.type
  268. if c.primary_key and implicit_returning:
  269. compiler.returning.append(c)
  270. value = compiler.process(value.self_group(), **kw)
  271. elif implicit_return_defaults and \
  272. c in implicit_return_defaults:
  273. compiler.returning.append(c)
  274. value = compiler.process(value.self_group(), **kw)
  275. else:
  276. compiler.postfetch.append(c)
  277. value = compiler.process(value.self_group(), **kw)
  278. values.append((c, value))
  279. def _append_param_insert_pk_returning(compiler, stmt, c, values, kw):
  280. """Create a primary key expression in the INSERT statement and
  281. possibly a RETURNING clause for it.
  282. If the column has a Python-side default, we will create a bound
  283. parameter for it and "pre-execute" the Python function. If
  284. the column has a SQL expression default, or is a sequence,
  285. we will add it directly into the INSERT statement and add a
  286. RETURNING element to get the new value. If the column has a
  287. server side default or is marked as the "autoincrement" column,
  288. we will add a RETRUNING element to get at the value.
  289. If all the above tests fail, that indicates a primary key column with no
  290. noted default generation capabilities that has no parameter passed;
  291. raise an exception.
  292. """
  293. if c.default is not None:
  294. if c.default.is_sequence:
  295. if compiler.dialect.supports_sequences and \
  296. (not c.default.optional or
  297. not compiler.dialect.sequences_optional):
  298. proc = compiler.process(c.default, **kw)
  299. values.append((c, proc))
  300. compiler.returning.append(c)
  301. elif c.default.is_clause_element:
  302. values.append(
  303. (c, compiler.process(
  304. c.default.arg.self_group(), **kw))
  305. )
  306. compiler.returning.append(c)
  307. else:
  308. values.append(
  309. (c, _create_insert_prefetch_bind_param(compiler, c))
  310. )
  311. elif c is stmt.table._autoincrement_column or c.server_default is not None:
  312. compiler.returning.append(c)
  313. elif not c.nullable:
  314. # no .default, no .server_default, not autoincrement, we have
  315. # no indication this primary key column will have any value
  316. _warn_pk_with_no_anticipated_value(c)
  317. def _create_insert_prefetch_bind_param(compiler, c, process=True, name=None):
  318. param = _create_bind_param(compiler, c, None, process=process, name=name)
  319. compiler.insert_prefetch.append(c)
  320. return param
  321. def _create_update_prefetch_bind_param(compiler, c, process=True, name=None):
  322. param = _create_bind_param(compiler, c, None, process=process, name=name)
  323. compiler.update_prefetch.append(c)
  324. return param
  325. class _multiparam_column(elements.ColumnElement):
  326. def __init__(self, original, index):
  327. self.key = "%s_m%d" % (original.key, index + 1)
  328. self.original = original
  329. self.default = original.default
  330. self.type = original.type
  331. def __eq__(self, other):
  332. return isinstance(other, _multiparam_column) and \
  333. other.key == self.key and \
  334. other.original == self.original
  335. def _process_multiparam_default_bind(compiler, stmt, c, index, kw):
  336. if not c.default:
  337. raise exc.CompileError(
  338. "INSERT value for column %s is explicitly rendered as a bound"
  339. "parameter in the VALUES clause; "
  340. "a Python-side value or SQL expression is required" % c)
  341. elif c.default.is_clause_element:
  342. return compiler.process(c.default.arg.self_group(), **kw)
  343. else:
  344. col = _multiparam_column(c, index)
  345. if isinstance(stmt, dml.Insert):
  346. return _create_insert_prefetch_bind_param(compiler, col)
  347. else:
  348. return _create_update_prefetch_bind_param(compiler, col)
  349. def _append_param_insert_pk(compiler, stmt, c, values, kw):
  350. """Create a bound parameter in the INSERT statement to receive a
  351. 'prefetched' default value.
  352. The 'prefetched' value indicates that we are to invoke a Python-side
  353. default function or expliclt SQL expression before the INSERT statement
  354. proceeds, so that we have a primary key value available.
  355. if the column has no noted default generation capabilities, it has
  356. no value passed in either; raise an exception.
  357. """
  358. if (
  359. (
  360. # column has a Python-side default
  361. c.default is not None and
  362. (
  363. # and it won't be a Sequence
  364. not c.default.is_sequence or
  365. compiler.dialect.supports_sequences
  366. )
  367. )
  368. or
  369. (
  370. # column is the "autoincrement column"
  371. c is stmt.table._autoincrement_column and
  372. (
  373. # and it's either a "sequence" or a
  374. # pre-executable "autoincrement" sequence
  375. compiler.dialect.supports_sequences or
  376. compiler.dialect.preexecute_autoincrement_sequences
  377. )
  378. )
  379. ):
  380. values.append(
  381. (c, _create_insert_prefetch_bind_param(compiler, c))
  382. )
  383. elif c.default is None and c.server_default is None and not c.nullable:
  384. # no .default, no .server_default, not autoincrement, we have
  385. # no indication this primary key column will have any value
  386. _warn_pk_with_no_anticipated_value(c)
  387. def _append_param_insert_hasdefault(
  388. compiler, stmt, c, implicit_return_defaults, values, kw):
  389. if c.default.is_sequence:
  390. if compiler.dialect.supports_sequences and \
  391. (not c.default.optional or
  392. not compiler.dialect.sequences_optional):
  393. proc = compiler.process(c.default, **kw)
  394. values.append((c, proc))
  395. if implicit_return_defaults and \
  396. c in implicit_return_defaults:
  397. compiler.returning.append(c)
  398. elif not c.primary_key:
  399. compiler.postfetch.append(c)
  400. elif c.default.is_clause_element:
  401. proc = compiler.process(c.default.arg.self_group(), **kw)
  402. values.append((c, proc))
  403. if implicit_return_defaults and \
  404. c in implicit_return_defaults:
  405. compiler.returning.append(c)
  406. elif not c.primary_key:
  407. # don't add primary key column to postfetch
  408. compiler.postfetch.append(c)
  409. else:
  410. values.append(
  411. (c, _create_insert_prefetch_bind_param(compiler, c))
  412. )
  413. def _append_param_insert_select_hasdefault(
  414. compiler, stmt, c, values, kw):
  415. if c.default.is_sequence:
  416. if compiler.dialect.supports_sequences and \
  417. (not c.default.optional or
  418. not compiler.dialect.sequences_optional):
  419. proc = c.default
  420. values.append((c, proc.next_value()))
  421. elif c.default.is_clause_element:
  422. proc = c.default.arg.self_group()
  423. values.append((c, proc))
  424. else:
  425. values.append(
  426. (c, _create_insert_prefetch_bind_param(compiler, c, process=False))
  427. )
  428. def _append_param_update(
  429. compiler, stmt, c, implicit_return_defaults, values, kw):
  430. if c.onupdate is not None and not c.onupdate.is_sequence:
  431. if c.onupdate.is_clause_element:
  432. values.append(
  433. (c, compiler.process(
  434. c.onupdate.arg.self_group(), **kw))
  435. )
  436. if implicit_return_defaults and \
  437. c in implicit_return_defaults:
  438. compiler.returning.append(c)
  439. else:
  440. compiler.postfetch.append(c)
  441. else:
  442. values.append(
  443. (c, _create_update_prefetch_bind_param(compiler, c))
  444. )
  445. elif c.server_onupdate is not None:
  446. if implicit_return_defaults and \
  447. c in implicit_return_defaults:
  448. compiler.returning.append(c)
  449. else:
  450. compiler.postfetch.append(c)
  451. elif implicit_return_defaults and \
  452. stmt._return_defaults is not True and \
  453. c in implicit_return_defaults:
  454. compiler.returning.append(c)
  455. def _get_multitable_params(
  456. compiler, stmt, stmt_parameters, check_columns,
  457. _col_bind_name, _getattr_col_key, values, kw):
  458. normalized_params = dict(
  459. (elements._clause_element_as_expr(c), param)
  460. for c, param in stmt_parameters.items()
  461. )
  462. affected_tables = set()
  463. for t in stmt._extra_froms:
  464. for c in t.c:
  465. if c in normalized_params:
  466. affected_tables.add(t)
  467. check_columns[_getattr_col_key(c)] = c
  468. value = normalized_params[c]
  469. if elements._is_literal(value):
  470. value = _create_bind_param(
  471. compiler, c, value, required=value is REQUIRED,
  472. name=_col_bind_name(c))
  473. else:
  474. compiler.postfetch.append(c)
  475. value = compiler.process(value.self_group(), **kw)
  476. values.append((c, value))
  477. # determine tables which are actually to be updated - process onupdate
  478. # and server_onupdate for these
  479. for t in affected_tables:
  480. for c in t.c:
  481. if c in normalized_params:
  482. continue
  483. elif (c.onupdate is not None and not
  484. c.onupdate.is_sequence):
  485. if c.onupdate.is_clause_element:
  486. values.append(
  487. (c, compiler.process(
  488. c.onupdate.arg.self_group(),
  489. **kw)
  490. )
  491. )
  492. compiler.postfetch.append(c)
  493. else:
  494. values.append(
  495. (c, _create_update_prefetch_bind_param(
  496. compiler, c, name=_col_bind_name(c)))
  497. )
  498. elif c.server_onupdate is not None:
  499. compiler.postfetch.append(c)
  500. def _extend_values_for_multiparams(compiler, stmt, values, kw):
  501. values_0 = values
  502. values = [values]
  503. values.extend(
  504. [
  505. (
  506. c,
  507. (_create_bind_param(
  508. compiler, c, row[c.key],
  509. name="%s_m%d" % (c.key, i + 1), **kw
  510. ) if elements._is_literal(row[c.key])
  511. else compiler.process(
  512. row[c.key].self_group(), **kw))
  513. if c.key in row else
  514. _process_multiparam_default_bind(compiler, stmt, c, i, kw)
  515. )
  516. for (c, param) in values_0
  517. ]
  518. for i, row in enumerate(stmt.parameters[1:])
  519. )
  520. return values
  521. def _get_stmt_parameters_params(
  522. compiler, parameters, stmt_parameters, _column_as_key, values, kw):
  523. for k, v in stmt_parameters.items():
  524. colkey = _column_as_key(k)
  525. if colkey is not None:
  526. parameters.setdefault(colkey, v)
  527. else:
  528. # a non-Column expression on the left side;
  529. # add it to values() in an "as-is" state,
  530. # coercing right side to bound param
  531. if elements._is_literal(v):
  532. v = compiler.process(
  533. elements.BindParameter(None, v, type_=k.type),
  534. **kw)
  535. else:
  536. v = compiler.process(v.self_group(), **kw)
  537. values.append((k, v))
  538. def _get_returning_modifiers(compiler, stmt):
  539. need_pks = compiler.isinsert and \
  540. not compiler.inline and \
  541. not stmt._returning and \
  542. not stmt._has_multi_parameters
  543. implicit_returning = need_pks and \
  544. compiler.dialect.implicit_returning and \
  545. stmt.table.implicit_returning
  546. if compiler.isinsert:
  547. implicit_return_defaults = (implicit_returning and
  548. stmt._return_defaults)
  549. elif compiler.isupdate:
  550. implicit_return_defaults = (compiler.dialect.implicit_returning and
  551. stmt.table.implicit_returning and
  552. stmt._return_defaults)
  553. else:
  554. # this line is unused, currently we are always
  555. # isinsert or isupdate
  556. implicit_return_defaults = False # pragma: no cover
  557. if implicit_return_defaults:
  558. if stmt._return_defaults is True:
  559. implicit_return_defaults = set(stmt.table.c)
  560. else:
  561. implicit_return_defaults = set(stmt._return_defaults)
  562. postfetch_lastrowid = need_pks and compiler.dialect.postfetch_lastrowid
  563. return need_pks, implicit_returning, \
  564. implicit_return_defaults, postfetch_lastrowid
  565. def _warn_pk_with_no_anticipated_value(c):
  566. msg = (
  567. "Column '%s.%s' is marked as a member of the "
  568. "primary key for table '%s', "
  569. "but has no Python-side or server-side default generator indicated, "
  570. "nor does it indicate 'autoincrement=True' or 'nullable=True', "
  571. "and no explicit value is passed. "
  572. "Primary key columns typically may not store NULL."
  573. %
  574. (c.table.fullname, c.name, c.table.fullname))
  575. if len(c.table.primary_key) > 1:
  576. msg += (
  577. " Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be "
  578. "indicated explicitly for composite (e.g. multicolumn) primary "
  579. "keys if AUTO_INCREMENT/SERIAL/IDENTITY "
  580. "behavior is expected for one of the columns in the primary key. "
  581. "CREATE TABLE statements are impacted by this change as well on "
  582. "most backends.")
  583. util.warn(msg)