Show More
@@ -2,7 +2,7 b'' | |||||
2 | Firebird database specific implementations of changeset classes. |
|
2 | Firebird database specific implementations of changeset classes. | |
3 | """ |
|
3 | """ | |
4 | from sqlalchemy.databases import firebird as sa_base |
|
4 | from sqlalchemy.databases import firebird as sa_base | |
5 |
|
5 | from sqlalchemy.schema import PrimaryKeyConstraint | ||
6 | from rhodecode.lib.dbmigrate.migrate import exceptions |
|
6 | from rhodecode.lib.dbmigrate.migrate import exceptions | |
7 | from rhodecode.lib.dbmigrate.migrate.changeset import ansisql, SQLA_06 |
|
7 | from rhodecode.lib.dbmigrate.migrate.changeset import ansisql, SQLA_06 | |
8 |
|
8 | |||
@@ -27,13 +27,32 b' class FBColumnDropper(ansisql.ANSIColumn' | |||||
27 | if column.table.primary_key.columns.contains_column(column): |
|
27 | if column.table.primary_key.columns.contains_column(column): | |
28 | column.table.primary_key.drop() |
|
28 | column.table.primary_key.drop() | |
29 | # TODO: recreate primary key if it references more than this column |
|
29 | # TODO: recreate primary key if it references more than this column | |
30 | if column.unique or getattr(column, 'unique_name', None): |
|
30 | ||
|
31 | for index in column.table.indexes: | |||
|
32 | # "column in index.columns" causes problems as all | |||
|
33 | # column objects compare equal and return a SQL expression | |||
|
34 | if column.name in [col.name for col in index.columns]: | |||
|
35 | index.drop() | |||
|
36 | # TODO: recreate index if it references more than this column | |||
|
37 | ||||
31 |
|
|
38 | for cons in column.table.constraints: | |
32 | if cons.contains_column(column): |
|
39 | if isinstance(cons,PrimaryKeyConstraint): | |
33 | cons.drop() |
|
40 | # will be deleted only when the column its on | |
|
41 | # is deleted! | |||
|
42 | continue | |||
|
43 | ||||
|
44 | if SQLA_06: | |||
|
45 | should_drop = column.name in cons.columns | |||
|
46 | else: | |||
|
47 | should_drop = cons.contains_column(column) and cons.name | |||
|
48 | if should_drop: | |||
|
49 | self.start_alter_table(column) | |||
|
50 | self.append("DROP CONSTRAINT ") | |||
|
51 | self.append(self.preparer.format_constraint(cons)) | |||
|
52 | self.execute() | |||
34 |
|
|
53 | # TODO: recreate unique constraint if it refenrences more than this column | |
35 |
|
54 | |||
36 |
|
|
55 | self.start_alter_table(column) | |
37 | self.append('DROP %s' % self.preparer.format_column(column)) |
|
56 | self.append('DROP %s' % self.preparer.format_column(column)) | |
38 | self.execute() |
|
57 | self.execute() | |
39 |
|
58 |
@@ -80,10 +80,17 b' class SQLiteColumnDropper(SQLiteHelper, ' | |||||
80 | """SQLite ColumnDropper""" |
|
80 | """SQLite ColumnDropper""" | |
81 |
|
81 | |||
82 | def _modify_table(self, table, column, delta): |
|
82 | def _modify_table(self, table, column, delta): | |
|
83 | ||||
83 | columns = ' ,'.join(map(self.preparer.format_column, table.columns)) |
|
84 | columns = ' ,'.join(map(self.preparer.format_column, table.columns)) | |
84 | return 'INSERT INTO %(table_name)s SELECT ' + columns + \ |
|
85 | return 'INSERT INTO %(table_name)s SELECT ' + columns + \ | |
85 | ' from migration_tmp' |
|
86 | ' from migration_tmp' | |
86 |
|
87 | |||
|
88 | def visit_column(self,column): | |||
|
89 | # For SQLite, we *have* to remove the column here so the table | |||
|
90 | # is re-created properly. | |||
|
91 | column.remove_from_table(column.table,unset_table=False) | |||
|
92 | super(SQLiteColumnDropper,self).visit_column(column) | |||
|
93 | ||||
87 |
|
94 | |||
88 | class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger): |
|
95 | class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger): | |
89 | """SQLite SchemaChanger""" |
|
96 | """SQLite SchemaChanger""" |
@@ -29,9 +29,6 b' from rhodecode.lib.dbmigrate.migrate.cha' | |||||
29 | 'ColumnDelta', |
|
29 | 'ColumnDelta', | |
30 | ] |
|
30 | ] | |
31 |
|
31 | |||
32 | DEFAULT_ALTER_METADATA = True |
|
|||
33 |
|
||||
34 |
|
||||
35 | def create_column(column, table=None, *p, **kw): |
|
32 | def create_column(column, table=None, *p, **kw): | |
36 | """Create a column, given the table. |
|
33 | """Create a column, given the table. | |
37 |
|
34 | |||
@@ -109,19 +106,11 b' def alter_column(*p, **k):' | |||||
109 | The :class:`~sqlalchemy.engine.base.Engine` to use for table |
|
106 | The :class:`~sqlalchemy.engine.base.Engine` to use for table | |
110 | reflection and schema alterations. |
|
107 | reflection and schema alterations. | |
111 |
|
108 | |||
112 | :param alter_metadata: |
|
|||
113 | If `True`, which is the default, the |
|
|||
114 | :class:`~sqlalchemy.schema.Column` will also modified. |
|
|||
115 | If `False`, the :class:`~sqlalchemy.schema.Column` will be left |
|
|||
116 | as it was. |
|
|||
117 |
|
||||
118 | :returns: A :class:`ColumnDelta` instance representing the change. |
|
109 | :returns: A :class:`ColumnDelta` instance representing the change. | |
119 |
|
110 | |||
120 |
|
111 | |||
121 | """ |
|
112 | """ | |
122 |
|
113 | |||
123 | k.setdefault('alter_metadata', DEFAULT_ALTER_METADATA) |
|
|||
124 |
|
||||
125 | if 'table' not in k and isinstance(p[0], sqlalchemy.Column): |
|
114 | if 'table' not in k and isinstance(p[0], sqlalchemy.Column): | |
126 | k['table'] = p[0].table |
|
115 | k['table'] = p[0].table | |
127 | if 'engine' not in k: |
|
116 | if 'engine' not in k: | |
@@ -135,6 +124,12 b' def alter_column(*p, **k):' | |||||
135 | MigrateDeprecationWarning |
|
124 | MigrateDeprecationWarning | |
136 | ) |
|
125 | ) | |
137 | engine = k['engine'] |
|
126 | engine = k['engine'] | |
|
127 | ||||
|
128 | # enough tests seem to break when metadata is always altered | |||
|
129 | # that this crutch has to be left in until they can be sorted | |||
|
130 | # out | |||
|
131 | k['alter_metadata']=True | |||
|
132 | ||||
138 | delta = ColumnDelta(*p, **k) |
|
133 | delta = ColumnDelta(*p, **k) | |
139 |
|
134 | |||
140 | visitorcallable = get_engine_visitor(engine, 'schemachanger') |
|
135 | visitorcallable = get_engine_visitor(engine, 'schemachanger') | |
@@ -188,11 +183,10 b' class ColumnDelta(DictMixin, sqlalchemy.' | |||||
188 | :param table: Table at which current Column should be bound to.\ |
|
183 | :param table: Table at which current Column should be bound to.\ | |
189 | If table name is given, reflection will be used. |
|
184 | If table name is given, reflection will be used. | |
190 | :type table: string or Table instance |
|
185 | :type table: string or Table instance | |
191 | :param alter_metadata: If True, it will apply changes to metadata. |
|
186 | ||
192 | :type alter_metadata: bool |
|
187 | :param metadata: A :class:`MetaData` instance to store | |
193 | :param metadata: If `alter_metadata` is true, \ |
|
188 | reflected table names | |
194 | metadata is used to reflect table names into |
|
189 | ||
195 | :type metadata: :class:`MetaData` instance |
|
|||
196 | :param engine: When reflecting tables, either engine or metadata must \ |
|
190 | :param engine: When reflecting tables, either engine or metadata must \ | |
197 | be specified to acquire engine object. |
|
191 | be specified to acquire engine object. | |
198 | :type engine: :class:`Engine` instance |
|
192 | :type engine: :class:`Engine` instance | |
@@ -213,7 +207,11 b' class ColumnDelta(DictMixin, sqlalchemy.' | |||||
213 | __visit_name__ = 'column' |
|
207 | __visit_name__ = 'column' | |
214 |
|
208 | |||
215 | def __init__(self, *p, **kw): |
|
209 | def __init__(self, *p, **kw): | |
|
210 | # 'alter_metadata' is not a public api. It exists purely | |||
|
211 | # as a crutch until the tests that fail when 'alter_metadata' | |||
|
212 | # behaviour always happens can be sorted out | |||
216 | self.alter_metadata = kw.pop("alter_metadata", False) |
|
213 | self.alter_metadata = kw.pop("alter_metadata", False) | |
|
214 | ||||
217 | self.meta = kw.pop("metadata", None) |
|
215 | self.meta = kw.pop("metadata", None) | |
218 | self.engine = kw.pop("engine", None) |
|
216 | self.engine = kw.pop("engine", None) | |
219 |
|
217 | |||
@@ -237,8 +235,10 b' class ColumnDelta(DictMixin, sqlalchemy.' | |||||
237 | self.apply_diffs(diffs) |
|
235 | self.apply_diffs(diffs) | |
238 |
|
236 | |||
239 | def __repr__(self): |
|
237 | def __repr__(self): | |
240 |
return '<ColumnDelta altermetadata=%r, %s>' % ( |
|
238 | return '<ColumnDelta altermetadata=%r, %s>' % ( | |
241 | super(ColumnDelta, self).__repr__()) |
|
239 | self.alter_metadata, | |
|
240 | super(ColumnDelta, self).__repr__() | |||
|
241 | ) | |||
242 |
|
242 | |||
243 | def __getitem__(self, key): |
|
243 | def __getitem__(self, key): | |
244 | if key not in self.keys(): |
|
244 | if key not in self.keys(): | |
@@ -395,7 +395,6 b' class ColumnDelta(DictMixin, sqlalchemy.' | |||||
395 | self._table = table |
|
395 | self._table = table | |
396 | if not self.alter_metadata: |
|
396 | if not self.alter_metadata: | |
397 | self._table.meta = sqlalchemy.MetaData(bind=self._table.bind) |
|
397 | self._table.meta = sqlalchemy.MetaData(bind=self._table.bind) | |
398 |
|
||||
399 | def _get_result_column(self): |
|
398 | def _get_result_column(self): | |
400 | return getattr(self, '_result_column', None) |
|
399 | return getattr(self, '_result_column', None) | |
401 |
|
400 | |||
@@ -456,19 +455,15 b' class ChangesetTable(object):' | |||||
456 |
|
455 | |||
457 | :param name: New name of the table. |
|
456 | :param name: New name of the table. | |
458 | :type name: string |
|
457 | :type name: string | |
459 | :param alter_metadata: If True, table will be removed from metadata |
|
|||
460 | :type alter_metadata: bool |
|
|||
461 | :param connection: reuse connection istead of creating new one. |
|
458 | :param connection: reuse connection istead of creating new one. | |
462 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance |
|
459 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance | |
463 | """ |
|
460 | """ | |
464 | self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) |
|
|||
465 | engine = self.bind |
|
461 | engine = self.bind | |
466 | self.new_name = name |
|
462 | self.new_name = name | |
467 | visitorcallable = get_engine_visitor(engine, 'schemachanger') |
|
463 | visitorcallable = get_engine_visitor(engine, 'schemachanger') | |
468 | run_single_visitor(engine, visitorcallable, self, connection, **kwargs) |
|
464 | run_single_visitor(engine, visitorcallable, self, connection, **kwargs) | |
469 |
|
465 | |||
470 | # Fix metadata registration |
|
466 | # Fix metadata registration | |
471 | if self.alter_metadata: |
|
|||
472 |
|
|
467 | self.name = name | |
473 |
|
|
468 | self.deregister() | |
474 |
|
|
469 | self._set_parent(self.metadata) | |
@@ -510,7 +505,6 b' class ChangesetColumn(object):' | |||||
510 | `~migrate.changeset.constraint.UniqueConstraint` on this column. |
|
505 | `~migrate.changeset.constraint.UniqueConstraint` on this column. | |
511 | :param primary_key_name: Creates :class:\ |
|
506 | :param primary_key_name: Creates :class:\ | |
512 | `~migrate.changeset.constraint.PrimaryKeyConstraint` on this column. |
|
507 | `~migrate.changeset.constraint.PrimaryKeyConstraint` on this column. | |
513 | :param alter_metadata: If True, column will be added to table object. |
|
|||
514 | :param populate_default: If True, created column will be \ |
|
508 | :param populate_default: If True, created column will be \ | |
515 | populated with defaults |
|
509 | populated with defaults | |
516 | :param connection: reuse connection istead of creating new one. |
|
510 | :param connection: reuse connection istead of creating new one. | |
@@ -518,21 +512,18 b' populated with defaults' | |||||
518 | :type index_name: string |
|
512 | :type index_name: string | |
519 | :type unique_name: string |
|
513 | :type unique_name: string | |
520 | :type primary_key_name: string |
|
514 | :type primary_key_name: string | |
521 | :type alter_metadata: bool |
|
|||
522 | :type populate_default: bool |
|
515 | :type populate_default: bool | |
523 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance |
|
516 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance | |
524 |
|
517 | |||
525 | :returns: self |
|
518 | :returns: self | |
526 | """ |
|
519 | """ | |
527 | self.populate_default = populate_default |
|
520 | self.populate_default = populate_default | |
528 | self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) |
|
|||
529 | self.index_name = index_name |
|
521 | self.index_name = index_name | |
530 | self.unique_name = unique_name |
|
522 | self.unique_name = unique_name | |
531 | self.primary_key_name = primary_key_name |
|
523 | self.primary_key_name = primary_key_name | |
532 | for cons in ('index_name', 'unique_name', 'primary_key_name'): |
|
524 | for cons in ('index_name', 'unique_name', 'primary_key_name'): | |
533 | self._check_sanity_constraints(cons) |
|
525 | self._check_sanity_constraints(cons) | |
534 |
|
526 | |||
535 | if self.alter_metadata: |
|
|||
536 |
|
|
527 | self.add_to_table(table) | |
537 | engine = self.table.bind |
|
528 | engine = self.table.bind | |
538 | visitorcallable = get_engine_visitor(engine, 'columngenerator') |
|
529 | visitorcallable = get_engine_visitor(engine, 'columngenerator') | |
@@ -550,20 +541,15 b' populated with defaults' | |||||
550 |
|
541 | |||
551 | ``ALTER TABLE DROP COLUMN``, for most databases. |
|
542 | ``ALTER TABLE DROP COLUMN``, for most databases. | |
552 |
|
543 | |||
553 | :param alter_metadata: If True, column will be removed from table object. |
|
|||
554 | :type alter_metadata: bool |
|
|||
555 | :param connection: reuse connection istead of creating new one. |
|
544 | :param connection: reuse connection istead of creating new one. | |
556 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance |
|
545 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance | |
557 | """ |
|
546 | """ | |
558 | self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) |
|
|||
559 | if table is not None: |
|
547 | if table is not None: | |
560 | self.table = table |
|
548 | self.table = table | |
561 | engine = self.table.bind |
|
549 | engine = self.table.bind | |
562 | if self.alter_metadata: |
|
|||
563 | self.remove_from_table(self.table, unset_table=False) |
|
|||
564 | visitorcallable = get_engine_visitor(engine, 'columndropper') |
|
550 | visitorcallable = get_engine_visitor(engine, 'columndropper') | |
565 | engine._run_visitor(visitorcallable, self, connection, **kwargs) |
|
551 | engine._run_visitor(visitorcallable, self, connection, **kwargs) | |
566 | if self.alter_metadata: |
|
552 | self.remove_from_table(self.table, unset_table=False) | |
567 |
|
|
553 | self.table = None | |
568 | return self |
|
554 | return self | |
569 |
|
555 | |||
@@ -643,17 +629,13 b' class ChangesetIndex(object):' | |||||
643 |
|
629 | |||
644 | :param name: New name of the Index. |
|
630 | :param name: New name of the Index. | |
645 | :type name: string |
|
631 | :type name: string | |
646 | :param alter_metadata: If True, Index object will be altered. |
|
|||
647 | :type alter_metadata: bool |
|
|||
648 | :param connection: reuse connection istead of creating new one. |
|
632 | :param connection: reuse connection istead of creating new one. | |
649 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance |
|
633 | :type connection: :class:`sqlalchemy.engine.base.Connection` instance | |
650 | """ |
|
634 | """ | |
651 | self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) |
|
|||
652 | engine = self.table.bind |
|
635 | engine = self.table.bind | |
653 | self.new_name = name |
|
636 | self.new_name = name | |
654 | visitorcallable = get_engine_visitor(engine, 'schemachanger') |
|
637 | visitorcallable = get_engine_visitor(engine, 'schemachanger') | |
655 | engine._run_visitor(visitorcallable, self, connection, **kwargs) |
|
638 | engine._run_visitor(visitorcallable, self, connection, **kwargs) | |
656 | if self.alter_metadata: |
|
|||
657 |
|
|
639 | self.name = name | |
658 |
|
640 | |||
659 |
|
641 |
@@ -169,11 +169,11 b' class ModelGenerator(object):' | |||||
169 | modelTable, col.name)) |
|
169 | modelTable, col.name)) | |
170 | for modelCol, databaseCol, modelDecl, databaseDecl in diffDecl: |
|
170 | for modelCol, databaseCol, modelDecl, databaseDecl in diffDecl: | |
171 | upgradeCommands.append( |
|
171 | upgradeCommands.append( | |
172 |
'assert False, "Can\'t alter columns: %s:%s=>%s"' |
|
172 | 'assert False, "Can\'t alter columns: %s:%s=>%s"' % ( | |
173 | modelTable, modelCol.name, databaseCol.name) |
|
173 | modelTable, modelCol.name, databaseCol.name)) | |
174 | downgradeCommands.append( |
|
174 | downgradeCommands.append( | |
175 |
'assert False, "Can\'t alter columns: %s:%s=>%s"' |
|
175 | 'assert False, "Can\'t alter columns: %s:%s=>%s"' % ( | |
176 | modelTable, modelCol.name, databaseCol.name) |
|
176 | modelTable, modelCol.name, databaseCol.name)) | |
177 | pre_command = ' meta.bind = migrate_engine' |
|
177 | pre_command = ' meta.bind = migrate_engine' | |
178 |
|
178 | |||
179 | return ( |
|
179 | return ( |
@@ -4,6 +4,7 b'' | |||||
4 | import shutil |
|
4 | import shutil | |
5 | import warnings |
|
5 | import warnings | |
6 | import logging |
|
6 | import logging | |
|
7 | import inspect | |||
7 | from StringIO import StringIO |
|
8 | from StringIO import StringIO | |
8 |
|
9 | |||
9 | from rhodecode.lib.dbmigrate import migrate |
|
10 | from rhodecode.lib.dbmigrate import migrate | |
@@ -136,12 +137,12 b' class PythonScript(base.BaseScript):' | |||||
136 | funcname = base.operations[op] |
|
137 | funcname = base.operations[op] | |
137 | script_func = self._func(funcname) |
|
138 | script_func = self._func(funcname) | |
138 |
|
139 | |||
139 | try: |
|
140 | # check for old way of using engine | |
|
141 | if not inspect.getargspec(script_func)[0]: | |||
|
142 | raise TypeError("upgrade/downgrade functions must accept engine" | |||
|
143 | " parameter (since version 0.5.4)") | |||
|
144 | ||||
140 |
|
|
145 | script_func(engine) | |
141 | except TypeError: |
|
|||
142 | warnings.warn("upgrade/downgrade functions must accept engine" |
|
|||
143 | " parameter (since version > 0.5.4)", MigrateDeprecationWarning) |
|
|||
144 | raise |
|
|||
145 |
|
146 | |||
146 | @property |
|
147 | @property | |
147 | def module(self): |
|
148 | def module(self): |
@@ -18,6 +18,7 b' class SqlScript(base.BaseScript):' | |||||
18 |
|
18 | |||
19 | :returns: :class:`SqlScript instance <migrate.versioning.script.sql.SqlScript>`""" |
|
19 | :returns: :class:`SqlScript instance <migrate.versioning.script.sql.SqlScript>`""" | |
20 | cls.require_notfound(path) |
|
20 | cls.require_notfound(path) | |
|
21 | ||||
21 | src = Template(opts.pop('templates_path', None)).get_sql_script(theme=opts.pop('templates_theme', None)) |
|
22 | src = Template(opts.pop('templates_path', None)).get_sql_script(theme=opts.pop('templates_theme', None)) | |
22 | shutil.copy(src, path) |
|
23 | shutil.copy(src, path) | |
23 | return cls(path) |
|
24 | return cls(path) |
@@ -77,8 +77,7 b' def main(argv=None, **kwargs):' | |||||
77 | %s |
|
77 | %s | |
78 |
|
78 | |||
79 | Enter "%%prog help COMMAND" for information on a particular command. |
|
79 | Enter "%%prog help COMMAND" for information on a particular command. | |
80 | """ % '\n\t'.join(["%s - %s" % (command.ljust(28), |
|
80 | """ % '\n\t'.join(["%s - %s" % (command.ljust(28), api.command_desc.get(command)) for command in commands]) | |
81 | api.command_desc.get(command)) for command in commands]) |
|
|||
82 |
|
81 | |||
83 | parser = PassiveOptionParser(usage=usage) |
|
82 | parser = PassiveOptionParser(usage=usage) | |
84 | parser.add_option("-d", "--debug", |
|
83 | parser.add_option("-d", "--debug", |
General Comments 0
You need to be logged in to leave comments.
Login now