pgtoolkit/pgdiff.py
changeset 63 8c7f0a51ba50
parent 61 703bba757605
child 64 687e18e5ca93
equal deleted inserted replaced
62:af637235ca81 63:8c7f0a51ba50
    24 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    24 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    25 # THE SOFTWARE.
    25 # THE SOFTWARE.
    26 
    26 
    27 
    27 
    28 from pgtoolkit.highlight import *
    28 from pgtoolkit.highlight import *
       
    29 from pgtoolkit.colordiff import colordiff
       
    30 
       
    31 import re
       
    32 import difflib
    29 
    33 
    30 
    34 
    31 class PgDiffError(Exception):
    35 class PgDiffError(Exception):
    32     pass
    36     pass
    33 
    37 
   132         self.argument = argument
   136         self.argument = argument
   133         self.name = argument
   137         self.name = argument
   134 
   138 
   135 
   139 
   136 class DiffFunction(DiffBase):
   140 class DiffFunction(DiffBase):
   137     def __init__(self, change, schema, function):
   141     def __init__(self, change, schema, function, show_body_diff=False):
   138         DiffBase.__init__(self)
   142         DiffBase.__init__(self)
   139         self.level = 1
   143         self.level = 1
   140         self.type = 'function'
   144         self.type = 'function'
   141         self.change = change
   145         self.change = change
   142         self.schema = schema
   146         self.schema = schema
   143         self.function = function
   147         self.function = function
   144         self.name = function
   148         self.name = function
       
   149         self.show_body_diff = show_body_diff
   145 
   150 
   146     def _formatchanges(self):
   151     def _formatchanges(self):
   147         res = []
   152         res = []
   148         for x in self.changes:
   153         for x in self.changes:
   149             type, a, b = x
   154             type, a, b = x
   150             if type == 'source':
   155             if type == 'source':
   151                 s = 'Changed source.'
   156                 if self.show_body_diff:
   152             else:
   157                     lines = ['Source differs:\n']
   153                 s = ''.join(['Changed ', type, ' from ',
   158                     for line in difflib.unified_diff(a, b, lineterm=''):
       
   159                         if line[:3] in ('---', '+++'):
       
   160                             continue
       
   161                         lines.append(line + '\n')
       
   162                     diff = ''.join(lines)
       
   163                     diff = colordiff(diff)
       
   164                     res.append(diff)
       
   165                 else:
       
   166                     res.append('Source differs.')
       
   167             else:
       
   168                 res.append(''.join(['Changed ', type, ' from ',
   154                     highlight(1,15), a, highlight(0), ' to ',
   169                     highlight(1,15), a, highlight(0), ' to ',
   155                     highlight(1,15), b, highlight(0), '.'])
   170                     highlight(1,15), b, highlight(0), '.']))
   156             res.append(s)
       
   157         return ' '.join(res)
   171         return ' '.join(res)
   158 
   172 
   159 
   173 
   160 class DiffColumn(DiffBase):
   174 class DiffColumn(DiffBase):
   161     ALTER_COMMANDS = {
   175     ALTER_COMMANDS = {
   247         self.dst = dstbrowser
   261         self.dst = dstbrowser
   248         self.include_schemas = set()  # if not empty, consider only these schemas for diff
   262         self.include_schemas = set()  # if not empty, consider only these schemas for diff
   249         self.exclude_schemas = set()  # exclude these schemas from diff
   263         self.exclude_schemas = set()  # exclude these schemas from diff
   250         self.include_tables = set()
   264         self.include_tables = set()
   251         self.exclude_tables = set()
   265         self.exclude_tables = set()
       
   266         self.function_regex = re.compile(r"")
       
   267         self.function_body_diff = False
   252 
   268 
   253     def _test_schema(self, schema):
   269     def _test_schema(self, schema):
   254         if self.include_schemas and schema not in self.include_schemas:
   270         if self.include_schemas and schema not in self.include_schemas:
   255             return False
   271             return False
   256         if schema in self.exclude_schemas:
   272         if schema in self.exclude_schemas:
   261         if self.include_tables and table not in self.include_tables:
   277         if self.include_tables and table not in self.include_tables:
   262             return False
   278             return False
   263         if table in self.exclude_tables:
   279         if table in self.exclude_tables:
   264             return False
   280             return False
   265         return True
   281         return True
       
   282 
       
   283     def _test_function(self, function):
       
   284         return bool(self.function_regex.match(function))
   266 
   285 
   267     def _diff_names(self, src, dst):
   286     def _diff_names(self, src, dst):
   268         for x in src:
   287         for x in src:
   269             if x in dst:
   288             if x in dst:
   270                 yield ('*', x)
   289                 yield ('*', x)
   294 
   313 
   295     def _compare_functions(self, a, b):
   314     def _compare_functions(self, a, b):
   296         diff = []
   315         diff = []
   297         if a.result != b.result:
   316         if a.result != b.result:
   298             diff.append(('result', a.result, b.result))
   317             diff.append(('result', a.result, b.result))
   299         if a.source != b.source:
   318         # function source may differ in newlines (\n vs \r\n)
   300             diff.append(('source', a.source, b.source))
   319         # split lines before comparison, so that these differencies are ignored
       
   320         a_source = a.source.splitlines()
       
   321         b_source = b.source.splitlines()
       
   322         if a_source != b_source:
       
   323             diff.append(('source', a_source, b_source))
   301         return diff
   324         return diff
   302 
   325 
   303     def _compare_arguments(self, a, b):
   326     def _compare_arguments(self, a, b):
   304         diff = []
   327         diff = []
   305         if a.type != b.type:
   328         if a.type != b.type:
   385             else:
   408             else:
   386                 yield ado
   409                 yield ado
   387 
   410 
   388     def _diff_functions(self, schema, src_functions, dst_functions):
   411     def _diff_functions(self, schema, src_functions, dst_functions):
   389         for nd in self._diff_names(src_functions, dst_functions):
   412         for nd in self._diff_names(src_functions, dst_functions):
   390             fdo = DiffFunction(change=nd[0], schema=schema, function=nd[1])
   413             if not self._test_function(nd[1]):
       
   414                 continue
       
   415             fdo = DiffFunction(change=nd[0], schema=schema, function=nd[1], show_body_diff=self.function_body_diff)
   391             if nd[0] == '*':
   416             if nd[0] == '*':
   392                 # compare function body and result
   417                 # compare function body and result
   393                 a = src_functions[nd[1]]
   418                 a = src_functions[nd[1]]
   394                 b = dst_functions[nd[1]]
   419                 b = dst_functions[nd[1]]
   395                 fdo.changes = self._compare_functions(a, b)
   420                 fdo.changes = self._compare_functions(a, b)
   490         self.include_schemas.clear()
   515         self.include_schemas.clear()
   491         self.include_schemas.update(include)
   516         self.include_schemas.update(include)
   492         self.exclude_schemas.clear()
   517         self.exclude_schemas.clear()
   493         self.exclude_schemas.update(exclude)
   518         self.exclude_schemas.update(exclude)
   494 
   519 
   495 
       
   496     def filter_tables(self, include=[], exclude=[]):
   520     def filter_tables(self, include=[], exclude=[]):
   497         self.include_tables.clear()
   521         self.include_tables.clear()
   498         self.include_tables.update(include)
   522         self.include_tables.update(include)
   499         self.exclude_tables.clear()
   523         self.exclude_tables.clear()
   500         self.exclude_tables.update(exclude)
   524         self.exclude_tables.update(exclude)
   501 
   525 
       
   526     def filter_functions(self, regex=''):
       
   527         self.function_regex = re.compile(regex)
   502 
   528 
   503     def _check_schema_exist(self, schema):
   529     def _check_schema_exist(self, schema):
   504         if not schema in self.src.schemas:
   530         if not schema in self.src.schemas:
   505             raise PgDiffError('Schema "%s" not found in source database.' % schema)
   531             raise PgDiffError('Schema "%s" not found in source database.' % schema)
   506 
   532