Show More
@@ -28,7 +28,7 b' class ConstraintChangeset(object):' | |||||
28 | def __do_imports(self, visitor_name, *a, **kw): |
|
28 | def __do_imports(self, visitor_name, *a, **kw): | |
29 | engine = kw.pop('engine', self.table.bind) |
|
29 | engine = kw.pop('engine', self.table.bind) | |
30 | from rhodecode.lib.dbmigrate.migrate.changeset.databases.visitor import (get_engine_visitor, |
|
30 | from rhodecode.lib.dbmigrate.migrate.changeset.databases.visitor import (get_engine_visitor, | |
31 | run_single_visitor) |
|
31 | run_single_visitor) | |
32 | visitorcallable = get_engine_visitor(engine, visitor_name) |
|
32 | visitorcallable = get_engine_visitor(engine, visitor_name) | |
33 | run_single_visitor(engine, visitorcallable, self, *a, **kw) |
|
33 | run_single_visitor(engine, visitorcallable, self, *a, **kw) | |
34 |
|
34 |
@@ -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): |
|
|||
31 | for cons in column.table.constraints: |
|
|||
32 | if cons.contains_column(column): |
|
|||
33 | cons.drop() |
|
|||
34 | # TODO: recreate unique constraint if it refenrences more than this column |
|
|||
35 |
|
30 | |||
36 | table = self.start_alter_table(column) |
|
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 | ||||
|
38 | for cons in column.table.constraints: | |||
|
39 | if isinstance(cons,PrimaryKeyConstraint): | |||
|
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() | |||
|
53 | # TODO: recreate unique constraint if it refenrences more than this column | |||
|
54 | ||||
|
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,17 +235,19 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, | |
242 |
|
240 | super(ColumnDelta, self).__repr__() | ||
|
241 | ) | |||
|
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(): | |
245 | raise KeyError("No such diff key, available: %s" % self.diffs) |
|
245 | raise KeyError("No such diff key, available: %s" % self.diffs ) | |
246 | return getattr(self.result_column, key) |
|
246 | return getattr(self.result_column, key) | |
247 |
|
247 | |||
248 | def __setitem__(self, key, value): |
|
248 | def __setitem__(self, key, value): | |
249 | if key not in self.keys(): |
|
249 | if key not in self.keys(): | |
250 | raise KeyError("No such diff key, available: %s" % self.diffs) |
|
250 | raise KeyError("No such diff key, available: %s" % self.diffs ) | |
251 | setattr(self.result_column, key, value) |
|
251 | setattr(self.result_column, key, value) | |
252 |
|
252 | |||
253 | def __delitem__(self, key): |
|
253 | def __delitem__(self, key): | |
@@ -367,7 +367,7 b' class ColumnDelta(DictMixin, sqlalchemy.' | |||||
367 | for_update=True)) |
|
367 | for_update=True)) | |
368 | if toinit: |
|
368 | if toinit: | |
369 | column._init_items(*toinit) |
|
369 | column._init_items(*toinit) | |
370 |
|
370 | |||
371 | if not SQLA_06: |
|
371 | if not SQLA_06: | |
372 | column.args = [] |
|
372 | column.args = [] | |
373 |
|
373 | |||
@@ -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,22 +455,18 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 |
|
|
467 | self.name = name | |
472 | self.name = name |
|
468 | self.deregister() | |
473 | self.deregister() |
|
469 | self._set_parent(self.metadata) | |
474 | self._set_parent(self.metadata) |
|
|||
475 |
|
470 | |||
476 | def _meta_key(self): |
|
471 | def _meta_key(self): | |
477 | return sqlalchemy.schema._get_table_key(self.name, self.schema) |
|
472 | return sqlalchemy.schema._get_table_key(self.name, self.schema) | |
@@ -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,22 +512,19 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 |
|
|
527 | self.add_to_table(table) | |
536 | 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') | |
539 | engine._run_visitor(visitorcallable, self, connection, **kwargs) |
|
530 | engine._run_visitor(visitorcallable, self, connection, **kwargs) | |
@@ -550,59 +541,54 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 | |||
570 | def add_to_table(self, table): |
|
556 | def add_to_table(self, table): | |
571 | if table is not None and self.table is None: |
|
557 | if table is not None and self.table is None: | |
572 | self._set_parent(table) |
|
558 | self._set_parent(table) | |
573 |
|
559 | |||
574 |
def _col_name_in_constraint(self, |
|
560 | def _col_name_in_constraint(self,cons,name): | |
575 | return False |
|
561 | return False | |
576 |
|
562 | |||
577 | def remove_from_table(self, table, unset_table=True): |
|
563 | def remove_from_table(self, table, unset_table=True): | |
578 | # TODO: remove primary keys, constraints, etc |
|
564 | # TODO: remove primary keys, constraints, etc | |
579 | if unset_table: |
|
565 | if unset_table: | |
580 | self.table = None |
|
566 | self.table = None | |
581 |
|
567 | |||
582 | to_drop = set() |
|
568 | to_drop = set() | |
583 | for index in table.indexes: |
|
569 | for index in table.indexes: | |
584 | columns = [] |
|
570 | columns = [] | |
585 | for col in index.columns: |
|
571 | for col in index.columns: | |
586 |
if col.name |
|
572 | if col.name!=self.name: | |
587 | columns.append(col) |
|
573 | columns.append(col) | |
588 | if columns: |
|
574 | if columns: | |
589 |
index.columns |
|
575 | index.columns=columns | |
590 | else: |
|
576 | else: | |
591 | to_drop.add(index) |
|
577 | to_drop.add(index) | |
592 | table.indexes = table.indexes - to_drop |
|
578 | table.indexes = table.indexes - to_drop | |
593 |
|
579 | |||
594 | to_drop = set() |
|
580 | to_drop = set() | |
595 | for cons in table.constraints: |
|
581 | for cons in table.constraints: | |
596 | # TODO: deal with other types of constraint |
|
582 | # TODO: deal with other types of constraint | |
597 |
if isinstance(cons, |
|
583 | if isinstance(cons,(ForeignKeyConstraint, | |
598 | UniqueConstraint)): |
|
584 | UniqueConstraint)): | |
599 | for col_name in cons.columns: |
|
585 | for col_name in cons.columns: | |
600 |
if not isinstance(col_name, |
|
586 | if not isinstance(col_name,basestring): | |
601 | col_name = col_name.name |
|
587 | col_name = col_name.name | |
602 |
if self.name |
|
588 | if self.name==col_name: | |
603 | to_drop.add(cons) |
|
589 | to_drop.add(cons) | |
604 | table.constraints = table.constraints - to_drop |
|
590 | table.constraints = table.constraints - to_drop | |
605 |
|
591 | |||
606 | if table.c.contains_column(self): |
|
592 | if table.c.contains_column(self): | |
607 | table.c.remove(self) |
|
593 | table.c.remove(self) | |
608 |
|
594 | |||
@@ -643,18 +629,14 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 |
|
|
639 | self.name = name | |
657 | self.name = name |
|
|||
658 |
|
640 | |||
659 |
|
641 | |||
660 | class ChangesetDefaultClause(object): |
|
642 | class ChangesetDefaultClause(object): |
@@ -111,12 +111,12 b' class ModelGenerator(object):' | |||||
111 | out.append(")") |
|
111 | out.append(")") | |
112 | return out |
|
112 | return out | |
113 |
|
113 | |||
114 |
def _get_tables(self, |
|
114 | def _get_tables(self,missingA=False,missingB=False,modified=False): | |
115 | to_process = [] |
|
115 | to_process = [] | |
116 |
for bool_, |
|
116 | for bool_,names,metadata in ( | |
117 |
(missingA, |
|
117 | (missingA,self.diff.tables_missing_from_A,self.diff.metadataB), | |
118 |
(missingB, |
|
118 | (missingB,self.diff.tables_missing_from_B,self.diff.metadataA), | |
119 |
(modified, |
|
119 | (modified,self.diff.tables_different,self.diff.metadataA), | |
120 | ): |
|
120 | ): | |
121 | if bool_: |
|
121 | if bool_: | |
122 | for name in names: |
|
122 | for name in names: | |
@@ -140,7 +140,7 b' class ModelGenerator(object):' | |||||
140 | decls = ['from rhodecode.lib.dbmigrate.migrate.changeset import schema', |
|
140 | decls = ['from rhodecode.lib.dbmigrate.migrate.changeset import schema', | |
141 | 'meta = MetaData()'] |
|
141 | 'meta = MetaData()'] | |
142 | for table in self._get_tables( |
|
142 | for table in self._get_tables( | |
143 |
missingA=True, |
|
143 | missingA=True,missingB=True,modified=True | |
144 | ): |
|
144 | ): | |
145 | decls.extend(self.getTableDefn(table)) |
|
145 | decls.extend(self.getTableDefn(table)) | |
146 |
|
146 | |||
@@ -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 ( | |
@@ -181,7 +181,7 b' class ModelGenerator(object):' | |||||
181 | '\n'.join([pre_command] + ['%s%s' % (indent, line) for line in upgradeCommands]), |
|
181 | '\n'.join([pre_command] + ['%s%s' % (indent, line) for line in upgradeCommands]), | |
182 | '\n'.join([pre_command] + ['%s%s' % (indent, line) for line in downgradeCommands])) |
|
182 | '\n'.join([pre_command] + ['%s%s' % (indent, line) for line in downgradeCommands])) | |
183 |
|
183 | |||
184 |
def _db_can_handle_this_change(self, |
|
184 | def _db_can_handle_this_change(self,td): | |
185 | if (td.columns_missing_from_B |
|
185 | if (td.columns_missing_from_B | |
186 | and not td.columns_missing_from_A |
|
186 | and not td.columns_missing_from_A | |
187 | and not td.columns_different): |
|
187 | and not td.columns_different): | |
@@ -207,9 +207,9 b' class ModelGenerator(object):' | |||||
207 | dbTable = self.diff.metadataB.tables[tableName] |
|
207 | dbTable = self.diff.metadataB.tables[tableName] | |
208 |
|
208 | |||
209 | td = self.diff.tables_different[tableName] |
|
209 | td = self.diff.tables_different[tableName] | |
210 |
|
210 | |||
211 | if self._db_can_handle_this_change(td): |
|
211 | if self._db_can_handle_this_change(td): | |
212 |
|
212 | |||
213 | for col in td.columns_missing_from_B: |
|
213 | for col in td.columns_missing_from_B: | |
214 | modelTable.columns[col].create() |
|
214 | modelTable.columns[col].create() | |
215 | for col in td.columns_missing_from_A: |
|
215 | for col in td.columns_missing_from_A: |
@@ -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 | |
@@ -49,7 +50,7 b' class PythonScript(base.BaseScript):' | |||||
49 | :returns: Upgrade / Downgrade script |
|
50 | :returns: Upgrade / Downgrade script | |
50 | :rtype: string |
|
51 | :rtype: string | |
51 | """ |
|
52 | """ | |
52 |
|
53 | |||
53 | if isinstance(repository, basestring): |
|
54 | if isinstance(repository, basestring): | |
54 | # oh dear, an import cycle! |
|
55 | # oh dear, an import cycle! | |
55 | from rhodecode.lib.dbmigrate.migrate.versioning.repository import Repository |
|
56 | from rhodecode.lib.dbmigrate.migrate.versioning.repository import Repository | |
@@ -65,7 +66,7 b' class PythonScript(base.BaseScript):' | |||||
65 | excludeTables=[repository.version_table]) |
|
66 | excludeTables=[repository.version_table]) | |
66 | # TODO: diff can be False (there is no difference?) |
|
67 | # TODO: diff can be False (there is no difference?) | |
67 | decls, upgradeCommands, downgradeCommands = \ |
|
68 | decls, upgradeCommands, downgradeCommands = \ | |
68 |
genmodel.ModelGenerator(diff, |
|
69 | genmodel.ModelGenerator(diff,engine).toUpgradeDowngradePython() | |
69 |
|
70 | |||
70 | # Store differences into file. |
|
71 | # Store differences into file. | |
71 | src = Template(opts.pop('templates_path', None)).get_script(opts.pop('templates_theme', None)) |
|
72 | src = Template(opts.pop('templates_path', None)).get_script(opts.pop('templates_theme', None)) | |
@@ -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 | |
140 | script_func(engine) |
|
141 | if not inspect.getargspec(script_func)[0]: | |
141 | except TypeError: |
|
142 | raise TypeError("upgrade/downgrade functions must accept engine" | |
142 | warnings.warn("upgrade/downgrade functions must accept engine" |
|
143 | " parameter (since version 0.5.4)") | |
143 | " parameter (since version > 0.5.4)", MigrateDeprecationWarning) |
|
144 | ||
144 | raise |
|
145 | script_func(engine) | |
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", |
@@ -80,7 +80,7 b' class Template(pathed.Pathed):' | |||||
80 | def get_repository(self, *a, **kw): |
|
80 | def get_repository(self, *a, **kw): | |
81 | """Calls self._get_item('repository', *a, **kw)""" |
|
81 | """Calls self._get_item('repository', *a, **kw)""" | |
82 | return self._get_item('repository', *a, **kw) |
|
82 | return self._get_item('repository', *a, **kw) | |
83 |
|
83 | |||
84 | def get_script(self, *a, **kw): |
|
84 | def get_script(self, *a, **kw): | |
85 | """Calls self._get_item('script', *a, **kw)""" |
|
85 | """Calls self._get_item('script', *a, **kw)""" | |
86 | return self._get_item('script', *a, **kw) |
|
86 | return self._get_item('script', *a, **kw) |
General Comments 0
You need to be logged in to leave comments.
Login now