44 out.append(self.change) |
51 out.append(self.change) |
45 |
52 |
46 out += [' ', self.type, ' ', self.name, highlight(0)] |
53 out += [' ', self.type, ' ', self.name, highlight(0)] |
47 |
54 |
48 if self.changes: |
55 if self.changes: |
49 out += [highlight(1, WHITE), ' (', self.formatchanges(), ')', highlight(0)] |
56 out += [highlight(1, WHITE), ' (', self._formatchanges(), ')', highlight(0)] |
50 |
57 |
51 return ''.join(out) |
58 return ''.join(out) |
52 |
59 |
53 def formatnotnull(self, notnull): |
60 def _formatnotnull(self, notnull): |
54 if notnull: |
61 if notnull: |
55 return 'NOT NULL' |
62 return 'NOT NULL' |
56 else: |
63 else: |
57 return None |
64 return None |
58 |
65 |
59 def formatchanges(self): |
66 def _formatchanges(self): |
60 res = [] |
67 res = [] |
61 for x in self.changes: |
68 for x in self.changes: |
62 type, a, b = x |
69 type, a, b = x |
63 if type == 'notnull': |
70 if type == 'notnull': |
64 type = '' |
71 type = '' |
65 a = self.formatnotnull(a) |
72 a = self._formatnotnull(a) |
66 b = self.formatnotnull(b) |
73 b = self._formatnotnull(b) |
67 |
74 |
68 if a and b: |
75 if a and b: |
69 s = ''.join(['Changed ', type, ' from ', |
76 s = ''.join(['Changed ', type, ' from ', |
70 highlight(1,15), a, highlight(0), ' to ', |
77 highlight(1,15), a, highlight(0), ' to ', |
71 highlight(1,15), b, highlight(0), '.']) |
78 highlight(1,15), b, highlight(0), '.']) |
81 l += [type, ' '] |
88 l += [type, ' '] |
82 l += [highlight(1,15), b, highlight(0), '.'] |
89 l += [highlight(1,15), b, highlight(0), '.'] |
83 s = ''.join(l) |
90 s = ''.join(l) |
84 res.append(s) |
91 res.append(s) |
85 return ' '.join(res) |
92 return ' '.join(res) |
|
93 |
|
94 def format_patch(self): |
|
95 if self.change == '*' and self.type in ('schema', 'table'): |
|
96 return None |
|
97 return '%s %s %s;' % (self.COMMANDS[self.change], self.type.upper(), self.name) |
86 |
98 |
87 |
99 |
88 class DiffSchema(DiffBase): |
100 class DiffSchema(DiffBase): |
89 def __init__(self, change, schema): |
101 def __init__(self, change, schema): |
90 DiffBase.__init__(self) |
102 DiffBase.__init__(self) |
91 self.level = 0 |
103 self.level = 0 |
92 self.type = 'schema' |
104 self.type = 'schema' |
93 self.change = change |
105 self.change = change |
94 self.schema = schema |
106 self.schema = schema |
95 self.name = schema |
107 self.name = schema |
96 |
108 |
97 |
109 |
98 class DiffTable(DiffBase): |
110 class DiffTable(DiffBase): |
99 def __init__(self, change, schema, table): |
111 def __init__(self, change, schema, table): |
100 DiffBase.__init__(self) |
112 DiffBase.__init__(self) |
101 self.level = 1 |
113 self.level = 1 |
105 self.table = table |
117 self.table = table |
106 self.name = table |
118 self.name = table |
107 |
119 |
108 |
120 |
109 class DiffColumn(DiffBase): |
121 class DiffColumn(DiffBase): |
110 def __init__(self, change, schema, table, column, changes=None): |
122 ALTER_COMMANDS = { |
|
123 '+' : 'ADD', |
|
124 '-' : 'DROP', |
|
125 '*' : 'ALTER', |
|
126 } |
|
127 |
|
128 def __init__(self, change, schema, table, column, columntype, columndefault, changes=None): |
111 DiffBase.__init__(self) |
129 DiffBase.__init__(self) |
112 self.level = 2 |
130 self.level = 2 |
113 self.type = 'column' |
131 self.type = 'column' |
114 self.change = change |
132 self.change = change |
115 self.schema = schema |
133 self.schema = schema |
116 self.table = table |
134 self.table = table |
117 self.column = column |
135 self.column = column |
|
136 self.columntype = columntype |
|
137 self.columndefault = columndefault |
118 self.name = column |
138 self.name = column |
119 self.changes = changes |
139 self.changes = changes |
|
140 |
|
141 def format_patch(self): |
|
142 out = 'ALTER TABLE %s.%s %s COLUMN %s %s' % ( |
|
143 self.schema, |
|
144 self.table, |
|
145 self.ALTER_COMMANDS[self.change], |
|
146 self.name, |
|
147 self.columntype |
|
148 ) |
|
149 if self.columndefault: |
|
150 out += ' DEFAULT ' + self.columndefault |
|
151 out += ';' |
|
152 return out |
120 |
153 |
121 |
154 |
122 class DiffConstraint(DiffBase): |
155 class DiffConstraint(DiffBase): |
123 def __init__(self, change, schema, table, constraint, changes=None): |
156 def __init__(self, change, schema, table, constraint, changes=None): |
124 DiffBase.__init__(self) |
157 DiffBase.__init__(self) |
140 self.include_schemas = set() # if not empty, consider only these schemas for diff |
173 self.include_schemas = set() # if not empty, consider only these schemas for diff |
141 self.exclude_schemas = set() # exclude these schemas from diff |
174 self.exclude_schemas = set() # exclude these schemas from diff |
142 self.include_tables = set() |
175 self.include_tables = set() |
143 self.exclude_tables = set() |
176 self.exclude_tables = set() |
144 |
177 |
145 def _test_filter(self, schema): |
178 def _test_schema(self, schema): |
146 if self.include_schemas and schema not in self.include_schemas: |
179 if self.include_schemas and schema not in self.include_schemas: |
147 return False |
180 return False |
148 if schema in self.exclude_schemas: |
181 if schema in self.exclude_schemas: |
149 return False |
182 return False |
150 return True |
183 return True |
151 |
184 |
152 def _test_table(self, schema, table): |
185 def _test_table(self, table): |
153 name = schema + '.' + table |
186 if self.include_tables and table not in self.include_tables: |
154 if self.include_tables and name not in self.include_tables: |
|
155 return False |
187 return False |
156 if name in self.exclude_tables: |
188 if table in self.exclude_tables: |
157 return False |
189 return False |
158 return True |
190 return True |
159 |
191 |
160 def _diffnames(self, src, dst): |
192 def _diff_names(self, src, dst): |
161 for x in src: |
193 for x in src: |
162 if x in dst: |
194 if x in dst: |
163 yield ('*', x) |
195 yield ('*', x) |
164 else: |
196 else: |
165 yield ('-', x) |
197 yield ('-', x) |
184 if a.definition != b.definition: |
216 if a.definition != b.definition: |
185 diff.append(('definition', a.definition, b.definition)) |
217 diff.append(('definition', a.definition, b.definition)) |
186 return diff |
218 return diff |
187 |
219 |
188 def _diff_columns(self, schema, table, src_columns, dst_columns): |
220 def _diff_columns(self, schema, table, src_columns, dst_columns): |
189 for nd in self._diffnames(src_columns, dst_columns): |
221 for nd in self._diff_names(src_columns, dst_columns): |
190 cdo = DiffColumn(change=nd[0], schema=schema, table=table, column=nd[1]) |
222 cdo = DiffColumn(change=nd[0], schema=schema, table=table, column=nd[1], |
|
223 columntype=dst_columns[nd[1]].type, columndefault=dst_columns[nd[1]].default) |
191 if nd[0] == '*': |
224 if nd[0] == '*': |
192 a = src_columns[nd[1]] |
225 a = src_columns[nd[1]] |
193 b = dst_columns[nd[1]] |
226 b = dst_columns[nd[1]] |
194 cdo.changes = self._compare_columns(a, b) |
227 cdo.changes = self._compare_columns(a, b) |
195 if cdo.changes: |
228 if cdo.changes: |
196 yield cdo |
229 yield cdo |
197 else: |
230 else: |
198 yield cdo |
231 yield cdo |
199 |
232 |
200 def _diff_constraints(self, schema, table, src_constraints, dst_constraints): |
233 def _diff_constraints(self, schema, table, src_constraints, dst_constraints): |
201 for nd in self._diffnames(src_constraints, dst_constraints): |
234 for nd in self._diff_names(src_constraints, dst_constraints): |
202 cdo = DiffConstraint(change=nd[0], schema=schema, table=table, constraint=nd[1]) |
235 cdo = DiffConstraint(change=nd[0], schema=schema, table=table, constraint=nd[1]) |
203 if nd[0] == '*': |
236 if nd[0] == '*': |
204 a = src_constraints[nd[1]] |
237 a = src_constraints[nd[1]] |
205 b = dst_constraints[nd[1]] |
238 b = dst_constraints[nd[1]] |
206 cdo.changes = self._compare_constraints(a, b) |
239 cdo.changes = self._compare_constraints(a, b) |
208 yield cdo |
241 yield cdo |
209 else: |
242 else: |
210 yield cdo |
243 yield cdo |
211 |
244 |
212 def _difftables(self, schema, src_tables, dst_tables): |
245 def _difftables(self, schema, src_tables, dst_tables): |
213 for nd in self._diffnames(src_tables, dst_tables): |
246 for nd in self._diff_names(src_tables, dst_tables): |
214 if not self._test_table(schema, nd[1]): |
247 if not self._test_table(nd[1]): |
215 continue |
248 continue |
216 tdo = DiffTable(change=nd[0], schema=schema, table=nd[1]) |
249 tdo = DiffTable(change=nd[0], schema=schema, table=nd[1]) |
217 if nd[0] == '*': |
250 if nd[0] == '*': |
218 # columns |
251 # columns |
219 src_columns = src_tables[nd[1]].columns |
252 src_columns = src_tables[nd[1]].columns |
234 else: |
267 else: |
235 yield tdo |
268 yield tdo |
236 |
269 |
237 def iter_diff(self): |
270 def iter_diff(self): |
238 '''Return diff between src and dst database schema. |
271 '''Return diff between src and dst database schema. |
239 |
272 |
240 Yields one line at the time. Each line is in form of object |
273 Yields one line at the time. Each line is in form of object |
241 iherited from DiffBase. This object contains all information |
274 iherited from DiffBase. This object contains all information |
242 about changes. See format() method. |
275 about changes. See format() method. |
243 |
276 |
244 ''' |
277 ''' |
245 src_schemas = self.src.schemas |
278 src_schemas = self.src.schemas |
246 dst_schemas = self.dst.schemas |
279 dst_schemas = self.dst.schemas |
247 src = [x.name for x in src_schemas.values() if not x.system and self._test_filter(x.name)] |
280 src = [x.name for x in src_schemas.values() if not x.system and self._test_schema(x.name)] |
248 dst = [x.name for x in dst_schemas.values() if not x.system and self._test_filter(x.name)] |
281 dst = [x.name for x in dst_schemas.values() if not x.system and self._test_schema(x.name)] |
249 for nd in self._diffnames(src, dst): |
282 for nd in self._diff_names(src, dst): |
250 sdo = DiffSchema(change=nd[0], schema=nd[1]) |
283 sdo = DiffSchema(change=nd[0], schema=nd[1]) |
251 if nd[0] == '*': |
284 if nd[0] == '*': |
252 src_tables = src_schemas[nd[1]].tables |
285 src_tables = src_schemas[nd[1]].tables |
253 dst_tables = dst_schemas[nd[1]].tables |
286 dst_tables = dst_schemas[nd[1]].tables |
254 for tdo in self._difftables(nd[1], src_tables, dst_tables): |
287 for tdo in self._difftables(nd[1], src_tables, dst_tables): |
284 Even if it works as intended, it can cause table lock ups |
317 Even if it works as intended, it can cause table lock ups |
285 and/or loss of data. You have been warned. |
318 and/or loss of data. You have been warned. |
286 |
319 |
287 ''' |
320 ''' |
288 for ln in self.iter_diff(): |
321 for ln in self.iter_diff(): |
289 print(ln.format_patch()) |
322 patch = ln.format_patch() |
|
323 if patch: |
|
324 print(patch) |
290 |
325 |
291 def filter_schemas(self, include=[], exclude=[]): |
326 def filter_schemas(self, include=[], exclude=[]): |
292 '''Modify list of schemas which are used for computing diff. |
327 '''Modify list of schemas which are used for computing diff. |
293 |
328 |
294 include (list) -- if not empty, consider only these schemas for diff |
329 include (list) -- if not empty, consider only these schemas for diff |