| 1 | """ | 
|---|
| 2 | information schema implementation. | 
|---|
| 3 |  | 
|---|
| 4 | This module is deprecated and will not be present in this form in SQLAlchemy 0.6. | 
|---|
| 5 |  | 
|---|
| 6 | """ | 
|---|
| 7 | from sqlalchemy import util | 
|---|
| 8 |  | 
|---|
| 9 | util.warn_deprecated("the information_schema module is deprecated.") | 
|---|
| 10 |  | 
|---|
| 11 | import sqlalchemy.sql as sql | 
|---|
| 12 | import sqlalchemy.exc as exc | 
|---|
| 13 | from sqlalchemy import select, MetaData, Table, Column, String, Integer | 
|---|
| 14 | from sqlalchemy.schema import DefaultClause, ForeignKeyConstraint | 
|---|
| 15 |  | 
|---|
| 16 | ischema = MetaData() | 
|---|
| 17 |  | 
|---|
| 18 | schemata = Table("schemata", ischema, | 
|---|
| 19 |     Column("catalog_name", String), | 
|---|
| 20 |     Column("schema_name", String), | 
|---|
| 21 |     Column("schema_owner", String), | 
|---|
| 22 |     schema="information_schema") | 
|---|
| 23 |  | 
|---|
| 24 | tables = Table("tables", ischema, | 
|---|
| 25 |     Column("table_catalog", String), | 
|---|
| 26 |     Column("table_schema", String), | 
|---|
| 27 |     Column("table_name", String), | 
|---|
| 28 |     Column("table_type", String), | 
|---|
| 29 |     schema="information_schema") | 
|---|
| 30 |  | 
|---|
| 31 | columns = Table("columns", ischema, | 
|---|
| 32 |     Column("table_schema", String), | 
|---|
| 33 |     Column("table_name", String), | 
|---|
| 34 |     Column("column_name", String), | 
|---|
| 35 |     Column("is_nullable", Integer), | 
|---|
| 36 |     Column("data_type", String), | 
|---|
| 37 |     Column("ordinal_position", Integer), | 
|---|
| 38 |     Column("character_maximum_length", Integer), | 
|---|
| 39 |     Column("numeric_precision", Integer), | 
|---|
| 40 |     Column("numeric_scale", Integer), | 
|---|
| 41 |     Column("column_default", Integer), | 
|---|
| 42 |     Column("collation_name", String), | 
|---|
| 43 |     schema="information_schema") | 
|---|
| 44 |  | 
|---|
| 45 | constraints = Table("table_constraints", ischema, | 
|---|
| 46 |     Column("table_schema", String), | 
|---|
| 47 |     Column("table_name", String), | 
|---|
| 48 |     Column("constraint_name", String), | 
|---|
| 49 |     Column("constraint_type", String), | 
|---|
| 50 |     schema="information_schema") | 
|---|
| 51 |  | 
|---|
| 52 | column_constraints = Table("constraint_column_usage", ischema, | 
|---|
| 53 |     Column("table_schema", String), | 
|---|
| 54 |     Column("table_name", String), | 
|---|
| 55 |     Column("column_name", String), | 
|---|
| 56 |     Column("constraint_name", String), | 
|---|
| 57 |     schema="information_schema") | 
|---|
| 58 |  | 
|---|
| 59 | pg_key_constraints = Table("key_column_usage", ischema, | 
|---|
| 60 |     Column("table_schema", String), | 
|---|
| 61 |     Column("table_name", String), | 
|---|
| 62 |     Column("column_name", String), | 
|---|
| 63 |     Column("constraint_name", String), | 
|---|
| 64 |     Column("ordinal_position", Integer), | 
|---|
| 65 |     schema="information_schema") | 
|---|
| 66 |  | 
|---|
| 67 | #mysql_key_constraints = Table("key_column_usage", ischema, | 
|---|
| 68 | #    Column("table_schema", String), | 
|---|
| 69 | #    Column("table_name", String), | 
|---|
| 70 | #    Column("column_name", String), | 
|---|
| 71 | #    Column("constraint_name", String), | 
|---|
| 72 | #    Column("referenced_table_schema", String), | 
|---|
| 73 | #    Column("referenced_table_name", String), | 
|---|
| 74 | #    Column("referenced_column_name", String), | 
|---|
| 75 | #    schema="information_schema") | 
|---|
| 76 |  | 
|---|
| 77 | key_constraints = pg_key_constraints | 
|---|
| 78 |  | 
|---|
| 79 | ref_constraints = Table("referential_constraints", ischema, | 
|---|
| 80 |     Column("constraint_catalog", String), | 
|---|
| 81 |     Column("constraint_schema", String), | 
|---|
| 82 |     Column("constraint_name", String), | 
|---|
| 83 |     Column("unique_constraint_catlog", String), | 
|---|
| 84 |     Column("unique_constraint_schema", String), | 
|---|
| 85 |     Column("unique_constraint_name", String), | 
|---|
| 86 |     Column("match_option", String), | 
|---|
| 87 |     Column("update_rule", String), | 
|---|
| 88 |     Column("delete_rule", String), | 
|---|
| 89 |     schema="information_schema") | 
|---|
| 90 |  | 
|---|
| 91 |  | 
|---|
| 92 | def table_names(connection, schema): | 
|---|
| 93 |     s = select([tables.c.table_name], tables.c.table_schema==schema) | 
|---|
| 94 |     return [row[0] for row in connection.execute(s)] | 
|---|
| 95 |  | 
|---|
| 96 |  | 
|---|
| 97 | def reflecttable(connection, table, include_columns, ischema_names): | 
|---|
| 98 |     key_constraints = pg_key_constraints | 
|---|
| 99 |  | 
|---|
| 100 |     if table.schema is not None: | 
|---|
| 101 |         current_schema = table.schema | 
|---|
| 102 |     else: | 
|---|
| 103 |         current_schema = connection.default_schema_name() | 
|---|
| 104 |  | 
|---|
| 105 |     s = select([columns], | 
|---|
| 106 |         sql.and_(columns.c.table_name==table.name, | 
|---|
| 107 |         columns.c.table_schema==current_schema), | 
|---|
| 108 |         order_by=[columns.c.ordinal_position]) | 
|---|
| 109 |  | 
|---|
| 110 |     c = connection.execute(s) | 
|---|
| 111 |     found_table = False | 
|---|
| 112 |     while True: | 
|---|
| 113 |         row = c.fetchone() | 
|---|
| 114 |         if row is None: | 
|---|
| 115 |             break | 
|---|
| 116 |         #print "row! " + repr(row) | 
|---|
| 117 |  #       continue | 
|---|
| 118 |         found_table = True | 
|---|
| 119 |         (name, type, nullable, charlen, numericprec, numericscale, default) = ( | 
|---|
| 120 |             row[columns.c.column_name], | 
|---|
| 121 |             row[columns.c.data_type], | 
|---|
| 122 |             row[columns.c.is_nullable] == 'YES', | 
|---|
| 123 |             row[columns.c.character_maximum_length], | 
|---|
| 124 |             row[columns.c.numeric_precision], | 
|---|
| 125 |             row[columns.c.numeric_scale], | 
|---|
| 126 |             row[columns.c.column_default] | 
|---|
| 127 |             ) | 
|---|
| 128 |         if include_columns and name not in include_columns: | 
|---|
| 129 |             continue | 
|---|
| 130 |  | 
|---|
| 131 |         args = [] | 
|---|
| 132 |         for a in (charlen, numericprec, numericscale): | 
|---|
| 133 |             if a is not None: | 
|---|
| 134 |                 args.append(a) | 
|---|
| 135 |         coltype = ischema_names[type] | 
|---|
| 136 |         #print "coltype " + repr(coltype) + " args " +  repr(args) | 
|---|
| 137 |         coltype = coltype(*args) | 
|---|
| 138 |         colargs = [] | 
|---|
| 139 |         if default is not None: | 
|---|
| 140 |             colargs.append(DefaultClause(sql.text(default))) | 
|---|
| 141 |         table.append_column(Column(name, coltype, nullable=nullable, *colargs)) | 
|---|
| 142 |  | 
|---|
| 143 |     if not found_table: | 
|---|
| 144 |         raise exc.NoSuchTableError(table.name) | 
|---|
| 145 |  | 
|---|
| 146 |     # we are relying on the natural ordering of the constraint_column_usage table to return the referenced columns | 
|---|
| 147 |     # in an order that corresponds to the ordinal_position in the key_constraints table, otherwise composite foreign keys | 
|---|
| 148 |     # wont reflect properly.  dont see a way around this based on whats available from information_schema | 
|---|
| 149 |     s = select([constraints.c.constraint_name, constraints.c.constraint_type, constraints.c.table_name, key_constraints], use_labels=True, from_obj=[constraints.join(column_constraints, column_constraints.c.constraint_name==constraints.c.constraint_name).join(key_constraints, key_constraints.c.constraint_name==column_constraints.c.constraint_name)], order_by=[key_constraints.c.ordinal_position]) | 
|---|
| 150 |     s.append_column(column_constraints) | 
|---|
| 151 |     s.append_whereclause(constraints.c.table_name==table.name) | 
|---|
| 152 |     s.append_whereclause(constraints.c.table_schema==current_schema) | 
|---|
| 153 |     colmap = [constraints.c.constraint_type, key_constraints.c.column_name, column_constraints.c.table_schema, column_constraints.c.table_name, column_constraints.c.column_name, constraints.c.constraint_name, key_constraints.c.ordinal_position] | 
|---|
| 154 |     c = connection.execute(s) | 
|---|
| 155 |  | 
|---|
| 156 |     fks = {} | 
|---|
| 157 |     while True: | 
|---|
| 158 |         row = c.fetchone() | 
|---|
| 159 |         if row is None: | 
|---|
| 160 |             break | 
|---|
| 161 |         (type, constrained_column, referred_schema, referred_table, referred_column, constraint_name, ordinal_position) = ( | 
|---|
| 162 |             row[colmap[0]], | 
|---|
| 163 |             row[colmap[1]], | 
|---|
| 164 |             row[colmap[2]], | 
|---|
| 165 |             row[colmap[3]], | 
|---|
| 166 |             row[colmap[4]], | 
|---|
| 167 |             row[colmap[5]], | 
|---|
| 168 |             row[colmap[6]] | 
|---|
| 169 |         ) | 
|---|
| 170 |         #print "type %s on column %s to remote %s.%s.%s" % (type, constrained_column, referred_schema, referred_table, referred_column) | 
|---|
| 171 |         if type == 'PRIMARY KEY': | 
|---|
| 172 |             table.primary_key.add(table.c[constrained_column]) | 
|---|
| 173 |         elif type == 'FOREIGN KEY': | 
|---|
| 174 |             try: | 
|---|
| 175 |                 fk = fks[constraint_name] | 
|---|
| 176 |             except KeyError: | 
|---|
| 177 |                 fk = ([], []) | 
|---|
| 178 |                 fks[constraint_name] = fk | 
|---|
| 179 |             if current_schema == referred_schema: | 
|---|
| 180 |                 referred_schema = table.schema | 
|---|
| 181 |             if referred_schema is not None: | 
|---|
| 182 |                 Table(referred_table, table.metadata, autoload=True, schema=referred_schema, autoload_with=connection) | 
|---|
| 183 |                 refspec = ".".join([referred_schema, referred_table, referred_column]) | 
|---|
| 184 |             else: | 
|---|
| 185 |                 Table(referred_table, table.metadata, autoload=True, autoload_with=connection) | 
|---|
| 186 |                 refspec = ".".join([referred_table, referred_column]) | 
|---|
| 187 |             if constrained_column not in fk[0]: | 
|---|
| 188 |                 fk[0].append(constrained_column) | 
|---|
| 189 |             if refspec not in fk[1]: | 
|---|
| 190 |                 fk[1].append(refspec) | 
|---|
| 191 |  | 
|---|
| 192 |     for name, value in fks.iteritems(): | 
|---|
| 193 |         table.append_constraint(ForeignKeyConstraint(value[0], value[1], name=name)) | 
|---|