pgtoolkit/pgdiff.py
changeset 104 d8ff52a0390f
parent 103 24e94a3da209
child 105 10551741f61f
equal deleted inserted replaced
103:24e94a3da209 104:d8ff52a0390f
     1 # -*- coding: utf-8 -*-
       
     2 #
       
     3 # PgDiff - capture differences of database metadata
       
     4 #
       
     5 # Depends on PgBrowser
       
     6 #
       
     7 # Copyright (c) 2011  Radek Brich <radek.brich@devl.cz>
       
     8 #
       
     9 # Permission is hereby granted, free of charge, to any person obtaining a copy
       
    10 # of this software and associated documentation files (the "Software"), to deal
       
    11 # in the Software without restriction, including without limitation the rights
       
    12 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    13 # copies of the Software, and to permit persons to whom the Software is
       
    14 # furnished to do so, subject to the following conditions:
       
    15 #
       
    16 # The above copyright notice and this permission notice shall be included in
       
    17 # all copies or substantial portions of the Software.
       
    18 #
       
    19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    20 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    21 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       
    22 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    23 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    24 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    25 # THE SOFTWARE.
       
    26 
       
    27 
       
    28 from pycolib.ansicolor import *
       
    29 
       
    30 import re
       
    31 import difflib
       
    32 
       
    33 
       
    34 class PgDiffError(Exception):
       
    35     pass
       
    36 
       
    37 
       
    38 class DiffBase:
       
    39     COLORS = {
       
    40         '+' : BOLD | GREEN,
       
    41         '-' : BOLD | RED,
       
    42         '*' : BOLD | YELLOW,
       
    43     }
       
    44 
       
    45     COMMANDS = {
       
    46         '+' : 'CREATE',
       
    47         '-' : 'DROP',
       
    48         '*' : 'ALTER',
       
    49     }
       
    50 
       
    51     def __init__(self):
       
    52         self.changes = None
       
    53 
       
    54     def format(self):
       
    55         out = ['  ' * self.level]
       
    56 
       
    57         out.append(highlight(1, self.COLORS[self.change]))
       
    58         out.append(self.change)
       
    59 
       
    60         out += [' ', self.type, ' ', self.name, highlight(0)]
       
    61 
       
    62         if self.changes:
       
    63             out += [highlight(1, WHITE), ' (', self._formatchanges(), ')', highlight(0)]
       
    64 
       
    65         return ''.join(out)
       
    66 
       
    67     def _formatnotnull(self, notnull):
       
    68         if notnull:
       
    69             return 'NOT NULL'
       
    70         else:
       
    71             return None
       
    72 
       
    73     def _formatchanges(self):
       
    74         res = []
       
    75         for type, a, b in self.changes:
       
    76             if type == 'notnull':
       
    77                 type = ''
       
    78                 a = self._formatnotnull(a)
       
    79                 b = self._formatnotnull(b)
       
    80 
       
    81             if a and b:
       
    82                 s = ''.join(['Changed ', type, ' from ',
       
    83                     highlight(1,15), a, highlight(0), ' to ',
       
    84                     highlight(1,15), b, highlight(0), '.'])
       
    85             elif a and not b:
       
    86                 l = ['Removed ']
       
    87                 if type:
       
    88                     l += [type, ' ']
       
    89                 l += [highlight(1,15), a, highlight(0), '.']
       
    90                 s = ''.join(l)
       
    91             elif b and not a:
       
    92                 l = ['Added ']
       
    93                 if type:
       
    94                     l += [type, ' ']
       
    95                 l += [highlight(1,15), b, highlight(0), '.']
       
    96                 s = ''.join(l)
       
    97             res.append(s)
       
    98         return ' '.join(res)
       
    99 
       
   100     def format_patch(self):
       
   101         if self.change == '*' and self.type in ('schema', 'table'):
       
   102             return None
       
   103         return ['%s %s %s;' % (self.COMMANDS[self.change], self.type.upper(), self.name)]
       
   104 
       
   105 
       
   106 class DiffSchema(DiffBase):
       
   107     def __init__(self, change, schema):
       
   108         DiffBase.__init__(self)
       
   109         self.level = 0
       
   110         self.type = 'schema'
       
   111         self.change = change
       
   112         self.schema = schema
       
   113         self.name = schema
       
   114 
       
   115 
       
   116 class DiffTable(DiffBase):
       
   117     def __init__(self, change, schema, table):
       
   118         DiffBase.__init__(self)
       
   119         self.level = 1
       
   120         self.type = 'table'
       
   121         self.change = change
       
   122         self.schema = schema
       
   123         self.table = table
       
   124         self.name = table
       
   125 
       
   126 
       
   127 class DiffArgument(DiffBase):
       
   128     def __init__(self, change, schema, function, argument):
       
   129         DiffBase.__init__(self)
       
   130         self.level = 2
       
   131         self.type = 'argument'
       
   132         self.change = change
       
   133         self.schema = schema
       
   134         self.function = function
       
   135         self.argument = argument
       
   136         self.name = argument
       
   137 
       
   138 
       
   139 class DiffFunction(DiffBase):
       
   140     def __init__(self, change, schema, function, definition, show_body_diff=False):
       
   141         DiffBase.__init__(self)
       
   142         self.level = 1
       
   143         self.type = 'function'
       
   144         self.change = change
       
   145         self.schema = schema
       
   146         self.function = function
       
   147         #: New function definition
       
   148         self.definition = definition
       
   149         self.name = function
       
   150         self.show_body_diff = show_body_diff
       
   151 
       
   152     def _formatchanges(self):
       
   153         res = []
       
   154         for x in self.changes:
       
   155             type, a, b = x
       
   156             if type == 'source':
       
   157                 if self.show_body_diff:
       
   158                     lines = ['Source differs:\n']
       
   159                     for line in difflib.unified_diff(a, b, lineterm=''):
       
   160                         if line[:3] in ('---', '+++'):
       
   161                             continue
       
   162                         color = {' ': WHITE, '-': YELLOW, '+': GREEN, '@': WHITE|BOLD}[line[0]]
       
   163                         lines.append(highlight(1, color) + line + highlight(0) + '\n')
       
   164                     res.append(''.join(lines))
       
   165                 else:
       
   166                     res.append('Source differs.')
       
   167             else:
       
   168                 res.append(''.join(['Changed ', type, ' from ',
       
   169                     highlight(1,15), a, highlight(0), ' to ',
       
   170                     highlight(1,15), b, highlight(0), '.']))
       
   171         return ' '.join(res)
       
   172 
       
   173     def format_patch(self):
       
   174         return [self.definition]
       
   175 
       
   176 
       
   177 class DiffColumn(DiffBase):
       
   178     ALTER_COMMANDS = {
       
   179         '+' : 'ADD',
       
   180         '-' : 'DROP',
       
   181         '*' : 'ALTER',
       
   182     }
       
   183 
       
   184     def __init__(self, change, schema, table, column, columntype, columndefault, columnnotnull, changes=None):
       
   185         DiffBase.__init__(self)
       
   186         self.level = 2
       
   187         self.type = 'column'
       
   188         self.change = change
       
   189         self.schema = schema
       
   190         self.table = table
       
   191         self.column = column
       
   192         self.columntype = columntype
       
   193         self.columndefault = columndefault
       
   194         self.columnnotnull = columnnotnull
       
   195         self.name = column
       
   196         self.changes = changes
       
   197 
       
   198     def format_patch(self):
       
   199         alter_table = 'ALTER TABLE %s.%s %s COLUMN %s' % (
       
   200             self.schema,
       
   201             self.table,
       
   202             self.ALTER_COMMANDS[self.change],
       
   203             self.name,
       
   204         )
       
   205         out = []
       
   206         if self.change == '-':
       
   207             out.append('%s;' % alter_table);
       
   208         if self.change == '+':
       
   209             notnull = ''
       
   210             if self.columnnotnull:
       
   211                 notnull = ' NOT NULL'
       
   212             default = ''
       
   213             if self.columndefault:
       
   214                 default = ' DEFAULT %s' % self.columndefault
       
   215             out.append('%s %s%s%s;'
       
   216                 % (alter_table, self.columntype, notnull, default));
       
   217         if self.change == '*':
       
   218             for type, a, b in self.changes:
       
   219                 if type == 'type':
       
   220                     out.append('%s TYPE %s;' % (alter_table, b))
       
   221                 if type == 'notnull':
       
   222                     if a and not b:
       
   223                         out.append('%s DROP NOT NULL;' % alter_table)
       
   224                     if not a and b:
       
   225                         out.append('%s SET NOT NULL;' % alter_table)
       
   226                 if type == 'default':
       
   227                     if b:
       
   228                         out.append('%s SET DEFAULT %s;' % (alter_table, b))
       
   229                     else:
       
   230                         out.append('%s DROP DEFAULT;' % alter_table)
       
   231         return out
       
   232 
       
   233 
       
   234 class DiffConstraint(DiffBase):
       
   235     def __init__(self, change, schema, table, constraint, definition, changes=None):
       
   236         DiffBase.__init__(self)
       
   237         self.level = 2
       
   238         self.type = 'constraint'
       
   239         self.change = change
       
   240         self.schema = schema
       
   241         self.table = table
       
   242         self.constraint = constraint
       
   243         self.name = constraint
       
   244         self.definition = definition
       
   245         self.changes = changes
       
   246 
       
   247     def format_patch(self):
       
   248         q_alter = 'ALTER TABLE %s.%s' % (self.schema, self.table)
       
   249         q_drop = '%s DROP CONSTRAINT %s;' % (q_alter, self.constraint)
       
   250         q_add = '%s ADD CONSTRAINT %s %s;' % (q_alter, self.constraint, self.definition)
       
   251         if self.change == '*':
       
   252             out = [q_drop, q_add]
       
   253         if self.change == '+':
       
   254             out = [q_add]
       
   255         if self.change == '-':
       
   256             out = [q_drop]
       
   257         return out
       
   258 
       
   259 
       
   260 class DiffIndex(DiffBase):
       
   261     def __init__(self, change, schema, table, index, definition, changes=None):
       
   262         DiffBase.__init__(self)
       
   263         self.level = 2
       
   264         self.type = 'index'
       
   265         self.change = change
       
   266         self.schema = schema
       
   267         self.table = table
       
   268         self.index = index
       
   269         self.name = index
       
   270         self.definition = definition
       
   271         self.changes = changes
       
   272 
       
   273     def format_patch(self):
       
   274         q_drop = 'DROP INDEX %s;' % (self.index,)
       
   275         q_add = '%s;' % (self.definition,)
       
   276         if self.change == '*':
       
   277             out = [q_drop, q_add]
       
   278         if self.change == '+':
       
   279             out = [q_add]
       
   280         if self.change == '-':
       
   281             out = [q_drop]
       
   282         return out
       
   283 
       
   284 
       
   285 class DiffType(DiffBase):
       
   286     def __init__(self, change, schema, name):
       
   287         DiffBase.__init__(self)
       
   288         self.level = 1
       
   289         self.type = 'type'
       
   290         self.change = change
       
   291         self.schema = schema
       
   292         self.name = name
       
   293 
       
   294 
       
   295 class PgDiff:
       
   296     def __init__(self, srcbrowser=None, dstbrowser=None):
       
   297         self.allowcolor = False
       
   298         self.src = srcbrowser
       
   299         self.dst = dstbrowser
       
   300         self.include_schemas = set()  # if not empty, consider only these schemas for diff
       
   301         self.exclude_schemas = set()  # exclude these schemas from diff
       
   302         self.include_tables = set()
       
   303         self.exclude_tables = set()
       
   304         self.function_regex = re.compile(r"")
       
   305         self.function_body_diff = False
       
   306 
       
   307     def _test_schema(self, schema):
       
   308         if self.include_schemas and schema not in self.include_schemas:
       
   309             return False
       
   310         if schema in self.exclude_schemas:
       
   311             return False
       
   312         return True
       
   313 
       
   314     def _test_table(self, table):
       
   315         if self.include_tables and table not in self.include_tables:
       
   316             return False
       
   317         if table in self.exclude_tables:
       
   318             return False
       
   319         return True
       
   320 
       
   321     def _test_function(self, function):
       
   322         return bool(self.function_regex.match(function))
       
   323 
       
   324     def _diff_names(self, src, dst):
       
   325         for x in src:
       
   326             if x in dst:
       
   327                 yield ('*', x)
       
   328             else:
       
   329                 yield ('-', x)
       
   330         for x in dst:
       
   331             if x not in src:
       
   332                 yield ('+', x)
       
   333 
       
   334     def _compare_columns(self, a, b):
       
   335         diff = []
       
   336         if a.type != b.type:
       
   337             diff.append(('type', a.type, b.type))
       
   338         if a.notnull != b.notnull:
       
   339             diff.append(('notnull', a.notnull, b.notnull))
       
   340         if a.default != b.default:
       
   341             diff.append(('default', a.default, b.default))
       
   342         return diff
       
   343 
       
   344     def _compare_constraints(self, a, b):
       
   345         diff = []
       
   346         if a.type != b.type:
       
   347             diff.append(('type', a.type, b.type))
       
   348         if a.definition != b.definition:
       
   349             diff.append(('definition', a.definition, b.definition))
       
   350         return diff
       
   351 
       
   352     def _compare_indexes(self, a, b):
       
   353         diff = []
       
   354         if a.definition != b.definition:
       
   355             diff.append(('definition', a.definition, b.definition))
       
   356         return diff
       
   357 
       
   358     def _compare_functions(self, a, b):
       
   359         diff = []
       
   360         if a.result != b.result:
       
   361             diff.append(('result', a.result, b.result))
       
   362         # function source may differ in newlines (\n vs \r\n)
       
   363         # split lines before comparison, so that these differencies are ignored
       
   364         a_source = a.source.splitlines()
       
   365         b_source = b.source.splitlines()
       
   366         if a_source != b_source:
       
   367             diff.append(('source', a_source, b_source))
       
   368         return diff
       
   369 
       
   370     def _compare_arguments(self, a, b):
       
   371         diff = []
       
   372         if a.type != b.type:
       
   373             diff.append(('type', a.type, b.type))
       
   374         if a.mode != b.mode:
       
   375             diff.append(('mode', a.mode, b.mode))
       
   376         if a.default != b.default:
       
   377             diff.append(('default', a.default, b.default))
       
   378         return diff
       
   379 
       
   380     def _compare_types(self, a, b):
       
   381         diff = []
       
   382         if a.type != b.type:
       
   383             diff.append(('type', a.type, b.type))
       
   384         if a.elements != b.elements:
       
   385             diff.append(('elements', repr(a.elements), repr(b.elements)))
       
   386         return diff
       
   387 
       
   388     def _diff_columns(self, schema, table, src_columns, dst_columns):
       
   389         for nd in self._diff_names(src_columns, dst_columns):
       
   390             if nd[1] in dst_columns:
       
   391                 dst_type = dst_columns[nd[1]].type
       
   392                 dst_default = dst_columns[nd[1]].default
       
   393                 dst_notnull = dst_columns[nd[1]].notnull
       
   394             else:
       
   395                 dst_type = None
       
   396                 dst_default = None
       
   397                 dst_notnull = None
       
   398             cdo = DiffColumn(change=nd[0], schema=schema, table=table, column=nd[1],
       
   399                 columntype=dst_type, columndefault=dst_default, columnnotnull=dst_notnull)
       
   400             if nd[0] == '*':
       
   401                 a = src_columns[nd[1]]
       
   402                 b = dst_columns[nd[1]]
       
   403                 cdo.changes = self._compare_columns(a, b)
       
   404                 if cdo.changes:
       
   405                     yield cdo
       
   406             else:
       
   407                 yield cdo
       
   408 
       
   409     def _diff_constraints(self, schema, table, src_constraints, dst_constraints):
       
   410         for nd in self._diff_names(src_constraints, dst_constraints):
       
   411             if nd[1] in dst_constraints:
       
   412                 dst_definition = dst_constraints[nd[1]].definition
       
   413             else:
       
   414                 dst_definition = None
       
   415             cdo = DiffConstraint(change=nd[0], schema=schema, table=table, constraint=nd[1],
       
   416                 definition=dst_definition)
       
   417             if nd[0] == '*':
       
   418                 a = src_constraints[nd[1]]
       
   419                 b = dst_constraints[nd[1]]
       
   420                 cdo.changes = self._compare_constraints(a, b)
       
   421                 if cdo.changes:
       
   422                     yield cdo
       
   423             else:
       
   424                 yield cdo
       
   425 
       
   426     def _diff_indexes(self, schema, table, src_indexes, dst_indexes):
       
   427         for nd in self._diff_names(src_indexes, dst_indexes):
       
   428             if nd[1] in dst_indexes:
       
   429                 dst_definition = dst_indexes[nd[1]].definition
       
   430             else:
       
   431                 dst_definition = None
       
   432             ido = DiffIndex(change=nd[0], schema=schema, table=table, index=nd[1],
       
   433                 definition=dst_definition)
       
   434             if nd[0] == '*':
       
   435                 a = src_indexes[nd[1]]
       
   436                 b = dst_indexes[nd[1]]
       
   437                 ido.changes = self._compare_indexes(a, b)
       
   438                 if ido.changes:
       
   439                     yield ido
       
   440             else:
       
   441                 yield ido
       
   442 
       
   443     def _diff_tables(self, schema, src_tables, dst_tables):
       
   444         for nd in self._diff_names(src_tables, dst_tables):
       
   445             if not self._test_table(nd[1]):
       
   446                 continue
       
   447             tdo = DiffTable(change=nd[0], schema=schema, table=nd[1])
       
   448             if nd[0] == '*':
       
   449                 # columns
       
   450                 src_columns = src_tables[nd[1]].columns
       
   451                 dst_columns = dst_tables[nd[1]].columns
       
   452                 for cdo in self._diff_columns(schema, nd[1], src_columns, dst_columns):
       
   453                     if tdo:
       
   454                         yield tdo
       
   455                         tdo = None
       
   456                     yield cdo
       
   457                 # constraints
       
   458                 src_constraints = src_tables[nd[1]].constraints
       
   459                 dst_constraints = dst_tables[nd[1]].constraints
       
   460                 for cdo in self._diff_constraints(schema, nd[1], src_constraints, dst_constraints):
       
   461                     if tdo:
       
   462                         yield tdo
       
   463                         tdo = None
       
   464                     yield cdo
       
   465                 # indexes
       
   466                 src_indexes = src_tables[nd[1]].indexes
       
   467                 dst_indexes = dst_tables[nd[1]].indexes
       
   468                 for ido in self._diff_indexes(schema, nd[1], src_indexes, dst_indexes):
       
   469                     if tdo:
       
   470                         yield tdo
       
   471                         tdo = None
       
   472                     yield ido
       
   473             else:
       
   474                 yield tdo
       
   475 
       
   476     def _diff_arguments(self, schema, function, src_args, dst_args):
       
   477         for nd in self._diff_names(src_args, dst_args):
       
   478             ado = DiffArgument(change=nd[0], schema=schema, function=function, argument=nd[1])
       
   479             if nd[0] == '*':
       
   480                 a = src_args[nd[1]]
       
   481                 b = dst_args[nd[1]]
       
   482                 ado.changes = self._compare_arguments(a, b)
       
   483                 if ado.changes:
       
   484                     yield ado
       
   485             else:
       
   486                 yield ado
       
   487 
       
   488     def _diff_functions(self, schema, src_functions, dst_functions):
       
   489         for nd in self._diff_names(src_functions, dst_functions):
       
   490             if not self._test_function(nd[1]):
       
   491                 continue
       
   492             if nd[1] in dst_functions:
       
   493                 dst_definition = dst_functions[nd[1]].definition
       
   494             else:
       
   495                 dst_definition = None
       
   496             fdo = DiffFunction(change=nd[0], schema=schema, function=nd[1],
       
   497                                definition=dst_definition,
       
   498                                show_body_diff=self.function_body_diff)
       
   499             if nd[0] == '*':
       
   500                 # compare function body and result
       
   501                 a = src_functions[nd[1]]
       
   502                 b = dst_functions[nd[1]]
       
   503                 fdo.changes = self._compare_functions(a, b)
       
   504                 if fdo.changes:
       
   505                     yield fdo
       
   506                     fdo = None
       
   507                 # arguments
       
   508                 src_args = src_functions[nd[1]].arguments
       
   509                 dst_args = dst_functions[nd[1]].arguments
       
   510                 for ado in self._diff_arguments(schema, nd[1], src_args, dst_args):
       
   511                     if fdo:
       
   512                         yield fdo
       
   513                         fdo = None
       
   514                     yield ado
       
   515             else:
       
   516                 yield fdo
       
   517 
       
   518     def _diff_types(self, schema, src_types, dst_types):
       
   519         for nd in self._diff_names(src_types, dst_types):
       
   520             tdo = DiffType(change=nd[0], schema=schema, name=nd[1])
       
   521             if nd[0] == '*':
       
   522                 a = src_types[nd[1]]
       
   523                 b = dst_types[nd[1]]
       
   524                 tdo.changes = self._compare_types(a, b)
       
   525                 if tdo.changes:
       
   526                     yield tdo
       
   527             else:
       
   528                 yield tdo
       
   529 
       
   530     def iter_diff(self):
       
   531         '''Return diff between src and dst database schema.
       
   532 
       
   533         Yields one line at the time. Each line is in form of object
       
   534         iherited from DiffBase. This object contains all information
       
   535         about changes. See format() method.
       
   536 
       
   537         '''
       
   538         src_schemas = self.src.schemas
       
   539         dst_schemas = self.dst.schemas
       
   540         src = [x.name for x in src_schemas.values() if not x.system and self._test_schema(x.name)]
       
   541         dst = [x.name for x in dst_schemas.values() if not x.system and self._test_schema(x.name)]
       
   542         for nd in self._diff_names(src, dst):
       
   543             sdo = DiffSchema(change=nd[0], schema=nd[1])
       
   544             if nd[0] == '*':
       
   545                 # tables
       
   546                 src_tables = src_schemas[nd[1]].tables
       
   547                 dst_tables = dst_schemas[nd[1]].tables
       
   548                 for tdo in self._diff_tables(nd[1], src_tables, dst_tables):
       
   549                     if sdo:
       
   550                         yield sdo
       
   551                         sdo = None
       
   552                     yield tdo
       
   553                 # functions
       
   554                 src_functions = src_schemas[nd[1]].functions
       
   555                 dst_functions = dst_schemas[nd[1]].functions
       
   556                 for fdo in self._diff_functions(nd[1], src_functions, dst_functions):
       
   557                     if sdo:
       
   558                         yield sdo
       
   559                         sdo = None
       
   560                     yield fdo
       
   561                 # types
       
   562                 src_types = src_schemas[nd[1]].types
       
   563                 dst_types = dst_schemas[nd[1]].types
       
   564                 for tdo in self._diff_types(nd[1], src_types, dst_types):
       
   565                     if sdo:
       
   566                         yield sdo
       
   567                         sdo = None
       
   568                     yield tdo
       
   569             else:
       
   570                 yield sdo
       
   571 
       
   572     def print_diff(self):
       
   573         '''Print diff between src and dst database schema.
       
   574 
       
   575         The output is in human readable form.
       
   576 
       
   577         Set allowcolor=True of PgDiff instance to get colored output.
       
   578 
       
   579         '''
       
   580         for ln in self.iter_diff():
       
   581             print(ln.format())
       
   582 
       
   583     def print_patch(self):
       
   584         '''Print patch for updating from src schema to dst schema.
       
   585 
       
   586         Supports table drop, add, column drop, add and following
       
   587         changes of columns:
       
   588           - type
       
   589           - set/remove not null
       
   590           - default value
       
   591 
       
   592         This is experimental, not tested very much.
       
   593         Do not use without checking the commands.
       
   594         Even if it works as intended, it can cause table lock ups
       
   595         and/or loss of data. You have been warned.
       
   596 
       
   597         '''
       
   598         for ln in self.iter_diff():
       
   599             patch = ln.format_patch()
       
   600             if patch:
       
   601                 print('\n'.join(patch))
       
   602 
       
   603     def filter_schemas(self, include=[], exclude=[]):
       
   604         '''Modify list of schemas which are used for computing diff.
       
   605 
       
   606         include (list) -- if not empty, consider only these schemas for diff
       
   607         exclude (list) -- exclude these schemas from diff
       
   608 
       
   609         Order: include, exclude
       
   610         include=[] means include everything
       
   611 
       
   612         Raises:
       
   613             PgDiffError: when schema from include list is not found in src db
       
   614 
       
   615         '''
       
   616         for schema in include:
       
   617             self._check_schema_exist(schema)
       
   618         self.include_schemas.clear()
       
   619         self.include_schemas.update(include)
       
   620         self.exclude_schemas.clear()
       
   621         self.exclude_schemas.update(exclude)
       
   622 
       
   623     def filter_tables(self, include=[], exclude=[]):
       
   624         self.include_tables.clear()
       
   625         self.include_tables.update(include)
       
   626         self.exclude_tables.clear()
       
   627         self.exclude_tables.update(exclude)
       
   628 
       
   629     def filter_functions(self, regex=''):
       
   630         self.function_regex = re.compile(regex)
       
   631 
       
   632     def _check_schema_exist(self, schema):
       
   633         if not schema in self.src.schemas:
       
   634             raise PgDiffError('Schema "%s" not found in source database.' % schema)
       
   635