summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrazer McLean <frazer@frazermclean.co.uk>2016-06-11 21:47:33 +0200
committerFrazer McLean <frazer@frazermclean.co.uk>2016-06-11 21:47:33 +0200
commit7ebfcd930f4d3f7b32e1a30a81a73d4f0f35000e (patch)
tree250677d68a0c6ba0070a97cd6cd3a7c2c2949c50
parent7189d0bc82598c2d6dcbb55b054837416db2ee7d (diff)
downloadsqlalchemy-pr/284.tar.gz
Initial implementation of comment supportpr/284
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py9
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py11
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py34
-rw-r--r--lib/sqlalchemy/engine/default.py1
-rw-r--r--lib/sqlalchemy/engine/reflection.py8
-rw-r--r--lib/sqlalchemy/schema.py6
-rw-r--r--lib/sqlalchemy/sql/compiler.py22
-rw-r--r--lib/sqlalchemy/sql/ddl.py45
-rw-r--r--lib/sqlalchemy/sql/schema.py5
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: