Skip to content

/ Zope / gocept svn checkins / Archive / 2006 / 2006-10 / SVN: r4338 - in tcm2sql/trunk: . tests

[ << ] [ >> ]

[ SVN: r4335 - AlphaFlow/branches/wosc-aspects / ... ] [ SVN: r4349 - lms/branches/mac_postgres_branch/lib/... ]

SVN: r4338 - in tcm2sql/trunk: . tests
Michael Howitz <mh(at)gocept.com>
2006-10-10 11:53:30 [ FULL ]
Author: mac
Date: Tue Oct 10 11:53:26 2006
New Revision: 4338

Modified:
   tcm2sql/trunk/SSDReader.py
   tcm2sql/trunk/tcm2sql.py
   tcm2sql/trunk/tests/test_ssdreader.py
Log:
fixed tests because global optCreateLoggingTables did not exist in test

Modified: tcm2sql/trunk/SSDReader.py
==============================================================================
--- tcm2sql/trunk/SSDReader.py	(original)
+++ tcm2sql/trunk/SSDReader.py	Tue Oct 10 11:53:26 2006
(at)(at) -58,12 +58,11 (at)(at)
         # mapping child -> parent
         self.parent = {}
 
-    def build(self):
+    def build(self, optCreateLoggingTables):
             self.read_all()
             self.build_tables()
             self.build_relations()
             self.build_generalizations()
-            global optCreateLoggingTables
             if optCreateLoggingTables:
                 self.build_logtables()
 

Modified: tcm2sql/trunk/tcm2sql.py
==============================================================================
--- tcm2sql/trunk/tcm2sql.py	(original)
+++ tcm2sql/trunk/tcm2sql.py	Tue Oct 10 11:53:26 2006
(at)(at) -93,13 +93,13 (at)(at)
     
 if oldStateFile is not None:
     oldState = SSDReader(oldStateFile)
-    oldState.build()
+    oldState.build(__builtins__.optCreateLoggingTables)
 
 if newStateFile is None:
     usage()
     sys.exit()
 newState = SSDReader(newStateFile)
-newState.build()
+newState.build(__builtins__.optCreateLoggingTables)
 
 if rendererName is None:
     sys.stderr.write("Note: using RenderPostgres\n")

Modified: tcm2sql/trunk/tests/test_ssdreader.py
==============================================================================
--- tcm2sql/trunk/tests/test_ssdreader.py	(original)
+++ tcm2sql/trunk/tests/test_ssdreader.py	Tue Oct 10 11:53:26 2006
(at)(at) -1,7 +1,7 (at)(at)
 # Copyright (c) 2003 gocept gmbh & co. kg, http://www.gocept.com
 # Christian Zagrodnick, cz(at)gocept.com
 # See also LICENSE.txt
-# $Id: test_ssdreader.py,v 1.2 2003/08/14 08:42:26 zagy Exp $
+# $Id$
 
 import os
 import unittest
(at)(at) -18,7 +18,7 (at)(at)
     def test_read(self):
         self.assertRaises(IOError, SSDReader, 'does-not-exist')
         ssd = SSDReader(self.ssd_filename)
-        ssd.build()
+        ssd.build(1)
         pet = ssd.tables['Pet']
         self.assertEquals(len(pet.attrlist), 4)
         self.assertEquals(len(pet.pklist), 1)

SVN: r4339 - tcm2sql/trunk/doc
Michael Howitz <mh(at)gocept.com>
2006-10-10 11:55:18 [ FULL ]
Author: mac
Date: Tue Oct 10 11:55:16 2006
New Revision: 4339

Modified:
   tcm2sql/trunk/doc/Example-1.ssd
Log:
fixed constraint definition

Modified: tcm2sql/trunk/doc/Example-1.ssd
==============================================================================
--- tcm2sql/trunk/doc/Example-1.ssd	(original)
+++ tcm2sql/trunk/doc/Example-1.ssd	Tue Oct 10 11:55:16 2006
(at)(at) -1,9 +1,9 (at)(at)
 Storage 
 {
-	{ Format 1.31 }
-	{ GeneratedFrom TSSD-version-2.01 }
-	{ WrittenBy zagy }
-	{ WrittenOn "Mon Feb  4 14:49:41 2002" }
+	{ Format 1.33 }
+	{ GeneratedFrom TSSD-version-2.20 }
+	{ WrittenBy mac }
+	{ WrittenOn "" }
 }
 
 Document 
(at)(at) -13,6 +13,7 (at)(at)
 	{ Author zagy }
 	{ CreatedOn "Mon Feb  4 09:16:02 2002" }
 	{ Annotation "" }
+	{ Hierarchy False }
 }
 
 Page 
(at)(at) -34,7 +35,7 (at)(at)
 SSDClassNode 1
 {
 	{ Name "Pet" }
-	{ Annotation "This is a nice Pet.\r\rBut be aware that\r? invalidGender:
check gender in ('m','f')\ris a constraint, whereas the <ext> is beeing
ignored." }
+	{ Annotation "This is a nice Pet.\r\rBut be aware that\r? invalidGender:
check(gender in ('m','f'))\ris a constraint, whereas the <ext> is beeing
ignored." }
 	{ Parent 0 }
 	{ Index "" }
 	{ Attributes 4 }
(at)(at) -370,7 +371,7 (at)(at)
 	{ End2 WhiteTriangle }
 	{ Points 2 }
 	{ Point 107 237 }
-	{ Point 128 148 }
+	{ Point 128 149 }
 	{ NamePosition 104 190 }
 	{ Color "black" }
 	{ LineWidth 1 }
(at)(at) -393,7 +394,7 (at)(at)
 	{ End2 WhiteTriangle }
 	{ Points 2 }
 	{ Point 230 237 }
-	{ Point 175 148 }
+	{ Point 175 149 }
 	{ NamePosition 213 187 }
 	{ Color "black" }
 	{ LineWidth 1 }

SVN: r4340 - tcm2sql/trunk
Michael Howitz <mh(at)gocept.com>
2006-10-10 11:57:28 [ FULL ]
Author: mac
Date: Tue Oct 10 11:57:27 2006
New Revision: 4340

Modified:
   tcm2sql/trunk/README   (contents, props changed)
Log:
corrected Composition relation documentation

Modified: tcm2sql/trunk/README
==============================================================================
--- tcm2sql/trunk/README	(original)
+++ tcm2sql/trunk/README	Tue Oct 10 11:57:27 2006
(at)(at) -5,7 +5,7 (at)(at)
     Author: Christian Zagrodnick 
     Email: cz+gocept.com
     Valid for: tcm2sql 0.9.1
-    CVS: $Id: README,v 1.9 2005/03/31 11:31:49 zagy Exp $
+    CVS: $Id$
 
 
 Copying
(at)(at) -142,7 +142,7 (at)(at)
 
 			* Composition (black diamond)
 
-				results in an `on delete cascade'
+				results in an `on delete cascade on update cascade'
 
 				The diamond has to be connected to the table
 				with the referenced PRIMARY KEY.

SVN: r4341 - tcm2sql/trunk
Michael Howitz <mh(at)gocept.com>
2006-10-10 11:58:58 [ FULL ]
Author: mac
Date: Tue Oct 10 11:58:54 2006
New Revision: 4341

Modified:
   tcm2sql/trunk/Render.py
Log:
reniced whitespace usage for readability

Modified: tcm2sql/trunk/Render.py
==============================================================================
--- tcm2sql/trunk/Render.py	(original)
+++ tcm2sql/trunk/Render.py	Tue Oct 10 11:58:54 2006
(at)(at) -1,6 +1,7 (at)(at)
-# Copyright (C) 2002 gocept gmbh & co.kg, 06366 Koethen/Anahlt, Germany
+# Copyright (C) 2002-2006 gocept gmbh & co.kg, 06112 Halle(Saale), Germany
 # Christian Zagrodnick, cz(at)gocept.com
-# $Id: Render.py,v 1.4 2003/08/14 09:50:39 zagy Exp $
+# Michael Howitz, mh(at)gocept.com
+# $Id$
 #
 #   This program is free software; you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
(at)(at) -16,10 +17,11 (at)(at)
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+import copy
 
 class Render:
 
-    def __init__(self,new,old=None):
+    def __init__(self, new, old=None):
         self.new=new
         self.old=old
     
(at)(at) -55,7 +57,7 (at)(at)
             
     def _remainingTables(self):
         return [t
-            for t in self._oldTables()+self._newTables()
+            for t in self._oldTables() + self._newTables()
             if t not in self._removedTables()
             if t not in self._addedTables()
             ]
(at)(at) -116,60 +118,50 (at)(at)
             if r not in self._oldRelations()
         ]
 
-
-
     def _getOrder(self, state):
-        import copy
-
-        TABLES=state.tables
-        PARENT=state.parent
-
-        k=TABLES.keys()
-        dim=len(TABLES.keys())
-        matrix=dim*[dim*[0]]
+        TABLES = state.tables
+        PARENT = state.parent
+        k = TABLES.keys()
+        dim = len(TABLES.keys())
+        matrix = dim*[dim*[0]]
+        
         # adjazenzmatrix aufbauen und reflexive huelle bilden
-        for i in range(0,len(matrix)):
-            matrix[i]=copy.deepcopy(matrix[i])
-            for j in range (0,len(matrix)):
-                if (PARENT.get(k[i],0)==k[j]) or i==j:
-                    matrix[i][j]=1
+        for i in xrange(0, len(matrix)):
+            matrix[i] = copy.deepcopy(matrix[i])
+            for j in xrange (0, len(matrix)):
+                if (PARENT.get(k[i], 0) == k[j]) or i == j:
+                    matrix[i][j] = 1
 
-        # transitive Huelle bilden 
+        # transitive Huelle bilden
         # Algorithmus nach Warshall 
         # http://www.informatik.uni-frankfurt.de/~zinndorf/haskell/mathe.htm
-        for k in range(0,len(matrix)): 
-            for zeile in range(0,len(matrix)):
-                for spalte in range(0,len(matrix)): 
+        for k in xrange(0, len(matrix)): 
+            for zeile in xrange(0, len(matrix)):
+                for spalte in xrange(0, len(matrix)): 
                     if not matrix[zeile][spalte]:
-                        matrix[zeile][spalte]=\
-                            (matrix[zeile][k] and 
-                            matrix[k][spalte])
-
-        
+                        matrix[zeile][spalte] = (matrix[zeile][k] and
+                                                 matrix[k][spalte])
         
         # Die Antisymetrie zu ueberpruefen spar ich mir; wenn der user nicht
         # ganz doof ist passt das schon mit der halbordnungsrelation
-
         
         # sortierung
-        sorted=[]
+        sorted = []
         
-        for k in range(0,len(matrix)):
-            max=0
-            maxid=0
-
-            for i in range(0,len(matrix)):
-                sum=0
-                for j in range(0,len(matrix)):
-                    sum=sum+matrix[j][i]
-                if sum>max:
-                    max=sum
-                    maxid=i
-
-            sorted=sorted+[TABLES.keys()[maxid]]
-            for j in range(0,len(matrix)):
-                matrix[j][maxid]=0
+        for k in xrange(0, len(matrix)):
+            max = 0
+            maxid = 0
+
+            for i in xrange(0, len(matrix)):
+                sum = 0
+                for j in xrange(0, len(matrix)):
+                    sum = sum + matrix[j][i]
+                if sum > max:
+                    max = sum
+                    maxid = i
+
+            sorted = sorted + [TABLES.keys()[maxid]]
+            for j in xrange(0, len(matrix)):
+                matrix[j][maxid] = 0
 
         return sorted
-
-

SVN: r4342 - tcm2sql/trunk
Michael Howitz <mh(at)gocept.com>
2006-10-10 12:00:07 [ FULL ]
Author: mac
Date: Tue Oct 10 12:00:04 2006
New Revision: 4342

Added:
   tcm2sql/trunk/RenderDB.py   (contents, props changed)
Modified:
   tcm2sql/trunk/RenderPostgres.py
Log:
Moved most functionality from RenderPostgress to RenderDB to make support of
other DBMS like MySQL easier

Added: tcm2sql/trunk/RenderDB.py
==============================================================================
--- (empty file)
+++ tcm2sql/trunk/RenderDB.py	Tue Oct 10 12:00:04 2006
(at)(at) -0,0 +1,388 (at)(at)
+# Copyright (C) 2006 gocept gmbh & co.kg, 06112 Halle(Saale), Germany
+# Christian Zagrodnick, cz(at)gocept.com
+# Michael Howitz, mh(at)gocept.com
+# $Id$
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# python
+import re
+
+# sibling
+from Render import Render as RenderBase
+
+class Render(RenderBase):
+    """Abstract renderer class for databases."""
+    
+    _abstract = True # set it to false on concrete classes
+
+    ### the following methods may have to be overwritten for concrete DBMS ###
+    
+    def startTransaction(self):
+        """Return the command to start a transaction."""
+        return "BEGIN;\n"
+
+    def commitTransaction(self):
+        """Return the command to commit a transaction."""
+        return "COMMIT;\n"
+
+    def createTable(self, tablename, column_list):
+        result = ["\nCREATE TABLE %s (" % tablename]
+        result.extend(column_list)
+        result.append(")")
+        return "\n".join(result)
+
+    def dropTable(self, tablename):
+        return 'DROP TABLE %s;\n' % tablename
+
+    def renderInheritenceTableFooter(self, tablename, parents, all_tables):
+        if tablename in parents.keys():
+            return (" INHERITS (%s)" %
+                    all_tables[parents[tablename]].name)
+        else:
+            return ""
+
+    def renderTableFooter(self):
+        return ""
+
+    def renderAttribute(self, attr):
+        """Render one attribute."""
+        return "    %s %s," % (attr[0], attr[1])
+
+    def renderSerialType(self, tablename, columnname):
+        return ("INTEGER NOT NULL DEFAULT "
+                "NEXTVAL('\"%s_%s_seq\"'::text)" %
+                (tablename.lower(), columnname.lower()))
+
+    def renderConstraint(self, name, rule):
+        return "CONSTRAINT %s %s," % (name, rule)
+
+    def copyToTemporaryTable(self, tablename):
+        return 'CREATE TEMPORARY TABLE "TMP%s" AS SELECT * FROM %s;\n' % \
+               (tablename.replace('"', ''), tablename)
+    
+    def copyFromTemporaryTable(self, tablename, fields):
+        return ('INSERT INTO %s (%s)\n'
+                '    SELECT %s FROM "TMP%s";\n' %
+                (tablename, fields, fields, tablename.replace('"', '')))
+
+    def createView(self, tablename, columnlist):
+        result = ['CREATE VIEW "sv%s" AS' % tablename,
+                  '    SELECT',
+                  '        ' + ','.join(columnlist),
+                  '    FROM %s;' % tablename,
+                  '', ]
+        return "\n".join(result)
+
+    def dropView(self, tablename):
+        return 'DROP VIEW "sv%s";\n' % tablename
+
+    def createSequence(self, tablename, columnname):
+        return "CREATE SEQUENCE %s_%s_seq;\n" % (tablename, columnname)
+
+    def dropSequence(self, tablename, columnname):
+        return "DROP SEQUENCE %s_%s_seq;\n" % (tablename, columnname)
+
+    def renderRelation(self, rel, TABLES):
+        """Create foreign key constraint."""
+        table1name = TABLES[rel.table1].name
+        table2name = TABLES[rel.table2].name
+        result = ["ALTER TABLE %s" % table1name,
+                  "ADD CONSTRAINT %s" % self._get_foreign_key(table2name,
+                                                              rel.field2),
+                  "FOREIGN KEY (%s)" % rel.field1,
+                  "REFERENCES %s (%s)" % (table2name, rel.field2),
+                  ]
+        if rel.kind == 1: # aggregation
+            result.append("ON DELETE SET NULL")
+        elif rel.kind == 2: # composition
+            result.extend(["ON DELETE CASCADE",
+                           "ON UPDATE CASCADE"])
+        elif rel.kind == 3: # binary
+            pass # XXX
+        result.append(";\n")
+        return "\n".join(result)
+
+    def dropRelation(self, rel, TABLES):
+        "Drop foreign key constraint."""
+        table1name = TABLES[rel.table1].name
+        table2name = TABLES[rel.table2].name
+        result = ["\nALTER TABLE %s" % table1name,
+                  "DROP CONSTRAINT %s" % self._get_foreign_key(table2name,
+                                                              rel.field2),
+                  ";"]
+        return "\n".join(result)
+
+    def logRules(self, TABLES, table, PARENT):
+        """Create triggers for logging tables. """
+        tn = table.name
+        log_table = table.getLoggingTable()
+        log_fields = ', '.join(
+            map(lambda x: x[0],
+                self._inheritedFields(log_table.name, TABLES, PARENT,
+                                      fields=[])))
+        table_fields = ', '.join(
+            map(lambda x: 'new.%s' % (x[0], ),
+                self._inheritedFields(tn, TABLES, PARENT, fields=[])))
+        
+        result = ["""\nCREATE or REPLACE FUNCTION "trig_%s"() """
+                  """RETURNS TRIGGER AS '""" %  tn,
+                  "    BEGIN",
+                  "        INSERT INTO %s (%s)" % (log_table.name,
log_fields),
+                  "        VALUES (now(), %s);" % table_fields,
+                  "        RETURN NULL;",
+                  "    END;'",
+                  "LANGUAGE 'plpgsql';",
+                  "CREATE TRIGGER I%s AFTER INSERT OR UPDATE ON %s" % (tn,
tn),
+                  'FOR EACH ROW EXECUTE PROCEDURE "trig_%s"();' % tn,
+                  'CREATE RULE "U%s" AS ON UPDATE TO %s DO INSTEAD NOTHING;'
%\
+                  (tn, log_table.name),
+                  'CREATE RULE "D%s" AS ON DELETE TO %s DO INSTEAD NOTHING;'
%\
+                  (tn, log_table.name),
+                  '']
+        return "\n".join(result)
+
+    def fixup_logging_helper(self, ssd):
+        global optCreateLoggingTables
+        for table in ssd.tables.values():
+            # add LOGUser if logging is on
+            if optCreateLoggingTables:
+                table.attrlist.insert(0, ('"LOGUser"', 'VARCHAR(32) NOT
NULL'))
+                table.flaglist.insert(0, ('"LOGUser"', ''))
+
+            # the rest here is only for logging tables
+            if not table.isLoggingTable():
+                continue
+            
+            # add LOGTime to logging tables
+            table.attrlist.insert(0, ('"LOGTime"',
+                'TIMESTAMP NOT NULL DEFAULT now()'))
+            table.flaglist.insert(0, ('"LOGTime"', ''))
+            
+            # set serials to integer in logging tables
+            for i in range(0, len(table.attrlist)):
+                name, type = table.attrlist[i]
+                if self._serial_pattern.match(type):
+                    table.attrlist[i] = (name, 'integer')
+
+
+    ### The following methods are independent from DBMS. ###
+    
+    _serial_pattern = re.compile(r"^\s*serial\s*$")
+    
+    def __init__(self, *cwd, **kw):
+        RenderBase.__init__(self, *cwd, **kw)
+        self._foreign_keys = []
+        self._fixup_logging()
+        if self._abstract:
+            raise SystemError('%s is an abstract class, you can not use it '
+                              'as a renderer.' % self.__class__.__name__)
+
+    def _layoutFull(self):
+        """Returns a string with the table layout."""
+        
+        TABLES = self.new.tables
+        TABLES_VALUES = TABLES.values()
+        RELATIONS = self.new.relations
+        PARENT = self.new.parent
+
+        ret = self.startTransaction()
+
+        # create tables
+        for tableid in self._getOrder(self.new):
+            table = TABLES[tableid]
+            ret += self._renderTable(table, TABLES, RELATIONS, PARENT)
+        ret += "\n\n"
+
+        # create sequences for tables
+        for (tablename, colname) in self._get_serials(TABLES_VALUES):
+            ret += self.createSequence(tablename, colname)
+        ret += "\n\n"
+
+        # create relations
+        for rel in RELATIONS:
+            ret += self.renderRelation(rel, TABLES)
+        ret += "\n\n"
+
+        # create logging tables if requested
+        if optCreateLoggingTables:
+            non_logging_tables = [ table
+                                   for table in TABLES_VALUES
+                                   if not table.isLoggingTable()
+                                   ]
+            for table in non_logging_tables:
+                ret += self.logRules(TABLES, table, PARENT)
+
+        ret += self.commitTransaction()
+
+        return ret
+
+    def _layoutDiff(self):
+        """Returns a string with the diff layout."""
+        global optCreateViews
+        
+        ret = self.startTransaction()
+
+        # drop the foreign key constraints, so tables may be freely dropped
+        for rel in self.old.relations:
+            ret += self.dropRelation(rel, self.old.tables)
+            
+        # clean foreign key cache so that numbering if their ids stays
constant
+        self._foreign_keys = []
+
+        # copy table contents to temporary tables, drop old tables
+        dropOrder=self._getOrder(self.old)
+        dropOrder.reverse()
+        for tid in dropOrder:
+            t = self.old.tables[tid]
+            ret += self.copyToTemporaryTable(t.name)
+            if not t.isLoggingTable() and optCreateViews:
+                ret += self.dropView(t.name)
+            ret += self.dropTable(t.name)
+            ret += "\n" 
+
+        # create the new tables
+        for tableid in self._getOrder(self.new):
+            ret += self._renderTable(self.new.tables[tableid],
self.new.tables,
+                                     self.new.relations, self.new.parent)
+
+        # copy data back
+        dropedFields=self._dropedFields()
+        for t in [t
+            for t in self._oldTables()
+            for t2 in self._newTables()
+            if t == t2
+            ]:
+            dfThis=\
+                map((lambda x: x[1]),
+                filter((lambda x: x[0]==t),
+                dropedFields))
+            fields=','.join(
+                filter(lambda x: x not in dfThis,
+                map(lambda x: x[0],
+                t.attrlist)))
+            if fields > '':
+                ret += self.copyFromTemporaryTable(t.name, fields)
+        ret += "\n\n"
+        
+        # drop sequences of deleted tables
+        for (tablename, colname) in self._get_serials(self._removedTables()):
+            ret += self.dropSequence(tablename, colname)
+        ret += "\n\n"
+        
+        # create sequences for new tables
+        for (tablename, colname) in self._get_serials(self._addedTables()):
+            ret += self.createSequence(tablename, colname)
+        ret += "\n\n"
+
+        # create the foreign key constraints
+        for rel in self.new.relations:
+            ret += self.renderRelation(rel, self.new.tables)
+
+        # create log rules
+        global optCreateLoggingTables
+        if optCreateLoggingTables:
+            for table in [t
+                          for t in self.new.tables.values()
+                          if not t.isLoggingTable()]:
+                ret += self.logRules(self.new.tables, table, self.new.parent)
+
+        ret += self.commitTransaction()
+        return ret
+
+
+    def _renderTable(self, table, TABLES, RELATIONS, PARENT):
+        """Render a table."""
+        global optCreateViews
+
+        # transforms Attributes with type 'serial'
+        attrs = []
+        for (name, type) in table.attrlist:
+            if self._serial_pattern.match(type) is not None:
+                type = self.renderSerialType(table.name, name)
+            attrs.append((name, type))
+        
+        # render Attributes
+        lines = [self.renderAttribute(attr) for attr in attrs]
+
+        # constraints
+        lines.extend([self.renderConstraint(cs.name, cs.rule)
+                      for cs in table.clist])
+        
+        if len(lines):
+            # if there are lines remove the komma on the end of the last line
+            lines[-1] = lines[-1][:-1]
+
+        ret = self.createTable(table.name, lines)
+
+        # inheritance (generalization)
+        ret += self.renderInheritenceTableFooter(table.name, PARENT, TABLES)
+    
+        ret += self.renderTableFooter()
+        
+        if not table.isLoggingTable():
+            attrlist=map(lambda x: x[0],
+                filter(lambda x: '-' not in x[1],
+                self._inheritedFields(table.name, TABLES, PARENT, fields=[])))
+
+            if optCreateViews and len(attrlist) > 0:
+                ret += "\n"
+                ret += self.createView(table.name, attrlist)
+        return ret
+
+
+    def _inheritedFields(self,tableid,TABLES,PARENT,fields=[]):
+        #print "in: %s: %s" %(tableid, fields)
+        f = []
+        if tableid in PARENT.keys():
+            f = self._inheritedFields(PARENT[tableid], TABLES, PARENT,
+                                      fields=fields)
+
+        for x in TABLES[tableid].flaglist + f:
+            if x not in fields:
+                fields += [x, ]
+
+        #print "out: %s: %s" %(tableid,fields)
+        return fields       
+    
+    def _get_foreign_key(self, table, field):
+        postfix = ''
+        field_name = '_'.join([x.strip() for x in field.split(',')])
+        while 1:
+            fk_name = 'FK%s_%s%s' % (table, field_name, postfix)
+            if fk_name not in self._foreign_keys:
+                break
+            if postfix == '':
+                postfix = 0
+            postfix += 1
+        self._foreign_keys.append(fk_name)
+        return fk_name
+
+    def _fixup_logging(self):
+        self.fixup_logging_helper(self.new)
+        if self.old:
+            self.fixup_logging_helper(self.old)
+
+    def _get_serials(self, tables):
+        """Get the columns which are of type 'serial'.
+
+        Returns list of tuples (tablename, columnname).
+        """
+        result = []
+        for table in tables:
+            for (name, type) in table.attrlist:
+                if self._serial_pattern.match(type) is not None:
+                    result.append((table.name, name))
+        return result

Modified: tcm2sql/trunk/RenderPostgres.py
==============================================================================
--- tcm2sql/trunk/RenderPostgres.py	(original)
+++ tcm2sql/trunk/RenderPostgres.py	Tue Oct 10 12:00:04 2006
(at)(at) -17,311 +17,16 (at)(at)
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-# python
-import string # XXX phase out
-import re
-import copy
-import random
-
-# sibling
-from Render import Render as RenderBase
 
+from RenderDB import Render as RenderBase
 
 class Render(RenderBase):
-    """ Postgres renderer """
-    
-    _serial_pattern = re.compile(r"^\s*serial\s*$")
-    
-    def __init__(self, *cwd, **kw):
-        RenderBase.__init__(self, *cwd, **kw)
-        self._foreign_keys = []
-        self._fixup_logging()
-    
-    def _layoutFull(self):
-        """ returns a string with the table layout """
-        
-        TABLES = self.new.tables
-        RELATIONS = self.new.relations
-        PARENT = self.new.parent
-
-        ret="BEGIN;\n"
-        
-        for tableid in self._getOrder(self.new):
-            table = TABLES[tableid]
-            ret += self._renderTable(table, TABLES, RELATIONS, PARENT)
-
-        for rel in RELATIONS:
-            ret+=self._renderRelation(rel,TABLES)
-
-        if optCreateLoggingTables:
-            non_logging_tables = [ table
-                for table in self.new.tables.values()
-                if not table.isLoggingTable()
-                ]
-            for table in non_logging_tables:
-                ret += self._logRules(self.new.tables, table, PARENT)
-
-        ret += "COMMIT;\n"
-
-        return ret
-
-    def _layoutDiff(self):
-        """ returns a string with the diff layout """
-        global optCreateViews
-        
-        random.seed()
-        ret="begin;\n"
-
-
-        # copy tables, drop tables
-        # this will drop all the foreign key constrains
-        dropOrder=self._getOrder(self.old)
-        dropOrder.reverse()
-        for tid in dropOrder:
-            t = self.old.tables[tid]
-            ret += 'create temporary table "TMP%s" ' \
-                'as select * from %s;\n' % (t.name.replace('"',''), t.name)
-            ret += "drop table %s;\n" % (t.name, )
-            if not t.isLoggingTable() and optCreateViews:
-                ret += 'drop view "sv%s";\n' % t.name
-            ret += "\n" 
-                
-                
-        # create the new tables     
-        for tableid in self._getOrder(self.new):
-            table=self.new.tables[tableid]
-            nattr=[]
-            for (name,type) in table.attrlist:
-                if self._serial_pattern.match(type) is not None:
-                    type="integer not null default "+\
-                    "nextval('\"%s_%s_seq\"'::text)" %\
-                    (table.name.lower(),name.lower())
-                nattr.append((name,type))
-            
-            oldattr = table.attrlist
-            table.attrlist = nattr
-            ret += self._renderTable(table, self.new.tables,
-                self.new.relations, self.new.parent)
-            table.attrlist = oldattr
-            
-            
-        # copy data back
-        dropedFields=self._dropedFields()
-        for t in [t
-            for t in self._oldTables()
-            for t2 in self._newTables()
-            if t==t2
-            ]:
-            dfThis=\
-                map((lambda x: x[1]),
-                filter((lambda x: x[0]==t),
-                dropedFields))
-            fields=string.join(
-                filter(lambda x: x not in dfThis,
-                map(lambda x: x[0],
-                t.attrlist)),',')
-            if fields>'':
-                ret+="insert into %s (%s)\n" % (t.name,fields)
-                ret+="    select %s from \"TMP%s\";\n" %\
-                    (fields,t.name.replace('"',''))
-
-        ret += "\n\n"
-        
-        # drop sequences of deleted tables
-        for table in self._removedTables():
-            for (name,type) in table.attrlist:
-                if self._serial_pattern.match(type) is not None:
-                    ret += "DROP SEQUENCE %s_%s_seq;\n" % (table.name, name)
-                    
-        ret += "\n\n"
-        
-        # create sequences for new tables
-        for table in self._addedTables():
-            for (name,type) in table.attrlist:
-                if self._serial_pattern.match(type) is not None:
-                    ret += "CREATE SEQUENCE %s_%s_seq;\n" % (table.name, name)
-
-        ret += "\n\n"      
-
-        # create the foreign key constraints
-        for rel in self.new.relations:
-            ret+=self._renderRelation(rel,self.new.tables)
-
-
-        for table in [t
-                for t in self.new.tables.values()
-                if not t.isLoggingTable()]:
-            ret += self._logRules(self.new.tables, table, self.new.parent)
-
-        ret += "commit;\n"
-
-        return ret
-                
-                
-    
-            
-            
-    def _renderTable(self,table,TABLES,RELATIONS,PARENT):
-        """ render table """
-        global optCreateViews
-
-        ret=""
-        
-        ret+= "CREATE TABLE %s (\n" % table.name
-        count=0
-
-        # Attributes
-        for attr in table.attrlist:
-            if count>0:
-                ret+= ",\n"
-            count=count+1
-            ret+=self._renderAttribute(table,attr,RELATIONS)
-                
-                                            
-        
-        # constraints
-        for cs in table.clist:
-            if count>0:
-                ret+= ",\n    "
-            ret+=self._renderConstraint(cs.name,cs.rule)
-            count=count+1
-
-        ret+= "\n)"
-
-        # inheritance (generalization)
-        if table.name in PARENT.keys():
-            ret+= (" INHERITS (%s)" %
-                TABLES[PARENT[table.name]].name)
-    
-        ret+= " WITHOUT OIDS;\n\n"
-        
-        if not table.isLoggingTable():
-            attrlist=map(lambda x: x[0],
-                filter(lambda x: '-' not in x[1],
-                self._inheritedFields(table.name, TABLES, PARENT, fields=[])))
-
-            if optCreateViews and len(attrlist)>0:
-                ret+='CREATE VIEW "sv%s" AS\n' %(table.name,)
-                ret+='    SELECT\n    '
-                ret+=string.join(attrlist,',')
-                ret+='\n    FROM %s;\n\n' % (table.name,)
-        return ret
-    
-        
-
-    def _renderAttribute(self,table,attr,RELATIONS):
-        """ render one attribute """ 
-        
-        ret=""
-        
-        ret+= ("    %s %s" % (attr[0],attr[1]))
-        return ret  
-
-    def _renderConstraint(self,name,rule):
-        return ("CONSTRAINT %s %s" % (name,rule))
-
-
-    def _renderRelation(self,rel,TABLES):
-        ret=""
-
-        ret+= ("ALTER TABLE %s\n" % TABLES[rel.table1].name)
-        ret+= ("ADD CONSTRAINT %s\n" % (self._get_foreign_key(
-                TABLES[rel.table2].name, rel.field2)))
-        ret+= ("FOREIGN KEY (%s)\n" % rel.field1)
-        ret+= ("REFERENCES %s (%s)\n" % (TABLES[rel.table2].name,
-            rel.field2))
-
-        if rel.kind==1:
-            # aggregation
-            ret+= "ON DELETE SET NULL\n"
-            pass
-        elif rel.kind==2:
-            # composition
-            ret+= "ON DELETE CASCADE\nON UPDATE CASCADE\n"
-        elif rel.kind==3:
-            # binary
-            pass # XXX
-        ret+=";\n"
-        return ret
-
-
-    def _logRules(self, TABLES, table, PARENT):
-        """create triggers for logging tables
-        """
-        log_table =  table.getLoggingTable()
-        log_fields =', '.join(
-            map(lambda x: x[0], 
-               
self._inheritedFields(log_table.name,TABLES,PARENT,fields=[])))
-        table_fields = ', '.join(
-            map(lambda x: 'new.%s' % (x[0], ),
-                self._inheritedFields(table.name,TABLES,PARENT,fields=[])))
-        ret = "" 
-        ret += 'CREATE or REPLACE FUNCTION "trig_%s"() RETURNS ' % (
-            table.name, )
-        ret += "TRIGGER AS '\n"     
-        ret += "    BEGIN\n"
-        ret += '        INSERT INTO %s (%s)\n' % (log_table.name, log_fields)
-        ret += "        VALUES (now(), %s);\n" % (table_fields, )
-        ret += '        RETURN NULL;\n'
-        ret += "    END;'\n"
-        ret += "LANGUAGE 'plpgsql';\n"
-        ret += 'CREATE TRIGGER I%s AFTER INSERT OR UPDATE ON %s\n' % (
-            table.name, table.name)
-        ret += 'FOR EACH ROW EXECUTE PROCEDURE "trig_%s"();\n' % (
-            table.name, )
-        ret += 'CREATE RULE "U%s" AS ON UPDATE TO %s DO INSTEAD NOTHING;\n' %
(
-            table.name, log_table.name)
-        ret += 'CREATE RULE "D%s" AS ON DELETE TO %s DO INSTEAD NOTHING;\n\n'
% (
-            table.name, log_table.name)
-
-        return ret
-
-    def _inheritedFields(self,tableid,TABLES,PARENT,fields=[]):
-        #print "in: %s: %s" %(tableid, fields)
-        f=[]
-        if tableid in PARENT.keys():
-           
f=self._inheritedFields(PARENT[tableid],TABLES,PARENT,fields=fields)
-
-        for x in TABLES[tableid].flaglist+f:
-            if x not in fields: fields+=[x,]
-
-        #print "out: %s: %s" %(tableid,fields)
+    """Postgres renderer."""
 
-        return fields       
-    
-    def _get_foreign_key(self, table, field):
-        postfix = ''
-        field_name = '_'.join([x.strip() for x in field.split(',')])
-        fk_name = None
-        while 1:
-            fk_name = 'FK%s_%s%s' % (table, field_name, postfix)
-            if fk_name not in self._foreign_keys:
-                break
-            if postfix == '':
-                postfix = 0
-            postfix += 1
-        self._foreign_keys.append(fk_name)
-        return fk_name
+    _abstract = False
 
-    def _fixup_logging(self):
-        self._fixup_logging_helper(self.new)
-        if self.old:
-            self._fixup_logging_helper(self.old)
+    def renderTableFooter(self):
+        return " WITHOUT OIDS;\n"
 
-    def _fixup_logging_helper(self, ssd):
-        global optCreateLoggingTables
-        for table in ssd.tables.values():
-            if optCreateLoggingTables:
-                table.attrlist.insert(0, ('"LOGUser"', 'varchar(32) not
null'))
-                table.flaglist.insert(0, ('"LOGUser"', ''))
-            if not table.isLoggingTable():
-                continue
-            table.attrlist.insert(0, ('"LOGTime"',
-                'timestamp not null default now()'))
-            table.flaglist.insert(0, ('"LOGTime"', ''))
-            for i in range(0, len(table.attrlist)):
-                name, type = table.attrlist[i]
-                if self._serial_pattern.match(type):
-                    table.attrlist[i] = (name, 'integer')

SVN: r4347 - tcm2sql/trunk
Michael Howitz <mh(at)gocept.com>
2006-10-11 09:30:05 [ FULL ]
Author: mac
Date: Wed Oct 11 09:30:02 2006
New Revision: 4347

Modified:
   tcm2sql/trunk/RenderDB.py
Log:
made constraints line up with columns in table definition

Modified: tcm2sql/trunk/RenderDB.py
==============================================================================
--- tcm2sql/trunk/RenderDB.py	(original)
+++ tcm2sql/trunk/RenderDB.py	Wed Oct 11 09:30:02 2006
(at)(at) -67,7 +67,7 (at)(at)
                 (tablename.lower(), columnname.lower()))
 
     def renderConstraint(self, name, rule):
-        return "CONSTRAINT %s %s," % (name, rule)
+        return "    CONSTRAINT %s %s," % (name, rule)
 
     def copyToTemporaryTable(self, tablename):
         return 'CREATE TEMPORARY TABLE "TMP%s" AS SELECT * FROM %s;\n' % \

SVN: r4356 - in tcm2sql/trunk: . doc
Michael Howitz <mh(at)gocept.com>
2006-10-18 14:13:01 [ FULL ]
Author: mac
Date: Wed Oct 18 14:12:54 2006
New Revision: 4356

Modified:
   tcm2sql/trunk/CHANGELOG
   tcm2sql/trunk/RenderDB.py
   tcm2sql/trunk/doc/Example-1.ssd
   tcm2sql/trunk/tcm2sql.py
Log:
implemented handling of bigserial like serial types + reniced help screen

Modified: tcm2sql/trunk/CHANGELOG
==============================================================================
--- tcm2sql/trunk/CHANGELOG	(original)
+++ tcm2sql/trunk/CHANGELOG	Wed Oct 18 14:12:54 2006
(at)(at) -1,10 +1,19 (at)(at)
-$Id: CHANGELOG,v 1.5 2005/03/31 11:31:49 zagy Exp $ 
+$Id$ 
+
+0.9.2 (2006-10-18)
+    - Restructured renderer classes to make it easier to write
+      renderers for other database managenemt systems.
+    - Better handling for serials and bigserials: Now they are always
+      written as integer with default + creation of sequence. (This
+      makes updating of tables possible which have serial columns.)
+    - Added commandline option --no_views to not generate views for the
tables.
+    - Made usage help a bit nicer.
 
 0.9.1 (2005-03-31)
     - Multi column foreign keys
     
 0.9 (2003-09-13)
-    - splitting database to several diagrams (see README for documentataion)
+    - splitting database to several diagrams (see README for documentation)
     - requires Python 2.2
     - complete rewrite of ssd file reader
     - large changes on internal data structures

Modified: tcm2sql/trunk/RenderDB.py
==============================================================================
--- tcm2sql/trunk/RenderDB.py	(original)
+++ tcm2sql/trunk/RenderDB.py	Wed Oct 18 14:12:54 2006
(at)(at) -61,10 +61,26 (at)(at)
         """Render one attribute."""
         return "    %s %s," % (attr[0], attr[1])
 
-    def renderSerialType(self, tablename, columnname):
-        return ("INTEGER NOT NULL DEFAULT "
-                "NEXTVAL('\"%s_%s_seq\"'::text)" %
-                (tablename.lower(), columnname.lower()))
+    def renderSerialType(self, tablename, columnname, datatype):
+        datatype = self.convertType(datatype)
+        return ("%s NOT NULL DEFAULT "
+                "NEXTVAL('%s_%s_seq'::text)" %
+                (datatype, tablename.lower(), columnname.lower()))
+
+    def convertType(self, type):
+        "Convert the type to the correct value for the DBMS."
+        serial_type = self._serial_pattern.match(type)
+        if serial_type is None:
+            return type
+        serial_type = serial_type.groups()[0]
+        if serial_type == "serial":
+            serial_type = "INTEGER"
+        elif serial_type == "bigserial":
+            serial_type = "BIGINT"
+        else:
+            raise ValueError("Don't know how to convert %s." % serial_type)
+        return serial_type
+
 
     def renderConstraint(self, name, rule):
         return "    CONSTRAINT %s %s," % (name, rule)
(at)(at) -90,10 +106,12 (at)(at)
         return 'DROP VIEW "sv%s";\n' % tablename
 
     def createSequence(self, tablename, columnname):
-        return "CREATE SEQUENCE %s_%s_seq;\n" % (tablename, columnname)
+        return "CREATE SEQUENCE %s_%s_seq;\n" % (tablename.lower(),
+                                                 columnname.lower())
 
     def dropSequence(self, tablename, columnname):
-        return "DROP SEQUENCE %s_%s_seq;\n" % (tablename, columnname)
+        return "DROP SEQUENCE %s_%s_seq;\n" % (tablename.lower(),
+                                               columnname.lower())
 
     def renderRelation(self, rel, TABLES):
         """Create foreign key constraint."""
(at)(at) -174,13 +192,12 (at)(at)
             # set serials to integer in logging tables
             for i in range(0, len(table.attrlist)):
                 name, type = table.attrlist[i]
-                if self._serial_pattern.match(type):
-                    table.attrlist[i] = (name, 'integer')
+                table.attrlist[i] = (name, self.convertType(type))
 
 
     ### The following methods are independent from DBMS. ###
     
-    _serial_pattern = re.compile(r"^\s*serial\s*$")
+    _serial_pattern = re.compile(r"^\s*(serial|bigserial)\s*$")
     
     def __init__(self, *cwd, **kw):
         RenderBase.__init__(self, *cwd, **kw)
(at)(at) -310,8 +327,10 (at)(at)
         # transforms Attributes with type 'serial'
         attrs = []
         for (name, type) in table.attrlist:
-            if self._serial_pattern.match(type) is not None:
-                type = self.renderSerialType(table.name, name)
+            serial_type = self._serial_pattern.match(type)
+            if serial_type is not None:
+                type = self.renderSerialType(table.name, name,
+                                             serial_type.groups()[0])
             attrs.append((name, type))
         
         # render Attributes
(at)(at) -376,7 +395,7 (at)(at)
             self.fixup_logging_helper(self.old)
 
     def _get_serials(self, tables):
-        """Get the columns which are of type 'serial'.
+        """Get the columns which are of type 'serial' or 'bigserial'.
 
         Returns list of tuples (tablename, columnname).
         """

Modified: tcm2sql/trunk/doc/Example-1.ssd
==============================================================================
--- tcm2sql/trunk/doc/Example-1.ssd	(original)
+++ tcm2sql/trunk/doc/Example-1.ssd	Wed Oct 18 14:12:54 2006
(at)(at) -56,7 +56,7 (at)(at)
 	{ Parent 0 }
 	{ Index "" }
 	{ Attributes 5 }
-	{ Attribute "#id: serial" }
+	{ Attribute "#id: bigserial" }
 	{ Attribute "~Family_id: integer" }
 	{ Attribute "name: varchar not null" }
 	{ Attribute "firstname: varchar not null" }

Modified: tcm2sql/trunk/tcm2sql.py
==============================================================================
--- tcm2sql/trunk/tcm2sql.py	(original)
+++ tcm2sql/trunk/tcm2sql.py	Wed Oct 18 14:12:54 2006
(at)(at) -30,15 +30,15 (at)(at)
 availableRenderers = ('Postgres', )
 
 def usage():
-    print """Usage: tcm2sql [options]
-        -n <file>        new SSD file [required]
-        -o <file>        old SSD file (enables DIFF)
-        -r <renderer>    select renderer [Postgres]
-        -w <file>        write to file [required]
-        --logtables      enable creation of logging tables
-        --no_views       disable creation of views for tables
-        -h, --help       this message
-""" 
+    print """\
+Usage: tcm2sql -n <file> -w <file> [other_options]
+   -n <file>      new SSD file [required]
+   -w <file>      write to file [required]
+   -o <file>      old SSD file (enables DIFF)
+   -r <renderer>  select renderer [currently only Postgres, which is the
default]
+   --logtables    enable creation of logging tables
+   --no_views     disable creation of views for tables
+   -h, --help     this message""" 
     
 oldState = None
 oldStateFile = None
(at)(at) -89,13 +89,13 (at)(at)
         print "Unknown Option %s" % (o,)
         usage()
         sys.exit()
-    
-    
+
+
 if oldStateFile is not None:
     oldState = SSDReader(oldStateFile)
     oldState.build(__builtins__.optCreateLoggingTables)
 
-if newStateFile is None:
+if newStateFile is None or outFile is None:
     usage()
     sys.exit()
 newState = SSDReader(newStateFile)

SVN: r4357 - tcm2sql/trunk
Michael Howitz <mh(at)gocept.com>
2006-10-18 14:13:27 [ FULL ]
Author: mac
Date: Wed Oct 18 14:13:26 2006
New Revision: 4357

Modified:
   tcm2sql/trunk/version.txt
Log:
releasing version 0.9.2

Modified: tcm2sql/trunk/version.txt
==============================================================================
--- tcm2sql/trunk/version.txt	(original)
+++ tcm2sql/trunk/version.txt	Wed Oct 18 14:13:26 2006
(at)(at) -1 +1 (at)(at)
-0.9.1
+0.9.2

SVN: r4358 - tcm2sql/tags/0.9.2
Michael Howitz <mh(at)gocept.com>
2006-10-18 14:14:41 [ FULL ]
Author: mac
Date: Wed Oct 18 14:14:39 2006
New Revision: 4358

Added:
   tcm2sql/tags/0.9.2/
      - copied from r4357, tcm2sql/trunk/
Log:
Releasing 0.9.2

MailBoxer