# HG changeset patch # User Radek Brich # Date 1399394261 -7200 # Node ID d6088dba8fea81edb7add7a5caedefa19fb9df5f # Parent 245646538743df803e7925cc64c87b4e02fe9690 Add pgtool wrapper for all tools. Only this script will be installed into system bin. diff -r 245646538743 -r d6088dba8fea pgtool --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pgtool Tue May 06 18:37:41 2014 +0200 @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +""" +Wrapper script for pgtoolkit tools. + +Usage: + + pgtool --list + List all available tools. + + pgtool ... + Run tool. All args except first one are forwarded to the tool. + + pgtool schemadiff db1 db2 + E.g. run schemadiff between db1 and db2. + + pgtool schemadiff --help + Get help for tool parameters. + +""" + +import pgtoolkit.tools +import sys +from importlib import import_module + + +if len(sys.argv) < 2: + print(__doc__, end='') + sys.exit() + +if sys.argv[1] == '--list': + for tool in pgtoolkit.tools.__all__: + print(tool) + sys.exit() + +tool = sys.argv[1] +tool_args = sys.argv[2:] + +module = import_module('pgtoolkit.tools.' + tool) + +tool = module.cls() +tool.init(tool_args) +tool.main() + diff -r 245646538743 -r d6088dba8fea pgtoolkit/toolbase.py --- a/pgtoolkit/toolbase.py Tue May 06 18:34:38 2014 +0200 +++ b/pgtoolkit/toolbase.py Tue May 06 18:37:41 2014 +0200 @@ -1,12 +1,13 @@ -import argparse -import logging -import re +from pgtoolkit import pgmanager, pgbrowser from pycolib.configparser import ConfigParser from pycolib.coloredformatter import ColoredFormatter from pycolib.ansicolor import highlight -from pgtoolkit import pgmanager, pgbrowser +import argparse +import logging +import re +import textwrap class ConnectionInfoNotFound(Exception): @@ -17,9 +18,17 @@ pass +class ToolDescriptionFormatter(argparse.HelpFormatter): + """Help message formatter which retains any formatting in descriptions.""" + + def _fill_text(self, text, width, indent): + return textwrap.dedent(text) + + class ToolBase: def __init__(self, name, desc, **kwargs): - self.parser = argparse.ArgumentParser(description=desc) + self.parser = argparse.ArgumentParser(prog=name, description=desc, + formatter_class=ToolDescriptionFormatter) self.parser.add_argument('-d', dest='debug', action='store_true', help='Debug mode - print database queries.') @@ -31,9 +40,9 @@ self.pgm = pgmanager.get_instance() self.target_isolation_level = None - def init(self): + def init(self, args=None): self.config.load('pgtoolkit.conf') - self.args = self.parser.parse_args() + self.args = self.parser.parse_args(args) self.init_logging() def init_logging(self): @@ -110,8 +119,8 @@ ToolBase.__init__(self, name, desc, **kwargs) self.parser.add_argument('target', metavar='target', type=str, help='Target database') - def init(self): - ToolBase.init(self) + def init(self, args=None): + ToolBase.init(self, args) self.prepare_conns(target=self.args.target) @@ -123,8 +132,8 @@ if 'allow_reverse' in kwargs and kwargs['allow_reverse']: self.parser.add_argument('-r', '--reverse', action='store_true', help='Reverse operation. Swap source and destination.') - def init(self): - ToolBase.init(self) + def init(self, args=None): + ToolBase.init(self, args) if self.is_reversed(): self.args.src, self.args.dst = self.args.dst, self.args.src self.prepare_conns(src=self.args.src, dst=self.args.dst) @@ -146,8 +155,8 @@ dest='dstschema', type=str, default='', help='Destination schema name (default=source_schema).') self.parser.add_argument('--regex', action='store_true', help="Use RE in schema or table name.") - def init(self): - SrcDstTool.init(self) + def init(self, args=None): + SrcDstTool.init(self, args) self.schema1 = self.args.srcschema self.table1 = self.args.srctable diff -r 245646538743 -r d6088dba8fea pgtoolkit/tools/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pgtoolkit/tools/__init__.py Tue May 06 18:37:41 2014 +0200 @@ -0,0 +1,1 @@ +__all__ = ['tablediff'] diff -r 245646538743 -r d6088dba8fea pgtoolkit/tools/tablediff.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pgtoolkit/tools/tablediff.py Tue May 06 18:37:41 2014 +0200 @@ -0,0 +1,55 @@ +from pgtoolkit import toolbase, pgmanager, pgdatadiff +from pgtoolkit.toolbase import SrcDstTablesTool +from pycolib.ansicolor import highlight, BOLD, YELLOW + + +class TableDiffTool(toolbase.SrcDstTablesTool): + + """ + Print differencies between data in tables. + + Requirements: + * Source table must have defined PRIMARY KEY. + * Destination table must contain all columns from source table. + Order is not important. + + """ + + def __init__(self): + SrcDstTablesTool.__init__(self, name='tablediff', desc=self.__doc__, allow_reverse=True) + self.parser.add_argument('--sql', action='store_true', help='Output is SQL script.') + self.parser.add_argument('--rowcount', action='store_true', help='Compare number of rows.') + + def main(self): + srcconn = self.pgm.get_conn('src') + dstconn = self.pgm.get_conn('dst') + + dd = pgdatadiff.PgDataDiff(srcconn, dstconn) + + for srcschema, srctable, dstschema, dsttable in self.tables(): + print('-- Diff from [%s] %s.%s to [%s] %s.%s' % ( + self.args.src, srcschema, srctable, + self.args.dst, dstschema, dsttable)) + + if self.args.rowcount: + with self.pgm.cursor('src') as curs: + curs.execute('''SELECT count(*) FROM "%s"."%s"''' % (srcschema, srctable)) + srccount = curs.fetchone()[0] + with self.pgm.cursor('dst') as curs: + curs.execute('''SELECT count(*) FROM "%s"."%s"''' % (dstschema, dsttable)) + dstcount = curs.fetchone()[0] + if srccount != dstcount: + print(highlight(1, BOLD | YELLOW), "Row count differs: src=%s dst=%s" % (srccount, dstcount), highlight(0), sep='') + continue + + dd.settable1(srctable, srcschema) + dd.settable2(dsttable, dstschema) + + if self.args.sql: + dd.print_patch() + else: + dd.print_diff() + + +cls = TableDiffTool + diff -r 245646538743 -r d6088dba8fea tablediff.py --- a/tablediff.py Tue May 06 18:34:38 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 -# -# Print differencies between data in tables. -# -# Requirements: -# * Source table must have defined PRIMARY KEY. -# * Destination table must contain all columns from source table. -# Order is not important. -# - -from pgtoolkit import toolbase, pgmanager, pgdatadiff -from pycolib.ansicolor import highlight, BOLD, YELLOW - - -class TableDiffTool(toolbase.SrcDstTablesTool): - def __init__(self): - toolbase.SrcDstTablesTool.__init__(self, name='tablediff', desc='Table diff.', allow_reverse = True) - - self.parser.add_argument('--sql', action='store_true', help='Output is SQL script.') - self.parser.add_argument('--rowcount', action='store_true', help='Compare number of rows.') - - self.init() - - def main(self): - srcconn = self.pgm.get_conn('src') - dstconn = self.pgm.get_conn('dst') - - dd = pgdatadiff.PgDataDiff(srcconn, dstconn) - - for srcschema, srctable, dstschema, dsttable in self.tables(): - print('-- Diff from [%s] %s.%s to [%s] %s.%s' % ( - self.args.src, srcschema, srctable, - self.args.dst, dstschema, dsttable)) - - if self.args.rowcount: - with self.pgm.cursor('src') as curs: - curs.execute('''SELECT count(*) FROM "%s"."%s"''' % (srcschema, srctable)) - srccount = curs.fetchone()[0] - with self.pgm.cursor('dst') as curs: - curs.execute('''SELECT count(*) FROM "%s"."%s"''' % (dstschema, dsttable)) - dstcount = curs.fetchone()[0] - if srccount != dstcount: - print(highlight(1, BOLD | YELLOW), "Row count differs: src=%s dst=%s" % (srccount, dstcount), highlight(0), sep='') - continue - - dd.settable1(srctable, srcschema) - dd.settable2(dsttable, dstschema) - - if self.args.sql: - dd.print_patch() - else: - dd.print_diff() - - -tool = TableDiffTool() -tool.main() -