render.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. from sqlalchemy import schema as sa_schema, types as sqltypes, sql
  2. from ..operations import ops
  3. from ..util import compat
  4. import re
  5. from ..util.compat import string_types
  6. from .. import util
  7. from mako.pygen import PythonPrinter
  8. from ..util.compat import StringIO
  9. MAX_PYTHON_ARGS = 255
  10. try:
  11. from sqlalchemy.sql.naming import conv
  12. def _render_gen_name(autogen_context, name):
  13. if isinstance(name, conv):
  14. return _f_name(_alembic_autogenerate_prefix(autogen_context), name)
  15. else:
  16. return name
  17. except ImportError:
  18. def _render_gen_name(autogen_context, name):
  19. return name
  20. def _indent(text):
  21. text = re.compile(r'^', re.M).sub(" ", text).strip()
  22. text = re.compile(r' +$', re.M).sub("", text)
  23. return text
  24. def _render_python_into_templatevars(
  25. autogen_context, migration_script, template_args):
  26. imports = autogen_context.imports
  27. for upgrade_ops, downgrade_ops in zip(
  28. migration_script.upgrade_ops_list,
  29. migration_script.downgrade_ops_list):
  30. template_args[upgrade_ops.upgrade_token] = _indent(
  31. _render_cmd_body(upgrade_ops, autogen_context))
  32. template_args[downgrade_ops.downgrade_token] = _indent(
  33. _render_cmd_body(downgrade_ops, autogen_context))
  34. template_args['imports'] = "\n".join(sorted(imports))
  35. default_renderers = renderers = util.Dispatcher()
  36. def _render_cmd_body(op_container, autogen_context):
  37. buf = StringIO()
  38. printer = PythonPrinter(buf)
  39. printer.writeline(
  40. "# ### commands auto generated by Alembic - please adjust! ###"
  41. )
  42. if not op_container.ops:
  43. printer.writeline("pass")
  44. else:
  45. for op in op_container.ops:
  46. lines = render_op(autogen_context, op)
  47. for line in lines:
  48. printer.writeline(line)
  49. printer.writeline("# ### end Alembic commands ###")
  50. return buf.getvalue()
  51. def render_op(autogen_context, op):
  52. renderer = renderers.dispatch(op)
  53. lines = util.to_list(renderer(autogen_context, op))
  54. return lines
  55. def render_op_text(autogen_context, op):
  56. return "\n".join(render_op(autogen_context, op))
  57. @renderers.dispatch_for(ops.ModifyTableOps)
  58. def _render_modify_table(autogen_context, op):
  59. opts = autogen_context.opts
  60. render_as_batch = opts.get('render_as_batch', False)
  61. if op.ops:
  62. lines = []
  63. if render_as_batch:
  64. with autogen_context._within_batch():
  65. lines.append(
  66. "with op.batch_alter_table(%r, schema=%r) as batch_op:"
  67. % (op.table_name, op.schema)
  68. )
  69. for t_op in op.ops:
  70. t_lines = render_op(autogen_context, t_op)
  71. lines.extend(t_lines)
  72. lines.append("")
  73. else:
  74. for t_op in op.ops:
  75. t_lines = render_op(autogen_context, t_op)
  76. lines.extend(t_lines)
  77. return lines
  78. else:
  79. return [
  80. "pass"
  81. ]
  82. @renderers.dispatch_for(ops.CreateTableOp)
  83. def _add_table(autogen_context, op):
  84. table = op.to_table()
  85. args = [col for col in
  86. [_render_column(col, autogen_context) for col in table.columns]
  87. if col] + \
  88. sorted([rcons for rcons in
  89. [_render_constraint(cons, autogen_context) for cons in
  90. table.constraints]
  91. if rcons is not None
  92. ])
  93. if len(args) > MAX_PYTHON_ARGS:
  94. args = '*[' + ',\n'.join(args) + ']'
  95. else:
  96. args = ',\n'.join(args)
  97. text = "%(prefix)screate_table(%(tablename)r,\n%(args)s" % {
  98. 'tablename': _ident(op.table_name),
  99. 'prefix': _alembic_autogenerate_prefix(autogen_context),
  100. 'args': args,
  101. }
  102. if op.schema:
  103. text += ",\nschema=%r" % _ident(op.schema)
  104. for k in sorted(op.kw):
  105. text += ",\n%s=%r" % (k.replace(" ", "_"), op.kw[k])
  106. text += "\n)"
  107. return text
  108. @renderers.dispatch_for(ops.DropTableOp)
  109. def _drop_table(autogen_context, op):
  110. text = "%(prefix)sdrop_table(%(tname)r" % {
  111. "prefix": _alembic_autogenerate_prefix(autogen_context),
  112. "tname": _ident(op.table_name)
  113. }
  114. if op.schema:
  115. text += ", schema=%r" % _ident(op.schema)
  116. text += ")"
  117. return text
  118. @renderers.dispatch_for(ops.CreateIndexOp)
  119. def _add_index(autogen_context, op):
  120. index = op.to_index()
  121. has_batch = autogen_context._has_batch
  122. if has_batch:
  123. tmpl = "%(prefix)screate_index(%(name)r, [%(columns)s], "\
  124. "unique=%(unique)r%(kwargs)s)"
  125. else:
  126. tmpl = "%(prefix)screate_index(%(name)r, %(table)r, [%(columns)s], "\
  127. "unique=%(unique)r%(schema)s%(kwargs)s)"
  128. text = tmpl % {
  129. 'prefix': _alembic_autogenerate_prefix(autogen_context),
  130. 'name': _render_gen_name(autogen_context, index.name),
  131. 'table': _ident(index.table.name),
  132. 'columns': ", ".join(
  133. _get_index_rendered_expressions(index, autogen_context)),
  134. 'unique': index.unique or False,
  135. 'schema': (", schema=%r" % _ident(index.table.schema))
  136. if index.table.schema else '',
  137. 'kwargs': (
  138. ', ' +
  139. ', '.join(
  140. ["%s=%s" %
  141. (key, _render_potential_expr(val, autogen_context))
  142. for key, val in index.kwargs.items()]))
  143. if len(index.kwargs) else ''
  144. }
  145. return text
  146. @renderers.dispatch_for(ops.DropIndexOp)
  147. def _drop_index(autogen_context, op):
  148. has_batch = autogen_context._has_batch
  149. if has_batch:
  150. tmpl = "%(prefix)sdrop_index(%(name)r)"
  151. else:
  152. tmpl = "%(prefix)sdrop_index(%(name)r, "\
  153. "table_name=%(table_name)r%(schema)s)"
  154. text = tmpl % {
  155. 'prefix': _alembic_autogenerate_prefix(autogen_context),
  156. 'name': _render_gen_name(autogen_context, op.index_name),
  157. 'table_name': _ident(op.table_name),
  158. 'schema': ((", schema=%r" % _ident(op.schema))
  159. if op.schema else '')
  160. }
  161. return text
  162. @renderers.dispatch_for(ops.CreateUniqueConstraintOp)
  163. def _add_unique_constraint(autogen_context, op):
  164. return [_uq_constraint(op.to_constraint(), autogen_context, True)]
  165. @renderers.dispatch_for(ops.CreateForeignKeyOp)
  166. def _add_fk_constraint(autogen_context, op):
  167. args = [
  168. repr(
  169. _render_gen_name(autogen_context, op.constraint_name)),
  170. ]
  171. if not autogen_context._has_batch:
  172. args.append(
  173. repr(_ident(op.source_table))
  174. )
  175. args.extend(
  176. [
  177. repr(_ident(op.referent_table)),
  178. repr([_ident(col) for col in op.local_cols]),
  179. repr([_ident(col) for col in op.remote_cols])
  180. ]
  181. )
  182. kwargs = [
  183. 'referent_schema',
  184. 'onupdate', 'ondelete', 'initially',
  185. 'deferrable', 'use_alter'
  186. ]
  187. if not autogen_context._has_batch:
  188. kwargs.insert(0, 'source_schema')
  189. for k in kwargs:
  190. if k in op.kw:
  191. value = op.kw[k]
  192. if value is not None:
  193. args.append("%s=%r" % (k, value))
  194. return "%(prefix)screate_foreign_key(%(args)s)" % {
  195. 'prefix': _alembic_autogenerate_prefix(autogen_context),
  196. 'args': ", ".join(args)
  197. }
  198. @renderers.dispatch_for(ops.CreatePrimaryKeyOp)
  199. def _add_pk_constraint(constraint, autogen_context):
  200. raise NotImplementedError()
  201. @renderers.dispatch_for(ops.CreateCheckConstraintOp)
  202. def _add_check_constraint(constraint, autogen_context):
  203. raise NotImplementedError()
  204. @renderers.dispatch_for(ops.DropConstraintOp)
  205. def _drop_constraint(autogen_context, op):
  206. if autogen_context._has_batch:
  207. template = "%(prefix)sdrop_constraint"\
  208. "(%(name)r, type_=%(type)r)"
  209. else:
  210. template = "%(prefix)sdrop_constraint"\
  211. "(%(name)r, '%(table_name)s'%(schema)s, type_=%(type)r)"
  212. text = template % {
  213. 'prefix': _alembic_autogenerate_prefix(autogen_context),
  214. 'name': _render_gen_name(
  215. autogen_context, op.constraint_name),
  216. 'table_name': _ident(op.table_name),
  217. 'type': op.constraint_type,
  218. 'schema': (", schema='%s'" % _ident(op.schema))
  219. if op.schema else '',
  220. }
  221. return text
  222. @renderers.dispatch_for(ops.AddColumnOp)
  223. def _add_column(autogen_context, op):
  224. schema, tname, column = op.schema, op.table_name, op.column
  225. if autogen_context._has_batch:
  226. template = "%(prefix)sadd_column(%(column)s)"
  227. else:
  228. template = "%(prefix)sadd_column(%(tname)r, %(column)s"
  229. if schema:
  230. template += ", schema=%(schema)r"
  231. template += ")"
  232. text = template % {
  233. "prefix": _alembic_autogenerate_prefix(autogen_context),
  234. "tname": tname,
  235. "column": _render_column(column, autogen_context),
  236. "schema": schema
  237. }
  238. return text
  239. @renderers.dispatch_for(ops.DropColumnOp)
  240. def _drop_column(autogen_context, op):
  241. schema, tname, column_name = op.schema, op.table_name, op.column_name
  242. if autogen_context._has_batch:
  243. template = "%(prefix)sdrop_column(%(cname)r)"
  244. else:
  245. template = "%(prefix)sdrop_column(%(tname)r, %(cname)r"
  246. if schema:
  247. template += ", schema=%(schema)r"
  248. template += ")"
  249. text = template % {
  250. "prefix": _alembic_autogenerate_prefix(autogen_context),
  251. "tname": _ident(tname),
  252. "cname": _ident(column_name),
  253. "schema": _ident(schema)
  254. }
  255. return text
  256. @renderers.dispatch_for(ops.AlterColumnOp)
  257. def _alter_column(autogen_context, op):
  258. tname = op.table_name
  259. cname = op.column_name
  260. server_default = op.modify_server_default
  261. type_ = op.modify_type
  262. nullable = op.modify_nullable
  263. autoincrement = op.kw.get('autoincrement', None)
  264. existing_type = op.existing_type
  265. existing_nullable = op.existing_nullable
  266. existing_server_default = op.existing_server_default
  267. schema = op.schema
  268. indent = " " * 11
  269. if autogen_context._has_batch:
  270. template = "%(prefix)salter_column(%(cname)r"
  271. else:
  272. template = "%(prefix)salter_column(%(tname)r, %(cname)r"
  273. text = template % {
  274. 'prefix': _alembic_autogenerate_prefix(
  275. autogen_context),
  276. 'tname': tname,
  277. 'cname': cname}
  278. if existing_type is not None:
  279. text += ",\n%sexisting_type=%s" % (
  280. indent,
  281. _repr_type(existing_type, autogen_context))
  282. if server_default is not False:
  283. rendered = _render_server_default(
  284. server_default, autogen_context)
  285. text += ",\n%sserver_default=%s" % (indent, rendered)
  286. if type_ is not None:
  287. text += ",\n%stype_=%s" % (indent,
  288. _repr_type(type_, autogen_context))
  289. if nullable is not None:
  290. text += ",\n%snullable=%r" % (
  291. indent, nullable,)
  292. if nullable is None and existing_nullable is not None:
  293. text += ",\n%sexisting_nullable=%r" % (
  294. indent, existing_nullable)
  295. if autoincrement is not None:
  296. text += ",\n%sautoincrement=%r" % (
  297. indent, autoincrement)
  298. if server_default is False and existing_server_default:
  299. rendered = _render_server_default(
  300. existing_server_default,
  301. autogen_context)
  302. text += ",\n%sexisting_server_default=%s" % (
  303. indent, rendered)
  304. if schema and not autogen_context._has_batch:
  305. text += ",\n%sschema=%r" % (indent, schema)
  306. text += ")"
  307. return text
  308. class _f_name(object):
  309. def __init__(self, prefix, name):
  310. self.prefix = prefix
  311. self.name = name
  312. def __repr__(self):
  313. return "%sf(%r)" % (self.prefix, _ident(self.name))
  314. def _ident(name):
  315. """produce a __repr__() object for a string identifier that may
  316. use quoted_name() in SQLAlchemy 0.9 and greater.
  317. The issue worked around here is that quoted_name() doesn't have
  318. very good repr() behavior by itself when unicode is involved.
  319. """
  320. if name is None:
  321. return name
  322. elif util.sqla_09 and isinstance(name, sql.elements.quoted_name):
  323. if compat.py2k:
  324. # the attempt to encode to ascii here isn't super ideal,
  325. # however we are trying to cut down on an explosion of
  326. # u'' literals only when py2k + SQLA 0.9, in particular
  327. # makes unit tests testing code generation very difficult
  328. try:
  329. return name.encode('ascii')
  330. except UnicodeError:
  331. return compat.text_type(name)
  332. else:
  333. return compat.text_type(name)
  334. elif isinstance(name, compat.string_types):
  335. return name
  336. def _render_potential_expr(value, autogen_context, wrap_in_text=True):
  337. if isinstance(value, sql.ClauseElement):
  338. if util.sqla_08:
  339. compile_kw = dict(compile_kwargs={
  340. 'literal_binds': True, "include_table": False})
  341. else:
  342. compile_kw = {}
  343. if wrap_in_text:
  344. template = "%(prefix)stext(%(sql)r)"
  345. else:
  346. template = "%(sql)r"
  347. return template % {
  348. "prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
  349. "sql": compat.text_type(
  350. value.compile(dialect=autogen_context.dialect,
  351. **compile_kw)
  352. )
  353. }
  354. else:
  355. return repr(value)
  356. def _get_index_rendered_expressions(idx, autogen_context):
  357. if util.sqla_08:
  358. return [repr(_ident(getattr(exp, "name", None)))
  359. if isinstance(exp, sa_schema.Column)
  360. else _render_potential_expr(exp, autogen_context)
  361. for exp in idx.expressions]
  362. else:
  363. return [
  364. repr(_ident(getattr(col, "name", None))) for col in idx.columns]
  365. def _uq_constraint(constraint, autogen_context, alter):
  366. opts = []
  367. has_batch = autogen_context._has_batch
  368. if constraint.deferrable:
  369. opts.append(("deferrable", str(constraint.deferrable)))
  370. if constraint.initially:
  371. opts.append(("initially", str(constraint.initially)))
  372. if not has_batch and alter and constraint.table.schema:
  373. opts.append(("schema", _ident(constraint.table.schema)))
  374. if not alter and constraint.name:
  375. opts.append(
  376. ("name",
  377. _render_gen_name(autogen_context, constraint.name)))
  378. if alter:
  379. args = [
  380. repr(_render_gen_name(
  381. autogen_context, constraint.name))]
  382. if not has_batch:
  383. args += [repr(_ident(constraint.table.name))]
  384. args.append(repr([_ident(col.name) for col in constraint.columns]))
  385. args.extend(["%s=%r" % (k, v) for k, v in opts])
  386. return "%(prefix)screate_unique_constraint(%(args)s)" % {
  387. 'prefix': _alembic_autogenerate_prefix(autogen_context),
  388. 'args': ", ".join(args)
  389. }
  390. else:
  391. args = [repr(_ident(col.name)) for col in constraint.columns]
  392. args.extend(["%s=%r" % (k, v) for k, v in opts])
  393. return "%(prefix)sUniqueConstraint(%(args)s)" % {
  394. "prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
  395. "args": ", ".join(args)
  396. }
  397. def _user_autogenerate_prefix(autogen_context, target):
  398. prefix = autogen_context.opts['user_module_prefix']
  399. if prefix is None:
  400. return "%s." % target.__module__
  401. else:
  402. return prefix
  403. def _sqlalchemy_autogenerate_prefix(autogen_context):
  404. return autogen_context.opts['sqlalchemy_module_prefix'] or ''
  405. def _alembic_autogenerate_prefix(autogen_context):
  406. if autogen_context._has_batch:
  407. return 'batch_op.'
  408. else:
  409. return autogen_context.opts['alembic_module_prefix'] or ''
  410. def _user_defined_render(type_, object_, autogen_context):
  411. if 'render_item' in autogen_context.opts:
  412. render = autogen_context.opts['render_item']
  413. if render:
  414. rendered = render(type_, object_, autogen_context)
  415. if rendered is not False:
  416. return rendered
  417. return False
  418. def _render_column(column, autogen_context):
  419. rendered = _user_defined_render("column", column, autogen_context)
  420. if rendered is not False:
  421. return rendered
  422. opts = []
  423. if column.server_default:
  424. rendered = _render_server_default(
  425. column.server_default, autogen_context
  426. )
  427. if rendered:
  428. opts.append(("server_default", rendered))
  429. if not column.autoincrement:
  430. opts.append(("autoincrement", column.autoincrement))
  431. if column.nullable is not None:
  432. opts.append(("nullable", column.nullable))
  433. # TODO: for non-ascii colname, assign a "key"
  434. return "%(prefix)sColumn(%(name)r, %(type)s, %(kw)s)" % {
  435. 'prefix': _sqlalchemy_autogenerate_prefix(autogen_context),
  436. 'name': _ident(column.name),
  437. 'type': _repr_type(column.type, autogen_context),
  438. 'kw': ", ".join(["%s=%s" % (kwname, val) for kwname, val in opts])
  439. }
  440. def _render_server_default(default, autogen_context, repr_=True):
  441. rendered = _user_defined_render("server_default", default, autogen_context)
  442. if rendered is not False:
  443. return rendered
  444. if isinstance(default, sa_schema.DefaultClause):
  445. if isinstance(default.arg, compat.string_types):
  446. default = default.arg
  447. else:
  448. return _render_potential_expr(default.arg, autogen_context)
  449. if isinstance(default, string_types) and repr_:
  450. default = repr(re.sub(r"^'|'$", "", default))
  451. return default
  452. def _repr_type(type_, autogen_context):
  453. rendered = _user_defined_render("type", type_, autogen_context)
  454. if rendered is not False:
  455. return rendered
  456. if hasattr(autogen_context.migration_context, 'impl'):
  457. impl_rt = autogen_context.migration_context.impl.render_type(
  458. type_, autogen_context)
  459. mod = type(type_).__module__
  460. imports = autogen_context.imports
  461. if mod.startswith("sqlalchemy.dialects"):
  462. dname = re.match(r"sqlalchemy\.dialects\.(\w+)", mod).group(1)
  463. if imports is not None:
  464. imports.add("from sqlalchemy.dialects import %s" % dname)
  465. if impl_rt:
  466. return impl_rt
  467. else:
  468. return "%s.%r" % (dname, type_)
  469. elif mod.startswith("sqlalchemy."):
  470. prefix = _sqlalchemy_autogenerate_prefix(autogen_context)
  471. return "%s%r" % (prefix, type_)
  472. else:
  473. prefix = _user_autogenerate_prefix(autogen_context, type_)
  474. return "%s%r" % (prefix, type_)
  475. _constraint_renderers = util.Dispatcher()
  476. def _render_constraint(constraint, autogen_context):
  477. try:
  478. renderer = _constraint_renderers.dispatch(constraint)
  479. except ValueError:
  480. util.warn("No renderer is established for object %r" % constraint)
  481. return "[Unknown Python object %r]" % constraint
  482. else:
  483. return renderer(constraint, autogen_context)
  484. @_constraint_renderers.dispatch_for(sa_schema.PrimaryKeyConstraint)
  485. def _render_primary_key(constraint, autogen_context):
  486. rendered = _user_defined_render("primary_key", constraint, autogen_context)
  487. if rendered is not False:
  488. return rendered
  489. if not constraint.columns:
  490. return None
  491. opts = []
  492. if constraint.name:
  493. opts.append(("name", repr(
  494. _render_gen_name(autogen_context, constraint.name))))
  495. return "%(prefix)sPrimaryKeyConstraint(%(args)s)" % {
  496. "prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
  497. "args": ", ".join(
  498. [repr(c.name) for c in constraint.columns] +
  499. ["%s=%s" % (kwname, val) for kwname, val in opts]
  500. ),
  501. }
  502. def _fk_colspec(fk, metadata_schema):
  503. """Implement a 'safe' version of ForeignKey._get_colspec() that
  504. never tries to resolve the remote table.
  505. """
  506. colspec = fk._get_colspec()
  507. tokens = colspec.split(".")
  508. tname, colname = tokens[-2:]
  509. if metadata_schema is not None and len(tokens) == 2:
  510. table_fullname = "%s.%s" % (metadata_schema, tname)
  511. else:
  512. table_fullname = ".".join(tokens[0:-1])
  513. if fk.parent is not None and fk.parent.table is not None:
  514. # try to resolve the remote table and adjust for column.key
  515. parent_metadata = fk.parent.table.metadata
  516. if table_fullname in parent_metadata.tables:
  517. colname = _ident(
  518. parent_metadata.tables[table_fullname].c[colname].name)
  519. colspec = "%s.%s" % (table_fullname, colname)
  520. return colspec
  521. def _populate_render_fk_opts(constraint, opts):
  522. if constraint.onupdate:
  523. opts.append(("onupdate", repr(constraint.onupdate)))
  524. if constraint.ondelete:
  525. opts.append(("ondelete", repr(constraint.ondelete)))
  526. if constraint.initially:
  527. opts.append(("initially", repr(constraint.initially)))
  528. if constraint.deferrable:
  529. opts.append(("deferrable", repr(constraint.deferrable)))
  530. if constraint.use_alter:
  531. opts.append(("use_alter", repr(constraint.use_alter)))
  532. @_constraint_renderers.dispatch_for(sa_schema.ForeignKeyConstraint)
  533. def _render_foreign_key(constraint, autogen_context):
  534. rendered = _user_defined_render("foreign_key", constraint, autogen_context)
  535. if rendered is not False:
  536. return rendered
  537. opts = []
  538. if constraint.name:
  539. opts.append(("name", repr(
  540. _render_gen_name(autogen_context, constraint.name))))
  541. _populate_render_fk_opts(constraint, opts)
  542. apply_metadata_schema = constraint.parent.metadata.schema
  543. return "%(prefix)sForeignKeyConstraint([%(cols)s], "\
  544. "[%(refcols)s], %(args)s)" % {
  545. "prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
  546. "cols": ", ".join(
  547. "%r" % _ident(f.parent.name) for f in constraint.elements),
  548. "refcols": ", ".join(repr(_fk_colspec(f, apply_metadata_schema))
  549. for f in constraint.elements),
  550. "args": ", ".join(
  551. ["%s=%s" % (kwname, val) for kwname, val in opts]
  552. ),
  553. }
  554. @_constraint_renderers.dispatch_for(sa_schema.UniqueConstraint)
  555. def _render_unique_constraint(constraint, autogen_context):
  556. rendered = _user_defined_render("unique", constraint, autogen_context)
  557. if rendered is not False:
  558. return rendered
  559. return _uq_constraint(constraint, autogen_context, False)
  560. @_constraint_renderers.dispatch_for(sa_schema.CheckConstraint)
  561. def _render_check_constraint(constraint, autogen_context):
  562. rendered = _user_defined_render("check", constraint, autogen_context)
  563. if rendered is not False:
  564. return rendered
  565. # detect the constraint being part of
  566. # a parent type which is probably in the Table already.
  567. # ideally SQLAlchemy would give us more of a first class
  568. # way to detect this.
  569. if constraint._create_rule and \
  570. hasattr(constraint._create_rule, 'target') and \
  571. isinstance(constraint._create_rule.target,
  572. sqltypes.TypeEngine):
  573. return None
  574. opts = []
  575. if constraint.name:
  576. opts.append(
  577. (
  578. "name",
  579. repr(
  580. _render_gen_name(
  581. autogen_context, constraint.name))
  582. )
  583. )
  584. return "%(prefix)sCheckConstraint(%(sqltext)s%(opts)s)" % {
  585. "prefix": _sqlalchemy_autogenerate_prefix(autogen_context),
  586. "opts": ", " + (", ".join("%s=%s" % (k, v)
  587. for k, v in opts)) if opts else "",
  588. "sqltext": _render_potential_expr(
  589. constraint.sqltext, autogen_context, wrap_in_text=False)
  590. }
  591. @renderers.dispatch_for(ops.ExecuteSQLOp)
  592. def _execute_sql(autogen_context, op):
  593. if not isinstance(op.sqltext, string_types):
  594. raise NotImplementedError(
  595. "Autogenerate rendering of SQL Expression language constructs "
  596. "not supported here; please use a plain SQL string"
  597. )
  598. return 'op.execute(%r)' % op.sqltext
  599. renderers = default_renderers.branch()