diff options
author | Frazer McLean <frazer@frazermclean.co.uk> | 2016-06-11 21:47:33 +0200 |
---|---|---|
committer | Frazer McLean <frazer@frazermclean.co.uk> | 2016-06-11 21:47:33 +0200 |
commit | 7ebfcd930f4d3f7b32e1a30a81a73d4f0f35000e (patch) | |
tree | 250677d68a0c6ba0070a97cd6cd3a7c2c2949c50 | |
parent | 7189d0bc82598c2d6dcbb55b054837416db2ee7d (diff) | |
download | sqlalchemy-pr/284.tar.gz |
Initial implementation of comment supportpr/284
-rw-r--r-- | lib/sqlalchemy/dialects/mysql/base.py | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/oracle/base.py | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 34 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/default.py | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/reflection.py | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/schema.py | 6 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 22 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/ddl.py | 45 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/schema.py | 5 |
9 files changed, 134 insertions, 7 deletions
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 5abb1f3d6..025737fde 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1093,6 +1093,15 @@ class MySQLDDLCompiler(compiler.DDLCompiler): "causes ON UPDATE/ON DELETE clauses to be ignored.") return "" + def visit_set_table_comment(self, create): + return "ALTER TABLE %s COMMENT '%s'" % ( + self.preparer.format_table(create.element), + create.element.comment + ) + + def visit_set_column_comment(self, create): + raise NotImplementedError + class MySQLTypeCompiler(compiler.GenericTypeCompiler): def _extend_numeric(self, type_, spec): diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index 3af308cbb..1d9cd7fa0 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -1294,6 +1294,17 @@ class OracleDialect(default.DefaultDialect): return columns @reflection.cache + def get_table_comment(self, connection, table_name, schema=None, **kw): + COMMENT_SQL = """ + SELECT table_name, comments + FROM all_tab_comments + WHERE table_name = :table_name + """ + + c = connection.execute(sql.text(COMMENT_SQL), table_name=table_name) + return c.scalar() + + @reflection.cache def get_indexes(self, connection, table_name, schema=None, resolve_synonyms=False, dblink='', **kw): diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 16b22129a..3c111e3cc 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -2057,8 +2057,11 @@ class PGDialect(default.DefaultDialect): WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) AS DEFAULT, - a.attnotnull, a.attnum, a.attrelid as table_oid + a.attnotnull, a.attnum, a.attrelid as table_oid, + pgd.description as comment FROM pg_catalog.pg_attribute a + LEFT JOIN pg_catalog.pg_description pgd ON ( + pgd.objoid = a.attrelid AND pgd.objsubid = a.attnum) WHERE a.attrelid = :table_oid AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum @@ -2082,14 +2085,16 @@ class PGDialect(default.DefaultDialect): # format columns columns = [] - for name, format_type, default, notnull, attnum, table_oid in rows: + for name, format_type, default, notnull, attnum, table_oid, \ + comment in rows: column_info = self._get_column_info( - name, format_type, default, notnull, domains, enums, schema) + name, format_type, default, notnull, domains, enums, + schema, comment) columns.append(column_info) return columns def _get_column_info(self, name, format_type, default, - notnull, domains, enums, schema): + notnull, domains, enums, schema, comment): # strip (*) from character varying(5), timestamp(5) # with time zone, geometry(POLYGON), etc. attype = re.sub(r'\(.*\)', '', format_type) @@ -2196,7 +2201,8 @@ class PGDialect(default.DefaultDialect): match.group(2) + match.group(3) column_info = dict(name=name, type=coltype, nullable=nullable, - default=default, autoincrement=autoincrement) + default=default, autoincrement=autoincrement, + comment=comment) return column_info @reflection.cache @@ -2528,6 +2534,24 @@ class PGDialect(default.DefaultDialect): ] @reflection.cache + def get_table_comment(self, connection, table_name, schema=None, **kw): + table_oid = self.get_table_oid(connection, table_name, schema, + info_cache=kw.get('info_cache')) + + COMMENT_SQL = """ + SELECT + pgd.description as table_comment + FROM + pg_catalog.pg_description pgd + WHERE + pgd.objsubid = 0 AND + pgd.objoid = :table_oid + """ + + c = connection.execute(sql.text(COMMENT_SQL), table_oid=table_oid) + return c.scalar() + + @reflection.cache def get_check_constraints( self, connection, table_name, schema=None, **kw): table_oid = self.get_table_oid(connection, table_name, schema, diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 3ed2d5ee8..8474cce07 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -36,6 +36,7 @@ class DefaultDialect(interfaces.Dialect): type_compiler = compiler.GenericTypeCompiler preparer = compiler.IdentifierPreparer supports_alter = True + supports_comments = True # the first value we'd get for an autoincrement # column. diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py index 14e98cecf..722b4f786 100644 --- a/lib/sqlalchemy/engine/reflection.py +++ b/lib/sqlalchemy/engine/reflection.py @@ -506,6 +506,10 @@ class Inspector(object): return self.dialect.get_unique_constraints( self.bind, table_name, schema, info_cache=self.info_cache, **kw) + def get_table_comment(self, table_name, schema=None, **kw): + return self.dialect.get_table_comment( + self.bind, table_name, schema, info_cache=self.info_cache, **kw) + def get_check_constraints(self, table_name, schema=None, **kw): """Return information about check constraints in `table_name`. @@ -616,6 +620,8 @@ class Inspector(object): table_name, schema, table, cols_by_orig_name, include_columns, exclude_columns, reflection_options) + table.comment = self.get_table_comment(table_name, schema) + def _reflect_column( self, table, col_d, include_columns, exclude_columns, cols_by_orig_name): @@ -635,7 +641,7 @@ class Inspector(object): col_kw = dict( (k, col_d[k]) - for k in ['nullable', 'autoincrement', 'quote', 'info', 'key'] + for k in ['nullable', 'autoincrement', 'quote', 'info', 'key', 'comment'] if k in col_d ) diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index bd0cbe54e..eb4b2686c 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -62,5 +62,9 @@ from .sql.ddl import ( _CreateDropBase, _DDLCompiles, sort_tables, - sort_tables_and_constraints + sort_tables_and_constraints, + SetTableComment, + DropTableComment, + SetColumnComment, + DropColumnComment, ) diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 6d9ab9039..b26b0d1a7 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -2396,6 +2396,28 @@ 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_bindparam( + elements.BindParameter('comment', value=create.comment)) + ) + + 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), + self.sql_compiler.render_literal_bindparam( + elements.BindParameter('comment', value=create.comment)) + ) + + 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) diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py index 48f27b8b8..79fbc8ed6 100644 --- a/lib/sqlalchemy/sql/ddl.py +++ b/lib/sqlalchemy/sql/ddl.py @@ -486,6 +486,15 @@ class CreateTable(_CreateDropBase): ] self.include_foreign_key_constraints = include_foreign_key_constraints + if element.comment is not None: + event.listen(element, 'after_create', + SetTableComment(element, element.comment)) + + for col in element.columns: + if col.comment is not None: + event.listen(element, 'after_create', + SetColumnComment(col, col.comment)) + class _DropView(_CreateDropBase): """Semi-public 'DROP VIEW' construct. @@ -661,6 +670,42 @@ class DropConstraint(_CreateDropBase): self._create_rule_disable) +class SetTableComment(_CreateDropBase): + """Represent a COMMENT ON TABLE IS statement.""" + + __visit_name__ = "set_table_comment" + + def __init__(self, element, comment, **kw): + self.comment = comment + super(SetTableComment, self).__init__(element, **kw) + element._create_rule = util.portable_instancemethod( + self._create_rule_disable) + + +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" + + def __init__(self, element, comment, **kw): + self.comment = comment + super(SetColumnComment, self).__init__(element, **kw) + element._create_rule = util.portable_instancemethod( + self._create_rule_disable) + + +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 diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index cb01a49e3..ede9c7944 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -490,6 +490,7 @@ class Table(DialectKWArgs, SchemaItem, TableClause): include_columns = kwargs.pop('include_columns', None) self.implicit_returning = kwargs.pop('implicit_returning', True) + self.comment = kwargs.pop('comment', None) if 'info' in kwargs: self.info = kwargs.pop('info') @@ -579,6 +580,9 @@ class Table(DialectKWArgs, SchemaItem, TableClause): if 'info' in kwargs: self.info = kwargs.pop('info') + if 'comment' in kwargs: + self.comment = kwargs.pop('comment') + if autoload: if not autoload_replace: exclude_columns = [c.name for c in self.c] @@ -1176,6 +1180,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: |