root/livinglogic.python.orasql/src/ll/orasql/scripts/oradiff.py @ 182:0d4d435866b6

Revision 182:0d4d435866b6, 7.3 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

Add class Privilege that models an object grant.

Add Connection.iterprivileges() for getting all privileges.

Add a script oragrant.py for copying privileges.

Update copyright year.

Line 
1#!/usr/bin/env python
2# -*- coding: iso-8859-1 -*-
3
4## Copyright 2005-2007 by LivingLogic AG, Bayreuth/Germany.
5## Copyright 2005-2007 by Walter Dörwald
6##
7## All Rights Reserved
8##
9## See orasql/__init__.py for the license
10
11
12import sys, os, difflib, optparse
13
14from ll import orasql, astyle
15
16
17s4comment = astyle.Style.fromenv("LL_ORASQL_REPRANSI_COMMENT", "black:black:bold")
18s4addedfile = astyle.Style.fromenv("LL_ORASQL_REPRANSI_ADDEDFILE", "black:green")
19s4addedline = astyle.Style.fromenv("LL_ORASQL_REPRANSI_ADDEDLINE", "green:black")
20s4removedfile = astyle.Style.fromenv("LL_ORASQL_REPRANSI_REMOVEDFILE", "black:red")
21s4removedline = astyle.Style.fromenv("LL_ORASQL_REPRANSI_REMOVEDLINE", "red:black")
22s4changedfile = astyle.Style.fromenv("LL_ORASQL_REPRANSI_CHANGEDFILE", "black:blue")
23s4changedline = astyle.Style.fromenv("LL_ORASQL_REPRANSI_CHANGEDLINE", "blue:black")
24s4pos = astyle.Style.fromenv("LL_ORASQL_REPRANSI_POS", "black:black:bold")
25s4connectstring = astyle.Style.fromenv("LL_ORASQL_REPRANSI_CONNECTSTRING", "yellow:black")
26s4object = astyle.Style.fromenv("LL_ORASQL_REPRANSI_OBJECT", "green:black")
27
28
29def cs(connection):
30    return s4connectstring(connection.connectstring())
31
32
33def df(obj):
34    return s4object(str(obj))
35
36
37def comment(msg, color):
38    return s4comment("-- ", msg)
39
40
41def gettimestamp(obj, connection, format):
42    try:
43        timestamp = obj.udate(connection)
44    except orasql.SQLObjectNotFoundError:
45        return "doesn't exist"
46    if timestamp is not None:
47        timestamp = timestamp.strftime(format)
48    else:
49        timestamp = "without timestamp"
50    return timestamp
51
52
53def getcanonicalddl(ddl, blank):
54    return [Line(line, blank) for line in ddl.splitlines()]
55
56
57class Line(object):
58    __slots__ = ("originalline", "compareline")
59
60    def __init__(self, line, blank):
61        self.originalline = line
62        if blank == "literal":
63            self.compareline = line
64        elif blank == "trail":
65            self.compareline = line.rstrip()
66        elif blank == "lead":
67            self.compareline = line.lstrip()
68        elif blank == "both":
69            self.compareline = line.strip()
70        elif blank == "collapse":
71            self.compareline = " ".join(line.strip().split())
72        else:
73            raise ValueError("unknown blank value %r" % blank)
74
75    def __eq__(self, other):
76        return self.compareline == other.compareline
77
78    def __ne__(self, other):
79        return self.compareline != other.compareline
80
81    def __hash__(self):
82        return hash(self.compareline)
83
84
85def showudiff(out, obj, ddl1, ddl2, connection1, connection2, context=3, timeformat="%c"):
86    def header(prefix, style, connection):
87        return style("%s %r in %s: %s" % (prefix, obj, connection.connectstring(), gettimestamp(obj, connection, timeformat)))
88
89    started = False
90    for group in difflib.SequenceMatcher(None, ddl1, ddl2).get_grouped_opcodes(context):
91        if not started:
92            out.writeln(header("---", s4removedfile, connection1))
93            out.writeln(header("+++", s4addedfile, connection2))
94            started = True
95        (i1, i2, j1, j2) = group[0][1], group[-1][2], group[0][3], group[-1][4]
96        out.writeln(s4pos("@@ -%d,%d +%d,%d @@" % (i1+1, i2-i1, j1+1, j2-j1)))
97        for (tag, i1, i2, j1, j2) in group:
98            if tag == "equal":
99                for line in ddl1[i1:i2]:
100                    out.writeln(" %s" % line.originalline)
101                continue
102            if tag == "replace" or tag == "delete":
103                for line in ddl1[i1:i2]:
104                    out.writeln(s4removedline("-", line.originalline))
105            if tag == "replace" or tag == "insert":
106                for line in ddl2[j1:j2]:
107                    out.writeln(s4addedline("+", line.originalline))
108
109
110def main(args=None):
111    colors = ("yes", "no", "auto")
112    modes = ("brief", "udiff", "full")
113    blanks = ("literal", "trail", "lead", "both", "collapse")
114    p = optparse.OptionParser(usage="usage: %prog [options] connectstring1 connectstring2")
115    p.add_option("-v", "--verbose", dest="verbose", help="Give a progress report?", default=False, action="store_true")
116    p.add_option("-c", "--color", dest="color", help="Color output (%s)" % ", ".join(colors), default="auto", choices=colors)
117    p.add_option("-m", "--mode", dest="mode", help="Output mode (%s)" % ", ".join(modes), default="udiff", choices=modes)
118    p.add_option("-n", "--context", dest="context", help="Number of context lines", type="int", default=2)
119    p.add_option("-k", "--keepjunk", dest="keepjunk", help="Output objects with '$' or 'SYS_EXPORT_SCHEMA_' in their name?", default=False, action="store_true")
120    p.add_option("-b", "--blank", dest="blank", help="How to treat whitespace (%s)" % ", ".join(blanks), default="literal", choices=blanks)
121
122    (options, args) = p.parse_args(args)
123    if len(args) != 2:
124        p.error("incorrect number of arguments")
125        return 1
126
127    if options.color == "yes":
128        color = True
129    elif options.color == "no":
130        color = False
131    else:
132        color = None
133    stdout = astyle.Stream(sys.stdout, color)
134    stderr = astyle.Stream(sys.stderr, color)
135
136    connection1 = orasql.connect(args[0])
137    connection2 = orasql.connect(args[1])
138
139    def fetch(connection):
140        objects = set()
141
142        def keep(obj):
143            if obj.owner is not None:
144                return False
145            if options.keepjunk:
146                return True
147            if "$" in obj.name or obj.name.startswith("SYS_EXPORT_SCHEMA_"):
148                return False
149            return True
150   
151        for (i, obj) in enumerate(connection.iterobjects(mode="flat", schema="user")):
152            keepdef = keep(obj)
153            if options.verbose:
154                msg = astyle.style_default("oradiff.py: ", cs(connection), ": fetching #%d " % (i+1), df(obj))
155                if not keepdef:
156                    msg += " (skipped)"
157                stderr.writeln(msg)
158            if keepdef:
159                objects.add(obj)
160        return objects
161
162    objects1 = fetch(connection1)
163    objects2 = fetch(connection2)
164
165    onlyin1 = objects1 - objects2
166    for (i, obj) in enumerate(onlyin1):
167        if options.verbose:
168            stderr.writeln("oradiff.py: only in ", cs(connection1), " #%d/%d " % (i+1, len(onlyin1)), df(obj))
169        if options.mode == "brief":
170            stdout.writeln(df(obj), ": only in ", cs(connection1))
171        elif options.mode == "full":
172            stdout.writeln(comment(df(obj), ": only in " % cs(connection1)))
173            ddl = obj.dropddl(connection1, term=True)
174            if ddl:
175                stdout.write(ddl)
176        elif options.mode == "udiff":
177            ddl = getcanonicalddl(obj.createddl(connection1), options.blank)
178            showudiff(stdout, obj, ddl, [], connection1, connection2, options.context)
179
180    onlyin2 = objects2 - objects1
181    for (i, obj) in enumerate(onlyin2):
182        if options.verbose:
183            stderr.writeln("oradiff.py: only in ", cs(connection2), " #%d/%d " % (i+1, len(onlyin2)), df(obj))
184        if options.mode == "brief":
185            stdout.writeln(df(obj), ": only in ", cs(connection2))
186        elif options.mode == "full":
187            stdout.writeln(comment(df(obj), ": only in ", cs(connection2)))
188            ddl = obj.createddl(connection2, term=True)
189            if ddl:
190                stdout.write(ddl)
191        elif options.mode == "udiff":
192            ddl = getcanonicalddl(obj.createddl(connection2), options.blank)
193            showudiff(stdout, obj, [], ddl, connection1, connection2, options.context)
194
195    common = objects1 & objects2
196    for (i, obj) in enumerate(common):
197        if options.verbose:
198            stderr.writeln("oradiff.py: diffing #%d/%d " % (i+1, len(common)), df(obj))
199        ddl1 = obj.createddl(connection1)
200        ddl2 = obj.createddl(connection2)
201        ddl1c = getcanonicalddl(ddl1, options.blank)
202        ddl2c = getcanonicalddl(ddl2, options.blank)
203        if ddl1c != ddl2c:
204            if options.mode == "brief":
205                stdout.writeln(df(obj), ": different")
206            elif options.mode == "full":
207                stdout.writeln(comment(df(obj), ": different"))
208                stdout.write(obj.createddl(connection2))
209            elif options.mode == "udiff":
210                showudiff(stdout, obj, ddl1c, ddl2c, connection1, connection2, options.context)
211
212
213if __name__ == "__main__":
214    sys.exit(main())
Note: See TracBrowser for help on using the browser.