From fadb8d61babb76ef7bdbc98279096a8900c7328d Mon Sep 17 00:00:00 2001 From: Frazer McLean Date: Sat, 11 Jun 2016 21:47:33 +0200 Subject: Implement comments for tables, columns Added support for SQL comments on :class:`.Table` and :class:`.Column` objects, via the new :paramref:`.Table.comment` and :paramref:`.Column.comment` arguments. The comments are included as part of DDL on table creation, either inline or via an appropriate ALTER statement, and are also reflected back within table reflection, as well as via the :class:`.Inspector`. Supported backends currently include MySQL, Postgresql, and Oracle. Co-authored-by: Mike Bayer Fixes: #1546 Change-Id: Ib90683850805a2b4ee198e420dc294f32f15d35d --- lib/sqlalchemy/sql/compiler.py | 29 ++++++++++++++++++++++++++--- lib/sqlalchemy/sql/ddl.py | 32 ++++++++++++++++++++++++++++++++ lib/sqlalchemy/sql/schema.py | 24 ++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 5 deletions(-) (limited to 'lib/sqlalchemy/sql') diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index a1d5a879d..e3bef8f82 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -2492,6 +2492,29 @@ class DDLCompiler(Compiled): self.process(create.element) ) + def visit_set_table_comment(self, create): + return "COMMENT ON TABLE %s IS %s" % ( + self.preparer.format_table(create.element), + self.sql_compiler.render_literal_value( + create.element.comment, sqltypes.String()) + ) + + def visit_drop_table_comment(self, drop): + return "COMMENT ON TABLE %s IS NULL" % \ + self.preparer.format_table(drop.element) + + def visit_set_column_comment(self, create): + return "COMMENT ON COLUMN %s IS %s" % ( + self.preparer.format_column( + create.element, use_table=True, use_schema=True), + self.sql_compiler.render_literal_value( + create.element.comment, sqltypes.String()) + ) + + def visit_drop_column_comment(self, drop): + return "COMMENT ON COLUMN %s IS NULL" % \ + self.preparer.format_column(drop.element, use_table=True) + def visit_create_sequence(self, create): text = "CREATE SEQUENCE %s" % \ self.preparer.format_sequence(create.element) @@ -2996,7 +3019,7 @@ class IdentifierPreparer(object): return self.quote(name, quote) def format_column(self, column, use_table=False, - name=None, table_name=None): + name=None, table_name=None, use_schema=False): """Prepare a quoted column name.""" if name is None: @@ -3004,7 +3027,7 @@ class IdentifierPreparer(object): if not getattr(column, 'is_literal', False): if use_table: return self.format_table( - column.table, use_schema=False, + column.table, use_schema=use_schema, name=table_name) + "." + self.quote(name) else: return self.quote(name) @@ -3014,7 +3037,7 @@ class IdentifierPreparer(object): if use_table: return self.format_table( - column.table, use_schema=False, + column.table, use_schema=use_schema, name=table_name) + '.' + name else: return name diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py index 5463afe99..74c424bff 100644 --- a/lib/sqlalchemy/sql/ddl.py +++ b/lib/sqlalchemy/sql/ddl.py @@ -661,6 +661,30 @@ class DropConstraint(_CreateDropBase): self._create_rule_disable) +class SetTableComment(_CreateDropBase): + """Represent a COMMENT ON TABLE IS statement.""" + + __visit_name__ = "set_table_comment" + + +class DropTableComment(_CreateDropBase): + """Represent a COMMENT ON TABLE IS NULL statement.""" + + __visit_name__ = "drop_table_comment" + + +class SetColumnComment(_CreateDropBase): + """Represent a COMMENT ON COLUMN IS statement.""" + + __visit_name__ = "set_column_comment" + + +class DropColumnComment(_CreateDropBase): + """Represent a COMMENT ON COLUMN IS NULL statement.""" + + __visit_name__ = "drop_column_comment" + + class DDLBase(SchemaVisitor): def __init__(self, connection): self.connection = connection @@ -771,6 +795,14 @@ class SchemaGenerator(DDLBase): for index in table.indexes: self.traverse_single(index) + if self.dialect.supports_comments and not self.dialect.inline_comments: + if table.comment is not None: + self.connection.execute(SetTableComment(table)) + + for column in table.columns: + if column.comment is not None: + self.connection.execute(SetColumnComment(column)) + table.dispatch.after_create( table, self.connection, checkfirst=self.checkfirst, diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index e6eabc461..accc1fe0d 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -371,6 +371,12 @@ class Table(DialectKWArgs, SchemaItem, TableClause): :param useexisting: Deprecated. Use :paramref:`.Table.extend_existing`. + :param comment: Optional string that will render an SQL comment on table + creation. + + .. versionadded:: 1.2 Added the :paramref:`.Table.comment` parameter + to :class:`.Table`. + :param \**kw: Additional keyword arguments not mentioned above are dialect specific, and passed in the form ``_``. See the documentation regarding an individual dialect at @@ -494,6 +500,8 @@ class Table(DialectKWArgs, SchemaItem, TableClause): self.implicit_returning = kwargs.pop('implicit_returning', True) + self.comment = kwargs.pop('comment', None) + if 'info' in kwargs: self.info = kwargs.pop('info') if 'listeners' in kwargs: @@ -588,6 +596,8 @@ class Table(DialectKWArgs, SchemaItem, TableClause): if 'info' in kwargs: self.info = kwargs.pop('info') + self.comment = kwargs.pop('comment', None) + if autoload: if not autoload_replace: # don't replace columns already present. @@ -1044,8 +1054,9 @@ class Column(SchemaItem, ColumnClause): :ref:`metadata_defaults_toplevel` :param doc: optional String that can be used by the ORM or similar - to document attributes. This attribute does not render SQL - comments (a future attribute 'comment' will achieve that). + to document attributes on the Python side. This attribute does + **not** render SQL comments; use the :paramref:`.Column.comment` + parameter for this purpose. :param key: An optional string identifier which will identify this ``Column`` object on the :class:`.Table`. When a key is provided, @@ -1159,6 +1170,13 @@ class Column(SchemaItem, ColumnClause): .. versionadded:: 0.8.3 Added the ``system=True`` parameter to :class:`.Column`. + :param comment: Optional string that will render an SQL comment on + table creation. + + .. versionadded:: 1.2 Added the :paramref:`.Column.comment` + parameter to :class:`.Column`. + + """ name = kwargs.pop('name', None) @@ -1205,6 +1223,7 @@ class Column(SchemaItem, ColumnClause): self.autoincrement = kwargs.pop('autoincrement', "auto") self.constraints = set() self.foreign_keys = set() + self.comment = kwargs.pop('comment', None) # check if this Column is proxying another column if '_proxies' in kwargs: @@ -2309,6 +2328,7 @@ class Sequence(DefaultGenerator): % self.__class__.__name__) + @inspection._self_inspects class FetchedValue(_NotAColumnExpr, SchemaEventTarget): """A marker for a transparent database-side default. -- cgit v1.2.1