commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

r5097 - in trunk/gnue-common/src/schema/scripter: . processors


From: johannes
Subject: r5097 - in trunk/gnue-common/src/schema/scripter: . processors
Date: Thu, 12 Feb 2004 08:19:42 -0600 (CST)

Author: johannes
Date: 2004-02-12 08:19:41 -0600 (Thu, 12 Feb 2004)
New Revision: 5097

Added:
   trunk/gnue-common/src/schema/scripter/Definition.py
   trunk/gnue-common/src/schema/scripter/processors/Base.py
   trunk/gnue-common/src/schema/scripter/processors/SQL.py
Removed:
   trunk/gnue-common/src/schema/scripter/processors/base.py
Modified:
   trunk/gnue-common/src/schema/scripter/Scripter.py
   trunk/gnue-common/src/schema/scripter/processors/interbase.py
   trunk/gnue-common/src/schema/scripter/processors/mysql.py
   trunk/gnue-common/src/schema/scripter/processors/oracle.py
   trunk/gnue-common/src/schema/scripter/processors/postgresql.py
Log:
Rewritten the scripter-stuff. gnue-schema is now able to create sql files with
schema-creation and data-insertion.


Added: trunk/gnue-common/src/schema/scripter/Definition.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/Definition.py 2004-02-12 00:40:26 UTC 
(rev 5096)
+++ trunk/gnue-common/src/schema/scripter/Definition.py 2004-02-12 14:19:41 UTC 
(rev 5097)
@@ -0,0 +1,109 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2001-2004 Free Software Foundation
+#
+# $Id: $
+#
+
+# =============================================================================
+# The basic definition class
+# =============================================================================
+class Definition:
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, name = None):
+    self.name = name
+
+
+# =============================================================================
+# Basic class for schema definitions
+# =============================================================================
+class SchemaDefinition (Definition):
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, name = None):
+    Definition.__init__ (self, name)
+    self.fields   = []
+    self.prologue = []
+    self.epilogue = []
+
+
+# =============================================================================
+# IndexDefinition is just basic at the moment
+# =============================================================================
+class IndexDefinition (SchemaDefinition):
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, name = None, unique = False):
+    SchemaDefinition.__init__ (self, name)
+    self.unique = unique
+
+# =============================================================================
+# TableDefinition adds postfield- and index- sequences
+# =============================================================================
+class TableDefinition (SchemaDefinition):
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, name = None):
+    SchemaDefinition.__init__ (self, name)
+
+    self.postfields = []
+    self.pk_fields  = []
+    self.indices    = []
+    self.constraints= []
+
+  # ---------------------------------------------------------------------------
+  # Create a new IndexDefinition and add it to our index-sequence
+  # ---------------------------------------------------------------------------
+  def newIndex (self, name, unique = False):
+    index = IndexDefinition (name, unique)
+    self.indices.append (index)
+    return index
+
+
+# =============================================================================
+# Definition class for data rows
+# =============================================================================
+class DataDefinition (Definition):
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, name = None):
+    Definition.__init__ (self, name)
+    self.clear ()
+
+  # ---------------------------------------------------------------------------
+  # Reset all members to initial state (on start of a new row)
+  # ---------------------------------------------------------------------------
+  def clear (self):
+    self.columns  = []
+    self.values   = []
+
+    self.prologue = []
+    self.lines    = []
+    self.epilogue = []

Modified: trunk/gnue-common/src/schema/scripter/Scripter.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/Scripter.py   2004-02-12 00:40:26 UTC 
(rev 5096)
+++ trunk/gnue-common/src/schema/scripter/Scripter.py   2004-02-12 14:19:41 UTC 
(rev 5097)
@@ -18,37 +18,33 @@
 #
 # Copyright 2002-2004 Free Software Foundation
 #
-# FILE:
-# Scripter.py
+# $Id: $
 #
-# DESCRIPTION:
-# Schema definition scripter
-#
-# NOTES:
-# Beware the Scriptor() class -- it smells like a billy goat.
-#
 
 from gnue.common import VERSION
 from gnue.common.schema import GSParser
 from gnue.common.utils.FileUtils import openResource, dyn_import
 from gnue.common.apps.GClientApp import GClientApp
 from processors import vendors
+from gnue.common.schema.scripter.Definition import *
 
 import sys
 import os
 
-class ScripterRunner(GClientApp):
-  #
-  # GClientApp() overrides
-  #
-  VERSION = VERSION
-  COMMAND = "gnue-schema"
-  NAME = "GNUe Schema Scripter"
-  USAGE = "[options] file [old-schema]"
+# =============================================================================
+# Generate SQL files from GNUe Schema Definition files
+# =============================================================================
+
+class Scripter (GClientApp):
+
+  VERSION         = VERSION
+  COMMAND         = "gnue-schema"
+  NAME            = "GNUe Schema Scripter"
+  USAGE           = "[options] file [old-schema]"
   COMMAND_OPTIONS = [
       [ 'drop_tables',None,'drop-tables', 0, None, None,
           'Generate commands to drop relevant tables.  * NOT IMPLEMENTED'],
-      [ 'ignore_schema','S','no-create', 0, None, None,
+      [ 'ignore_schema','S','no-schema', 0, None, None,
           'Do not generate schema creation code.  * NOT IMPLEMENTED'],
       [ 'ignore_data','D','no-data', 0, None, None,
           'Do not generate data insertion code.  * NOT IMPLEMENTED'],
@@ -65,8 +61,8 @@
       [ 'output','o','output', 1, None, 'dest',
           'The destination for the created schemas. This can be in several '
           'formats. If <dest> is a file name, then output is written to this '
-          'file. If <dest> is a directory, then <dest>/<Vendor>.sql is 
created. '
-          'The default is to create <Vendor>.sql in the current directory. '
+          'file. If <dest> is a directory, then <dest>/<Vendor>.sql is 
created.'
+          ' The default is to create <Vendor>.sql in the current directory. '
           'NOTE: the first form (<dest> as a filename) is not supported for '
           '--vendors all.' ],
       [ 'vendor','v','vendor', 1, 'all', 'vendor',
@@ -74,101 +70,73 @@
           'scripts for all supported vendors will be created. <vendor> can '
           'also be a comma-separated list.'],
       ]
+
   SUMMARY = \
      "GNUe Schema Scripter creates SQL files based on GNUe Schema Definitions."
 
-  def __init__(self, connections=None):
-    ConfigOptions = {}
-    GClientApp.__init__(self, connections,'schema',ConfigOptions)
-  
-  #
-  # Run
-  #
-  def run(self):
+  _PROC_PATH = "gnue.common.schema.scripter.processors.%s"
 
-    globals().update(self.OPTIONS)
 
-    # List vendors, if requested
-    if list_vendors:
-      self.listVendors()
-      sys.exit()
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, connections = None):
+    ConfigOptions = {}
+    GClientApp.__init__ (self, connections, 'schema', ConfigOptions)
+    self.__vendors = []
 
-    upgrading = 0
 
-    # Do some sanity checks on the options
-    if upgrade_schema or upgrade_data:
-      upgrading = 1
+  # ---------------------------------------------------------------------------
+  # Walk through all command line options 
+  # ---------------------------------------------------------------------------
+  def __check_options (self):
+    
+    # only list all available processors and quit the program
+    if self.OPTIONS ["list_vendors"]:
+      self.__listVendors ()
+      sys.exit (0)
 
-      if drop_tables:
-        self.handleStartupError('--drop-tables is not compatable with the '
-               '--upgrade-?? options. Unable to create an upgrade schema.')
-      try:
-        old_schema = self.ARGUMENTS[1]
-      except IndexError:
-        self.handleStartupError('An --upgrade-?? option was selected, but '
-               'only one source file was specified. Unable to create '
-               'an upgrade schema.')
+    # we need at least one thing to do :)
+    if self.OPTIONS ["ignore_schema"] and self.OPTIONS ["ignore_data"]:
+      self.handleStartupError (_("--no-schema and --no-data cannot be used ") +
+        _("together. What to export?"))
 
-    elif drop_tables and ignore_schema:
-      # If we are dropping tables and ignoring schema,
-      # then we can't possibly want the data.
-      ignore_data = 1
+    # check for unsupported options
+    if self.OPTIONS ["drop_tables"] or self.OPTIONS ["upgrade_schema"] or \
+       self.OPTIONS ["upgrade_data"]:
+      self.handleStartupError (_("--drop-tables, --upgrade-schema and ") + 
+                               _("--upgrade-data\n are not implemented yet."))
 
 
-    if vendor.lower() == 'all':
-      vens = vendors
+    # do we have an accessible input file
+    if not len (self.ARGUMENTS):
+      self.handleStartupError (_("No input file specified."))
 
-    else:
-      vens = vendor.split(',')
+    try:
+      self.__input = openResource (self.ARGUMENTS [0])
 
-    if len(vens) > 1 and (output and not os.path.isdir(output)):
-      self.handleStartupError('--output cannot reference a file '
-                              'if multiple vendors are specified.')
-
-    # Assign input file
-    try:
-      schema = self.ARGUMENTS[0]
-      input = openResource(schema)
-    except IndexError:
-      self.handleStartupError('No source file was specified.')
     except IOError:
-      self.handleStartupError('Unable to open requested file: %s' % schema)
+      self.handleStartupError (_("Unable to open input file %s.") % \
+                                 self.ARGUMENTS [0])
 
-    print
-    scripter = Scripter(schema)
-    for ven in vens:
-      if not output:
-        outfile = self.getVendorName(ven) + '.sql'
-      elif os.path.isdir(output):
-        outfile = os.path.join(output, self.getVendorName(ven) + '.sql')
-      else:
-        outfile = output
+    # check the specified vendors
+    if self.OPTIONS ["vendor"].lower () == "all":
+      self.__vendors.extend (vendors)
+    else:
+      self.__vendors.extend (self.OPTIONS ["vendor"].split (","))
 
-      try:
-        out = open(outfile,'w')
-      except IOError, mesg:
-        self.handleStartupError('Unable to open destination file: %s\n\n%s' % 
(outfile,mesg))
+    self.__output = self.OPTIONS ["output"]
+    if len (self.__vendors) > 1 and self.__output is not None:
+      if not os.path.isdir (self.__output):
+        self.handleStartupError ( \
+          _("If multiply vendors are specified --output must be a ") +
+          _("directory or\n left empty."))
 
-      print "Writing schema to %s ..." % outfile
 
-      try:
-        scripter.writeSql(ven, out)
-      except:
-        print "WARNING: Unable to create a schema for %s" % ven
-        raise
-
-      out.close()
-
-  #
-  # Get a vendor name
-  #
-  def getVendorName(self, vendor):
-    return dyn_import('gnue.common.schema.scripter.processors.%s' % 
vendor).name
-
-  #
-  # List Vendors
-  #
-  def listVendors(self):
+  # ---------------------------------------------------------------------------
+  # Print a list of all available processors
+  # ---------------------------------------------------------------------------
+  def __listVendors (self):
     header = "%s\nVersion %s" % (self.NAME, self.VERSION)
     print
     print header
@@ -176,96 +144,179 @@
     print "Supported Database Vendors"
     print "--------------------------"
 
-    modules = {}
-    maxsize = 0
+    modules  = {}
+    maxsize  = 0
+
     for vendor in vendors:
-      maxsize = max(maxsize, len(vendor))
+      maxsize = max(maxsize, len (vendor))
+
       try:
-        modules[vendor] = 
dyn_import('gnue.common.schema.scripter.processors.%s' % vendor)
+        modules [vendor] = dyn_import (self._PROC_PATH % vendor)
+
       except ImportError:
         pass
 
-    srt = modules.keys()
-    srt.sort()
-    for vendor in srt:
-      print vendor.ljust(maxsize+4), modules[vendor].description
+    available = modules.keys ()
+    available.sort()
 
+    for vendor in available:
+      print vendor.ljust (maxsize + 4), modules [vendor].description
+
     print
 
 
+  # ---------------------------------------------------------------------------
+  # Get the name of a given processor
+  # ---------------------------------------------------------------------------
+  def __getProcessorName (self, processor):
+    return dyn_import (self._PROC_PATH % processor).name
 
 
+  # ---------------------------------------------------------------------------
+  # Generate an SQL script for a given vendor
+  # ---------------------------------------------------------------------------
+  def __generateSQL (self, vendor):
+    if not self.__output:
+      filename = "%s.sql" % self.__getProcessorName (vendor)
 
+    elif os.path.isdir (self.__output):
+      filename = os.path.join (self.__output, 
+                               "%s.sql" % self.__getProcessorName (vendor))
+    else:
+      filename = self.__output
 
-############################################################
-#
-# The actual worker class
-#
-class Scripter:
-  def __init__(self, source, oldsource=None):
+    try:
+      self.destination = open (filename, "w")
 
-    if hasattr(source,'readline'):
-      input = source
-    else:
-      input = openResource(source)
+    except IOError:
+      sys.stderr.write (_("Unable to create output file %s.") % filename)
+      sys.exit (1)
 
-    self.schema = GSParser.loadFile(input)
+    # Instanciate the given processor and iterate over all schema objects
+    aModule = self._PROC_PATH % vendor
+    self.processor = dyn_import (aModule).Processor (self.destination)
 
+    print _("Writing schema to %s ...") % filename
+    self.schema.walk (self.__iterate_objects)
 
-  def writeSql(self, format, destination=sys.stdout):
-    self.processor = dyn_import('gnue.common.schema.scripter.processors.%s' % 
format).Processor()
-    self.destination = destination
+    # and finally close the output file
+    self.destination.close () 
 
-    self.schema.walk(self._walkTables)
 
-  def _walkTables(self, object):
-    if object._type == 'GSTable':
-      self.processor.comment (self.destination,"\nCreate '%s' table\n" % 
object.name)
-      self.destination.write('\n')
-      self._fields = []
-      self._postfields = []
-      self._pretable = []
-      self._posttable = []
-      self._pkfields = []
-      self._pkname = ""
-      object.walk(self._walkTable, tablename=object.name)
-      if len(self._pkfields):
-        self._handlePK(object.name)
-      self.destination.write(self.processor.createTable(object.name, 
self._fields + self._postfields, self._pretable, self._posttable))
-      self.destination.write('\n\n')
+  # ---------------------------------------------------------------------------
+  # iteration over all schema objects in the document tree
+  # ---------------------------------------------------------------------------
+  def __iterate_objects (self, sObject):
+    if sObject._type == "GSSchema":
+      if not self.OPTIONS ["ignore_schema"]:
+        self.processor.startSchema ()
 
-  def _walkTable(self, object, tablename):
-    if object._type == 'GSField':
-      exec 
"self._handlePrePost(self.processor.createField(object.name,tablename,self.processor.%s(object),
 object))" % object.type in locals()
-    elif object._type == 'GSPKField':
-      self._pkfields.append(object.name)
-    elif object._type == 'GSIndex':
-      self._indexfields = []
-      object.walk(self._walkIndex, tablename)
-      
self._handlePrePost(self.processor.createIndex(object.name,tablename,self._indexfields,
 object.unique))
+    elif sObject._type == "GSTable":
+      if not self.OPTIONS ["ignore_schema"]:
+        self.__schema_table (sObject)
 
+    elif sObject._type == "GSData":
+      if not self.OPTIONS ["ignore_data"]:
+        self.processor.startData ()
 
-  def _walkIndex(self, object, tablename):
-    if object._type == 'GSIndexField':
-      self._indexfields.append(object.name)
+    elif sObject._type == "GSTableData":
+      if not self.OPTIONS ["ignore_data"]:
+        self.__data_table (sObject)
 
+    return 
 
-  def _handlePK(self, tablename):
-    
self._handlePrePost(self.processor.createPrimaryKey(self._pkname,tablename, 
self._pkfields))
 
+  # ---------------------------------------------------------------------------
+  # Process the schema definition of a GSTable object
+  # ---------------------------------------------------------------------------
+  def __schema_table (self, sObject):
+    aTable = TableDefinition (sObject.name)
+    sObject.walk (self.__schema_fields, tableDef = aTable)
 
-  # Butt-ugly stuff
-  def _handlePrePost(self, results):
-    field, extra, pretable, posttable = results
-    if field:
-      self._fields.append(field)
-    if extra:
-      self._postfields += extra
-    if pretable:
-      self._pretable += pretable
-    if posttable:
-      self._posttable += posttable
+    self.processor.writeTable (aTable)
 
 
+  # ---------------------------------------------------------------------------
+  # Process the fields of a GSTable
+  # ---------------------------------------------------------------------------
+  def __schema_fields (self, sObject, tableDef):
+
+    # process a regular field of a table
+    if sObject._type == "GSField":
+      self.processor.addField (tableDef, sObject)
+
+    # process a primary key field
+    elif sObject._type == "GSPKField":
+      tableDef.pk_fields.append (sObject.name)
+
+    # start an index definition and process it's fields
+    elif sObject._type == "GSIndex":
+      uniq = hasattr (sObject, "unique") and sObject.unique
+      index = tableDef.newIndex (sObject.name, uniq)
+
+      # iterate over all index fields
+      sObject.walk (self.__schema_index, tableDef = tableDef, indexDef = index)
+
+    # TODO: constraints 
+
+
+  # ---------------------------------------------------------------------------
+  # Iterate over all fields of an index
+  # ---------------------------------------------------------------------------
+  def __schema_index (self, sObject, tableDef, indexDef):
+    if sObject._type == "GSIndexField":
+      indexDef.fields.append (sObject.name)
+
+
+  # ---------------------------------------------------------------------------
+  # Process a tabledata node
+  # ---------------------------------------------------------------------------
+  def __data_table (self, sObject):
+    data = DataDefinition (sObject.tablename)
+
+    self.destination.write ("\n")
+    for line in self.processor.comment (_("\nData for %s\n") % data.name):
+      self.destination.write (line + "\n")
+
+    sObject.walk (self.__data_rows, dataDef = data)
+
+    if len (data.values):
+      self.processor.writeData (data)
+
+
+  # ---------------------------------------------------------------------------
+  # Iterate over all rows of a tabledata definition
+  # ---------------------------------------------------------------------------
+  def __data_rows (self, sObject, dataDef):
+
+    if sObject._type == "GSRow":
+      if len (dataDef.values):
+        self.processor.writeData (dataDef)
+
+      dataDef.clear ()
+
+    elif sObject._type == "GSValue":
+      if hasattr (sObject, "field"):
+        dataDef.columns.append (sObject.field)
+      dataDef.values.append (sObject._children [0].getContent ())
+
+
+
+  # ---------------------------------------------------------------------------
+  # Main program
+  # ---------------------------------------------------------------------------
+  def run (self):
+
+    self.__check_options ()
+
+    self.schema = GSParser.loadFile (self.__input)
+
+    for vendor in self.__vendors:
+      self.__generateSQL (vendor)
+
+
+# =============================================================================
+# If executed directly, start the scripter
+# =============================================================================
 if __name__ == '__main__':
-  ScripterRunner().run()
+  Scripter ().run ()

Copied: trunk/gnue-common/src/schema/scripter/processors/Base.py (from rev 
5093, trunk/gnue-common/src/schema/scripter/processors/base.py)
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/base.py    2004-02-11 
00:03:33 UTC (rev 5093)
+++ trunk/gnue-common/src/schema/scripter/processors/Base.py    2004-02-12 
14:19:41 UTC (rev 5097)
@@ -0,0 +1,243 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2002-2004 Free Software Foundation
+#
+# $Id: $
+
+from string import join
+
+# =============================================================================
+# Base class for GNUe Schema Definition processors
+# =============================================================================
+class BaseProcessor:
+  """
+  This is the base class for GNUe Schema Definition processors. Such a
+  processor will be fed with TableDefinition- and DataDefinition-objects and
+  translates them according to it's language/dialect. It's doing this by
+  populating the Definition-object's list-properties. 
+  (see gnue.common.schema.scripter.Definition)
+  """
+
+  MAX_NAME_LENGTH    = 30
+
+  COMMENT_BEGIN      = "-- "
+  COMMENT_END        = ""
+  COMMENT_SINGLELINE = 1
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+  def __init__ (self, destination):
+    self.destination = destination
+
+
+  # ---------------------------------------------------------------------------
+  # Find a method implementation in this class or any superclass of this class
+  # ---------------------------------------------------------------------------
+  def __findMethod (self, aClass, aMethod):
+    if aClass.__dict__.has_key (aMethod):
+      return aClass.__dict__ [aMethod]
+    else:
+      for base in aClass.__bases__:
+        result = self.__findMethod (base, aMethod)
+        if result is not None:
+          return result
+
+    return None
+  
+
+  # ---------------------------------------------------------------------------
+  # Create an identifier for a sequence-like thing
+  # ---------------------------------------------------------------------------
+  def _getSequenceName (self, tablename, gsObject):
+    """
+    Create a name for a sequence-like objects using 'tablename' and 'gsObject'.
+    """
+    res = ""
+
+    if hasattr (gsObject, "name"):
+      res = "%s_%s_seq" % (tablename, gsObject.name)
+
+    if res == "" or len (res) > self.MAX_NAME_LENGTH:
+      res = "%s_%d_seq" % (tablename, 
+                           gsObject._parent._children.index (gsObject))
+
+    if len (res) > self.MAX_NAME_LENGTH:
+      res = "%s_%s_seq" % (tablename, id (gsObject))
+
+    if len (res) > self.MAX_NAME_LENGTH:
+      res = "seq_%s" % id (gsObject)
+
+    return res
+
+
+  # ---------------------------------------------------------------------------
+  # fully qualify a field with name and datatype
+  # ---------------------------------------------------------------------------
+  def _qualify (self, gsField):
+    """
+    This method qualifies 'gsField' by concatenating the fieldname and it's
+    translated datatype (see _translateType ()).
+    """
+    return "%s %s" % (gsField.name, self._translateType (gsField))
+
+
+  # ---------------------------------------------------------------------------
+  # get an apropriate representation for gsField's datatype
+  # ---------------------------------------------------------------------------
+  def _translateType (self, gsField):
+    """
+    Find a method for datatype translation of gsField.type in the current
+    class or any of it's superclasses and return it's result.
+    """
+    aMethod = self.__findMethod (self.__class__, gsField.type)
+    if aMethod is None:
+      raise AttributeError, _("%s instance has no attribute %s.") % \
+                             (self.__class__.__name__, gsField.type)
+    else:
+      return aMethod (self, gsField)
+
+
+  # ---------------------------------------------------------------------------
+  # Comment all lines from text
+  # ---------------------------------------------------------------------------
+  def comment (self, text):
+    """
+    Create a sequence of 'commented' lines given in the sequence 'text'. Use
+    the COMMENT_* constants to control this functions behaviour.
+    """
+    result = []
+    
+    if self.COMMENT_SINGLELINE:
+      for line in text.split ("\n"):
+        result.append ("%s%s%s" % (self.COMMENT_BEGIN, line, self.COMMENT_END))
+
+    else:
+      space = " " * len (self.COMMENT_BEGIN)
+      first = True
+      for line in text.split ("\n"):
+        if first:
+          line = "%s%s" % (self.COMMENT_BEGIN, line)
+          first = False
+        else:
+          if len (line):
+            line = "%s%s" % (space, line)
+
+        result.append (line)
+
+      if len (result):
+        result [-1] += " %s" % self.COMMENT_END
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Treat 'datetime' as 'timestamp'
+  # ---------------------------------------------------------------------------
+  def datetime (self, gsField):
+    """
+    The datatype 'datetime' will call 'timestamp' implicitly, so it doesn't
+    matter wether GSD files use datetime or timestamp.
+    """
+    return self.timestamp (gsField)
+
+
+  # ---------------------------------------------------------------------------
+  # Write a table definition to the destination
+  # ---------------------------------------------------------------------------
+  def writeTable (self, tableDef):
+    """
+    This function writes a table definition to the destination. If the table
+    definition has primary key fields, _processPrimaryKey () will be called
+    first. If there are indices defined, _processIndices () will be called. If
+    the table definition has constraints, _processConstraints () will be
+    called.
+
+    NOTE: gnue-schema doesn't support constraints at the moment.
+    """
+    # Integrate referenced definitions first
+    if len (tableDef.pk_fields):
+      self._processPrimaryKey (tableDef)
+
+    if len (tableDef.indices):
+      self._processIndices (tableDef)
+
+    if len (tableDef.constraints):
+      self._processConstraints (tableDef)
+
+    # Now dump the table definition
+    for line in self.comment (_("\nCreate table '%s'\n") % tableDef.name):
+      self.destination.write (line + "\n")
+
+    # Create the prologue
+    if len (tableDef.prologue):
+      self.destination.write (join (tableDef.prologue, ";\n") + ";\n")
+
+    # Add the fields and postfields
+    self.destination.write ("CREATE TABLE %s\n (" % tableDef.name)
+    self.destination.write (join (tableDef.fields + tableDef.postfields,
+                                         ",\n  ") + ");\n")
+    # Create the epilogue
+    if len (tableDef.epilogue):
+      self.destination.write (join (tableDef.epilogue, "\n") + "\n")
+
+    self.destination.write ("\n");
+
+
+  # ---------------------------------------------------------------------------
+  # Write a data definition to the destination
+  # ---------------------------------------------------------------------------
+  def writeData (self, dataDef):
+    """
+    Writes a data definition to destination. Before dumping the data
+    definition's sequences, _processData () get's called to prepare the
+    definition.
+    """
+    self._processData (dataDef)
+
+    if len (dataDef.prologue):
+      self.destination.write (join (dataDef.prologue, "\n"))
+
+    if len (dataDef.lines):
+      self.destination.write (join (dataDef.lines, "\n") + "\n")
+
+    if len (dataDef.epilogue):
+      self.destination.write (join (dataDef.epilogue, "\n") + "\n")
+
+
+  # ---------------------------------------------------------------------------
+  # Virtual: called on start of a schema dump
+  # ---------------------------------------------------------------------------
+  def startSchema (self):
+    """
+    This method is called by the scripter on start of a schema dump. Use it to
+    take initial actions.
+    """
+    pass
+
+
+  # ---------------------------------------------------------------------------
+  # Virtual: called on start of a data dump
+  # ---------------------------------------------------------------------------
+  def startData (self):
+    """
+    This method is called by the scripter on start of a data dump. Use it to
+    take initial actions.
+    """
+    pass

Added: trunk/gnue-common/src/schema/scripter/processors/SQL.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/SQL.py     2004-02-12 
00:40:26 UTC (rev 5096)
+++ trunk/gnue-common/src/schema/scripter/processors/SQL.py     2004-02-12 
14:19:41 UTC (rev 5097)
@@ -0,0 +1,143 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2001-2004 Free Software Foundation
+#
+# $Id: $
+#
+
+from gnue.common.schema.scripter.processors.Base import BaseProcessor
+from string import join
+
+# =============================================================================
+# Baseclass for SQL processors
+# =============================================================================
+class SQLProcessor (BaseProcessor):
+  """
+  This class implements a GNUe Schema Definition processor for SQL-like
+  backends. 
+  """
+
+
+  # ---------------------------------------------------------------------------
+  # Primary key definition comes after the last field of a table
+  # ---------------------------------------------------------------------------
+  def _processPrimaryKey (self, tableDef):
+    """
+    Primary keys are specified after the last field of the table.
+    """
+    tableDef.postfields.append ("PRIMARY KEY (%s)" % 
+                                join (tableDef.pk_fields, ", "))
+
+
+  # ---------------------------------------------------------------------------
+  # Integrate index definitions into tableDef
+  # ---------------------------------------------------------------------------
+  def _processIndices (self, tableDef):
+    """
+    This function iterates over all specified index definitions and adds a
+    CREATE INDEX statement to the epilogue of the table definition.
+    """
+    # index definition comes after table definition, so we use the epilogue
+    epi = tableDef.epilogue
+
+    for index in tableDef.indices:
+      uni = ""
+      if index.unique:
+        uni = "UNIQUE "
+
+      epi.append ("")
+      epi.extend (self.comment (_("\nCreate index '%s'\n") % index.name))
+      epi.append ("CREATE %sINDEX %s ON %s" % (uni, index.name, tableDef.name))
+      epi.append (" (%s);" % join (index.fields, ", "))
+
+
+  # ---------------------------------------------------------------------------
+  # Integrate constraints into table definition
+  # ---------------------------------------------------------------------------
+  def _processConstraints (self, tableDef):
+    """
+    Constraints are NOT implemented at the moment
+    """
+    for constraint in tableDef.constraints:
+      pass
+
+
+  # ---------------------------------------------------------------------------
+  # Translate a data definition
+  # ---------------------------------------------------------------------------
+  def _processData (self, dataDef):
+    """
+    This function creates an INSERT statement to populate a table with data.
+    If the columns-sequence of the data definition holds values, a columnlist
+    will be added to the INSERT statement.
+    """
+    if len (dataDef.columns):
+      collist = " (%s)" % join (dataDef.columns, ", ")
+    else:
+      collist = ""
+
+    dataDef.lines.append ("INSERT INTO %s%s VALUES (%s);" % \
+      (dataDef.name, collist, join (dataDef.values, ", ")))
+
+
+
+  # ===========================================================================
+  # Datatype translation 
+  # ===========================================================================
+
+  # ---------------------------------------------------------------------------
+  # String usually becomes a 'varchar'
+  # ---------------------------------------------------------------------------
+  def string (self, gsField):
+    """
+    Returns a 'varchar' or 'varchar (length)' if gsField has a length property.
+    """
+    res = "varchar"
+    if hasattr (gsField, "length"):
+      res += " (%s)" % gsField.length
+
+    return res
+
+  # ---------------------------------------------------------------------------
+  # Keep date as 'date'
+  # ---------------------------------------------------------------------------
+  def date (self, gsField):
+    """
+    Keep date as 'date'
+    """
+    return "date"
+
+  # ---------------------------------------------------------------------------
+  # Keep time as 'time'
+  # ---------------------------------------------------------------------------
+  def time (self, gsField):
+    """
+    Keep time as 'time'
+    """
+    return "time"
+
+  # ---------------------------------------------------------------------------
+  # Keep timestamp as 'timestamp'
+  # ---------------------------------------------------------------------------
+  def timestamp (self, gsField):
+    """
+    Keep timestamp as 'timestamp'
+    """
+    return "timestamp"
+

Deleted: trunk/gnue-common/src/schema/scripter/processors/base.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/base.py    2004-02-12 
00:40:26 UTC (rev 5096)
+++ trunk/gnue-common/src/schema/scripter/processors/base.py    2004-02-12 
14:19:41 UTC (rev 5097)
@@ -1,117 +0,0 @@
-#
-# This file is part of GNU Enterprise.
-#
-# GNU Enterprise 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, or (at your option) any later version.
-#
-# GNU Enterprise 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 program; see the file COPYING. If not,
-# write to the Free Software Foundation, Inc., 59 Temple Place
-# - Suite 330, Boston, MA 02111-1307, USA.
-#
-# Copyright 2002-2004 Free Software Foundation
-#
-# FILE:
-# base.py
-#
-# DESCRIPTION:
-# PostgreSQL
-#
-# NOTES:
-#
-
-import string
-
-class BaseProcessor:
-
-  MAX_NAME_LENGTH = 30
-
-  COMMENT_BEGIN = "-- "
-#  COMMENT_END = " */\n"
-  COMMENT_END = ""
-  COMMENT_SINGLELINE=1
-
-  def __init__(self):
-    self.prepare()
-
-  def prepare(self):
-    pass
-
-  def number(self, attributes={}):
-    raise "Unsupported datatype: number"
-
-  def string(self, attributes={}):
-    raise "Unsupported datatype: string"
-
-  def date(self, attributes={}):
-    raise "Unsupported datatype: date"
-
-  def time(self, attributes={}):
-    raise "Unsupported datatype: time"
-
-  def timestamp(self, attributes={}):
-    raise "Unsupported datatype: timestamp"
-
-  def text(self, attributes={}):
-    raise "Unsupported datatype: text"
-
-  def key(self, attributes={}):
-    raise "Unsupported datatype: key"
-
-  def comment(self, destination, comment):
-
-    if self.COMMENT_SINGLELINE:
-
-      for line in comment.split('\n'):
-        destination.write("%s%s%s\n" % (self.COMMENT_BEGIN, line, 
self.COMMENT_END))
-
-    else:
-      lead = "\n " * len(self.COMMENT_BEGIN)
-      destination.write(self.COMMENT_BEGIN)
-      first = 1
-      for line in comment.split('\n'):
-        if not first:
-          destination.write(lead)
-
-        destination.write("%s" % (line))
-        first = 0
-      destination.write(self.COMMENT_END)
-
-
-  def createTable(self, name, fields, pretable=[], posttable=[]):
-    if pretable:
-      rv = string.join(pretable,';\n') + ';\n'
-    else:
-      rv = ""
-    rv += "CREATE TABLE %s\n (" % name
-    rv += string.join(fields,',\n  ') + ');\n'
-    if posttable:
-      rv += "\n" + string.join(posttable,'\n') + '\n'
-
-    return rv
-
-
-  def createField(self, name, tablename, datatype, object):
-    rv = '%s %s' % (name, datatype)
-    return ([rv], None, None, None)
-
-
-  def createPrimaryKey(self, name, tablename, fields):
-    rv = 'PRIMARY KEY (%s)' % string.join(fields,', ')
-    return (None, [rv], None, None)
-
-
-  def createIndex(self, name, tablename, fields, unique):
-    rv = 'CREATE %sINDEX %s ON %s (%s);' % (unique and "UNIQUE " or "",
-                           name, tablename, string.join(fields,', '))
-    return (None, None, None, [rv])
-
-
-

Modified: trunk/gnue-common/src/schema/scripter/processors/interbase.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/interbase.py       
2004-02-12 00:40:26 UTC (rev 5096)
+++ trunk/gnue-common/src/schema/scripter/processors/interbase.py       
2004-02-12 14:19:41 UTC (rev 5097)
@@ -18,111 +18,115 @@
 #
 # Copyright 2002-2004 Free Software Foundation
 #
-# FILE:
-# interbase.py
+# $Id: $
 #
-# DESCRIPTION:
-# Interbase
-#
-# NOTES:
-#
 
+from gnue.common.schema.scripter.processors.SQL import SQLProcessor
 
-from base import BaseProcessor
-
-name = "Interbase"
+name        = "Interbase"
 description = "Interbase (5.x/6.x)/Firebird (1.x)"
 
 
-class Processor(BaseProcessor):
+# =============================================================================
+# GSD processor for Interbase
+# =============================================================================
+class Processor (SQLProcessor):
 
+  COMMENT_BEGIN      = "/*"
+  COMMENT_END        = "*/"
+  COMMENT_SINGLELINE = False
+  MAX_NAME_LENGTH    = 31
 
-  COMMENT_BEGIN = "/*"
-  COMMENT_END = "*/"
-  MAX_NAME_LENGTH = 31
 
-  def prepare(self):
-    self.trigBody = []
+  # ---------------------------------------------------------------------------
+  # Add a GSField instance to the table definition
+  # ---------------------------------------------------------------------------
+  def addField (self, tableDef, gsField):
+    field = self._qualify (gsField)
 
-  def number(self, object):
-    if object.precision == 0:
-      if object.length <= 4:
-        return "smallint"
-      elif object.length <= 9:
-        return "integer"
-      else:
-        return "decimal(%s,0)" % object.length
-    else:
-      return "numeric(%s,%s)" % (object.length + object.precision, 
object.precision)
+    if gsField.defaultwith == "serial":
+      gen = self._getSequenceName (tableDef.name, gsField)
+      tableDef.prologue.append ("CREATE GENERATOR %s" % gen)
 
-  def string(self, object):
-    return "varchar(%s)" % object.length
+      self.__addTrigger (tableDef, gsField, gen)
 
-  def date(self, object):
-    return "date"
+    elif gsField.defaultwith == "timestamp":
+      field += " DEFAULT 'NOW'"
 
-  def time(self, object):
-    return "date"
+    elif hasattr (gsField, "default") and gsField.default is not None:
+      field += " DEFAULT %s" % gsField.default
 
-  def timestamp(self, object):
-    return "date"
+    if not gsField.nullable:
+      field += " NOT NULL"
 
-  def text(self, object):
-    if hasattr(object,'length') and object.length <= 2000:
-      return "varchar(%s)" % object.length
-    else:
-      return "blob"
+    tableDef.fields.append (field)
 
-  def key(self, object):
-    return "integer"
 
+  # ---------------------------------------------------------------------------
+  # Add a generator trigger to the table definition
+  # ---------------------------------------------------------------------------
+  def __addTrigger (self, tableDef, gsField, gen):
+    # TODO: check if this trigger code really makes sense !
+    epi = tableDef.epilogue
+    epi.append ("SET TERM ^ ;")
+    epi.append ("CREATE TRIGGER trg_%s FOR %s ACTIVE BEFORE INSERT %s" % \
+       (gsField.name, gen, "POSITION 0 AS BEGIN"))
+    epi.append (" new.%s=gen_id(%s,1);" % (gsField.name, gen))
+    epi.append ("END ^")
+    epi.append ("SET TERM ;^")
 
-  def createField(self, name, tablename, datatype, object):
-    rv = '%s %s' % (name, datatype)
 
-    posttable = []
-    pretable = []
-    trigBody = self.trigBody
+  # ===========================================================================
+  # Datatype translation
+  # ===========================================================================
 
-    if object.defaultwith == "serial":
+  # ---------------------------------------------------------------------------
+  # Keys are allways 'integer'
+  # ---------------------------------------------------------------------------
+  def key (self, gsField):
+    return "integer"
 
-      gen = tablename + '_' + name + '_gen'
-      if len(gen) > self.MAX_NAME_LENGTH:
-        gen = tablename + "_gen_"
-        gen += str(object._parent._children.index(object))
-      if len(gen) > self.MAX_NAME_LENGTH:
-        gen = tablename + "_" + str(id(object)) + '_gen'
-      if len(gen) > self.MAX_NAME_LENGTH:
-        gen = 'gen_%s' % id(object)
+  # ---------------------------------------------------------------------------
+  # Time becomes 'date'
+  # ---------------------------------------------------------------------------
+  def time (self, gsField):
+    return "date"
 
-      trigBody.append(" new.%s=gen_id(%s,1);\n" % (name,gen))
+  # ---------------------------------------------------------------------------
+  # Timestampe becomes 'date'
+  # ---------------------------------------------------------------------------
+  def timestamp (self, gsField):
+    return "date"
 
-      pretable.append ('CREATE GENERATOR %s' % gen)
+  # ---------------------------------------------------------------------------
+  # Text becomes either 'string' or 'blob'
+  # ---------------------------------------------------------------------------
+  def text (self, gsField):
+    if hasattr (gsField, "length") and gsField.length <= 2000:
+      return string (gsField)
+    else:
+      return "blob"
 
-    elif object.defaultwith == "timestamp":
-      rv += " DEFAULT 'NOW'"
+  # ---------------------------------------------------------------------------
+  # translate a number according to it's precision and length
+  # ---------------------------------------------------------------------------
+  def number (self, gsField):
+    if gsField.precision == 0:
+      if gsField.length <= 4:
+        return "smallint"
 
-    elif hasattr(object, 'default') and len(object.default):
-      rv += ' DEFAULT %s' % object.default
+      elif gsField.length <= 9:
+        return "integer"
 
-    if not object.nullable:
-      rv += ' NOT NULL'
+      else:
+        return "decimal(%s,0)" % gsField.length
 
-    # Field def, Post-Field Def, Pre-Table command, Post-Table command
-    return (rv,None,pretable or None,posttable or None)
+    else:
+      return "numeric(%s,%s)" % (gsField.length + gsField.precision, 
+                                 gsField.precision)
 
-  def createTable(self, name, fields, pretable=[], posttable=[]):
-    trigBody = self.trigBody
-
-    pretable.append("DROP TABLE %s" % name)
-
-    if len(trigBody):
-      posttable.append ("SET TERM ^ ;\n"+\
-        "CREATE TRIGGER trg_%s FOR %s ACTIVE BEFORE INSERT" % (name,name)+\
-        " POSITION 0 AS BEGIN\n" + ''.join(trigBody)+\
-        "END ^\n"+\
-        "SET TERM ;^")
-
-    self.prepare()
-
-    return BaseProcessor.createTable(self, name, fields, pretable, posttable)
+  # ---------------------------------------------------------------------------
+  # boolean becomes a number; TODO: add some check-constraints
+  # ---------------------------------------------------------------------------
+  def boolean (self, gsField):
+    return "smallint CHECK (VALUE IS NULL OR VALUE IN (0,1))"

Modified: trunk/gnue-common/src/schema/scripter/processors/mysql.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/mysql.py   2004-02-12 
00:40:26 UTC (rev 5096)
+++ trunk/gnue-common/src/schema/scripter/processors/mysql.py   2004-02-12 
14:19:41 UTC (rev 5097)
@@ -18,81 +18,83 @@
 #
 # Copyright 2002-2004 Free Software Foundation
 #
-# FILE:
-# mysql.py
+# $Id: $
 #
-# DESCRIPTION:
-# MySQL
-#
-# NOTES:
-#
 
+from gnue.common.schema.scripter.processors.SQL import SQLProcessor
 
-from base import BaseProcessor
-
-name = "MySQL"
+name        = "MySQL"
 description = "MySQL (3.x/4.x)"
 
 
-class Processor(BaseProcessor):
+# =============================================================================
+# GSD processor for PostgreSQL
+# =============================================================================
+class Processor (SQLProcessor):
 
-
   MAX_NAME_LENGTH = 64
 
-  def number(self, object):
-    if object.precision == 0:
-      if object.length <= 4:
-        return "smallint"
-      elif object.length <= 9:
-        return "int"
-      elif object.length <= 18:
-        return "bigint"
-      else:
-        return "decimal(%s,0)" % object.length
-    else:
-      return "decimal(%s,%s)" % (object.length, object.precision)
+  # ---------------------------------------------------------------------------
+  # Add a GSField instance to the table definition
+  # ---------------------------------------------------------------------------
+  def addField (self, tableDef, gsField):
+    field = self._qualify (gsField)
+    
+    if gsField.defaultwith == "serial":
+      field += " auto_increment"
 
-  def string(self, object):
-    return "varchar(%s)" % object.length
+    elif gsField.defaultwith == "timestamp":
+      field += " DEFAULT `current_timestamp()`"
 
-  def date(self, object):
-    return "date"
+    elif hasattr (gsField, "default") and gsField.default is not None:
+      field += " DEFAULT %s" % gsField.default
 
-  def time(self, object):
-    return "time"
+    if not gsField.nullable:
+      field += " NOT NULL"
 
-  def timestamp(self, object):
-    return "timestamp"
+    tableDef.fields.append (field)
 
-  def text(self, object):
-    if hasattr(object,'length') and object.length <= 255:
-      return "varchar(%s)" % object.length
-    else:
-      return "text"
 
-  def key(self, object):
+  # ===========================================================================
+  # Datatype translation
+  # ===========================================================================
+
+  # ---------------------------------------------------------------------------
+  # Keys are unsinged integers
+  # ---------------------------------------------------------------------------
+  def key (self, gsField):
     return "int unsigned"
 
+  # ---------------------------------------------------------------------------
+  # text becomes either a 'string' or 'text'
+  # ---------------------------------------------------------------------------
+  def text (self, gsField):
+    if hasattr (gsField, "length") and gsField.length <= 255:
+      return self.string (gsField)
+    else:
+      return "text"
 
-  def createField(self, name, tablename, datatype, object):
-    rv = '%s %s' % (name, datatype)
+  # ---------------------------------------------------------------------------
+  # Translate a number according to it's precision and length
+  # ---------------------------------------------------------------------------
+  def number (self, gsField):
+    if gsField.precision == 0:
+      if gsField.length <= 4:
+        return "smallint"
 
-    posttable = []
-    pretable = []
+      elif gsField.length <= 9:
+        return "int"
 
-    if object.defaultwith == "serial":
+      elif gsField.length <= 18:
+        return "bigint"
 
-      rv += " auto_increment"
+      else:
+        return "decimal (%s,0)" % gsField.length
+    else:
+      return "decimal (%s,%s)" % (gsField.length, gsField.precision)
 
-    elif object.defaultwith == "timestamp":
-      rv += " DEFAULT `current_timestamp()`"
-
-    elif hasattr(object, 'default') and len(object.default):
-      rv += ' DEFAULT %s' % object.default
-
-    if not object.nullable:
-      rv += ' NOT NULL'
-
-    # Field def, Post-Field Def, Pre-Table command, Post-Table command
-    return (rv,None,pretable or None,posttable or None)
-
+  # ---------------------------------------------------------------------------
+  # MySQL has no native boolean data type
+  # ---------------------------------------------------------------------------
+  def boolean (self, gsField):
+    return "tinyint (1) unsigned"

Modified: trunk/gnue-common/src/schema/scripter/processors/oracle.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/oracle.py  2004-02-12 
00:40:26 UTC (rev 5096)
+++ trunk/gnue-common/src/schema/scripter/processors/oracle.py  2004-02-12 
14:19:41 UTC (rev 5097)
@@ -1,111 +1,152 @@
+#
+# This file is part of GNU Enterprise.
+#
+# GNU Enterprise 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, or (at your option) any later version.
+#
+# GNU Enterprise 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 program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2001-2004 Free Software Foundation
+#
+# $Id: $
+#
 
-name = "Oracle"
+from gnue.common.schema.scripter.processors.SQL import SQLProcessor
+
+name        = "Oracle"
 description = "Oracle (7.x/8i/9i+)"
 
-from base import BaseProcessor
-import string
 
-_PK_PRECISION = 10
+# =============================================================================
+# GSD processor for PostgreSQL
+# =============================================================================
+class Processor (SQLProcessor):
 
-class Processor(BaseProcessor):
-
-
-  COMMENT_BEGIN = "REM ** "
+  COMMENT_BEGIN   = "REM ** "
   MAX_NAME_LENGTH = 31
+  _PK_PRECISION   = 10
 
-  def prepare(self):
-    self.trigWhen = []
-    self.trigBody = []
+  # ---------------------------------------------------------------------------
+  # add a GSField instance to the table definition
+  # ---------------------------------------------------------------------------
+  def addField (self, tableDef, gsField):
+    field = self._qualify (gsField)
 
+    # Add a 'serial' as default value
+    if gsField.defaultwith == "serial":
+      seq = self._getSequenceName (tableDef.name, gsField)
 
-  def number(self, object):
-    if object.precision == 0:
-      return "number(%s)" % object.length
-    else:
-      return "number(%s,%s)" % (object.length + object.precision, 
object.precision)
+      tableDef.prologue.append ("CREATE SEQUENCE %s MAXVALUE %s NOCYCLE" % \
+                                (seq, "9" * self._PK_PRECISION))
+      trig = []
+      trig.append ("   IF :new.%s IS NULL THEN" % gsField.name)
+      trig.append ("      SELECT %s.nextval INTO :new.%s FROM dual;" % \
+                   (seq, gsField.name))
+      trig.append ("   END IF;")
 
-  def string(self, object):
-    return "varchar2(%s)" % object.length
+      self.__addTrigger (tableDef, gsField, trig)
 
-  def date(self, object):
-    return "date"
+    # add a timestamp as default value
+    elif gsField.defaultwith == "timestamp":
+      if gsField.type == "date":
+        sysdate = "TRUNC (sysdate)"
+      else:
+        sysdate = "sysdate"
 
-  def time(self, object):
-    return "date"
+      trig = [] 
+      trig.append ("   IF :new.%s IS NULL THEN" % gsField.name)
+      trig.append ("      :new.%s := %s;" % (gsField.name, sysdate))
+      trig.append ("   END IF;")
 
-  def timestamp(self, object):
-    return "date"
+      self.__addTrigger (tableDef, gsField, trig)
 
-  def text(self, object):
-    if hasattr(object,'length') and object.length <= 2000:
-      return "varchar2(%s)" % object.length
-    else:
-      return "long"
+    # add a given 'constant' as default value 
+    elif hasattr (gsField, "default") and gsField.default is not None:
+      field += " DEFAULT %s" % gsField.default
 
-  def key(self, object):
-    return "number(%s)" % _PK_PRECISION
+    if not gsField.nullable:
+      field += " NOT NULL"
 
+    tableDef.fields.append (field)
 
-  def createField(self, name, tablename, datatype, object):
-    rv = '%s %s' % (name, datatype)
 
-    posttable = []
-    pretable = []
-    trigWhen = self.trigWhen
-    trigBody = self.trigBody
+  # ---------------------------------------------------------------------------
+  # Add a trigger for defaults to the table definition
+  # ---------------------------------------------------------------------------
+  def __addTrigger (self, tableDef, gsField, body):
+    epi = tableDef.epilogue
 
-    if object.defaultwith == "serial":
+    epi.append ("")
+    epi.append ("CREATE OR REPLACE TRIGGER t__%s__pre" % tableDef.name)
+    epi.append ("  BEFORE INSERT ON %s" % tableDef.name)
+    epi.append ("  FOR EACH ROW")
+    epi.append ("  WHEN ((new.%s IS NULL))" % gsField.name)
+    epi.append ("  BEGIN")
+    epi.extend (body)
+    epi.append ("  END;")
+    epi.append ("/")
 
-      sq = tablename + '_' + name + '_seq'
-      if len(sq) > self.MAX_NAME_LENGTH:
-        sq = tablename + "_seq_"
-        sq += str(object._parent._children.index(object))
-      if len(sq) > self.MAX_NAME_LENGTH:
-        sq = tablename + "_" + str(id(object)) + '_seq'
-      if len(sq) > self.MAX_NAME_LENGTH:
-        sq = 'seq_%s' % id(object)
+  # ===========================================================================
+  # Datatype translation
+  # ===========================================================================
 
-      trigWhen.append('(new.%s is null)' % object.name)
-      trigBody.append('     IF :new.%s IS NULL THEN\n' % (object.name) + \
-                      '        SELECT %s.nextval INTO :new.%s FROM dual;\n' % 
(sq, object.name) + \
-                      '     END IF;')
+  # ---------------------------------------------------------------------------
+  # String becomes 'varchar2'
+  # ---------------------------------------------------------------------------
+  def string (self, gsField):
+    return "varchar2 (%s)" % gsField.length
 
-      pretable.append ('CREATE SEQUENCE %s MAXVALUE %s NOCYCLE' % (sq, 
'9'*_PK_PRECISION))
+  # ---------------------------------------------------------------------------
+  # time becomes 'date'
+  # ---------------------------------------------------------------------------
+  def time (self, gsField):
+    return "date"
 
-    elif object.defaultwith == "timestamp":
-      sysdate = object.type == 'date' and "TRUNC(sysdate)" or "sysdate"
-      trigWhen.append('(new.%s is null)' % object.name)
-      trigBody.append('     IF :new.%s IS NULL then\n' % (object.name) + \
-                      '        :new.%s := %s;\n' % (object.name, sysdate) + \
-                      '     END IF;')
+  # ---------------------------------------------------------------------------
+  # so does timestamp
+  # ---------------------------------------------------------------------------
+  def timestamp (self, gsField):
+    return "date"
 
-    elif hasattr(object, 'default') and len(object.default):
-      rv += ' DEFAULT %s' % object.default
+  # ---------------------------------------------------------------------------
+  # text becomes either 'string' or 'long'
+  # ---------------------------------------------------------------------------
+  def text (self, gsField):
+    if hasattr (gsField, 'length') and gsField.length <= 2000:
+      return self.string (gsField)
+    else:
+      return "long"
 
-    if not object.nullable:
-      rv += ' NOT NULL'
+  # ---------------------------------------------------------------------------
+  # A key is a number of _PK_PRECISION
+  # ---------------------------------------------------------------------------
+  def key (self, gsField):
+    return "number (%s)" % self._PK_PRECISION
 
-    # Field def, Post-Field Def, Pre-Table command, Post-Table command
-    return (rv,None,pretable or None,posttable or None)
+  # ---------------------------------------------------------------------------
+  # Oracle doesn't seem to have booleans, so we're using number (1)
+  # ---------------------------------------------------------------------------
+  def boolean (self, gsField):
+    return "number (1) CHECK (%s IS NULL OR %s IN (0,1))" % \
+                            (gsField.name, gsField.name)
 
-  def createTable(self, name, fields, pretable=[], posttable=[]):
-    trigWhen = self.trigWhen
-    trigBody = self.trigBody
-    if len(trigBody):
-      if len(trigWhen):
-        when = "  WHEN ( %s )\n" % (string.join(trigWhen,' OR\n         '))
-      else:
-        when = ""
+  # ---------------------------------------------------------------------------
+  # Translate a number according to it's precision and length
+  # ---------------------------------------------------------------------------
+  def number (self, gsField):
+    if gsField.precision == 0:
+      return "number (%s)" % gsField.length
+    else:
+      return "number (%s,%s)" % (gsField.length + gsField.precision, 
+                                 gsField.precision)
 
-      posttable.append ('CREATE OR REPLACE TRIGGER t__%s__pre\n' % name + \
-                        '  BEFORE INSERT ON %s\n' % name + \
-                        '  FOR EACH ROW\n' + \
-                        when + \
-                        '  BEGIN\n' + \
-                        string.join(trigBody,'\n') + '\n' +\
-                        '  END;\n/\n')
-
-    self.prepare()
-
-    return BaseProcessor.createTable(self, name, fields, pretable, posttable)
-

Modified: trunk/gnue-common/src/schema/scripter/processors/postgresql.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/processors/postgresql.py      
2004-02-12 00:40:26 UTC (rev 5096)
+++ trunk/gnue-common/src/schema/scripter/processors/postgresql.py      
2004-02-12 14:19:41 UTC (rev 5097)
@@ -18,88 +18,85 @@
 #
 # Copyright 2002-2004 Free Software Foundation
 #
-# FILE:
-# postgresql.py
+# $Id: $
 #
-# DESCRIPTION:
-# PostgreSQL
-#
-# NOTES:
-#
 
+from gnue.common.schema.scripter.processors.SQL import SQLProcessor
 
-from base import BaseProcessor
-
-name = "PostgreSQL"
+name        = "PostgreSQL"
 description = "PostgreSQL (6.x/7.x)"
 
+# =============================================================================
+# GSD processor for PostgreSQL
+# =============================================================================
+class Processor (SQLProcessor):
 
-class Processor(BaseProcessor):
-
-
   MAX_NAME_LENGTH = 31
 
-  def number(self, object):
-    if object.precision == 0:
-      if object.length <= 4:
-        return "smallint"
-      elif object.length <= 9:
-        return "integer"
-      elif object.length <= 18:
-        return "bigint"
-      else:
-        return "decimal(%s,0)" % object.length
-    else:
-      return "numeric(%s,%s)" % (object.length + object.precision, 
object.precision)
+  # ---------------------------------------------------------------------------
+  # Process a GSField instance
+  # ---------------------------------------------------------------------------
+  def addField (self, tableDef, gsField):
+    field = self._qualify (gsField)
 
-  def string(self, object):
-    return "varchar(%s)" % object.length
+    # build a default value for this field
+    if gsField.defaultwith == "serial":
+      seq = self._getSequenceName (tableDef.name, gsField)
 
-  def date(self, object):
-    return "date"
+      tableDef.prologue.append ("CREATE SEQUENCE %s" % seq)
+      field += " DEFAULT nextval ('%s')" % seq
 
-  def time(self, object):
-    return "time"
+    elif gsField.defaultwith == "timestamp":
+      field += " DEFAULT now()"
 
-  def timestamp(self, object):
-    return "timestamp"
+    elif hasattr (gsField, "default") and len (gsField.default):
+      field += " DEFAULT %s" % gsField.default
 
-  def text(self, object):
-    return "text"
+    if not gsField.nullable:
+      field += " NOT NULL"
 
-  def key(self, object):
-    return "int8"
+    tableDef.fields.append (field)
 
 
-  def createField(self, name, tablename, datatype, object):
-    rv = '%s %s' % (name, datatype)
+  # ===========================================================================
+  # Datatype translation
+  # ===========================================================================
 
-    posttable = []
-    pretable = []
+  # ---------------------------------------------------------------------------
+  # A key field is of type 'int8'
+  # ---------------------------------------------------------------------------
+  def key (self, gsField):
+    return "int8"
 
-    if object.defaultwith == "serial":
+  # ---------------------------------------------------------------------------
+  # Keep text as 'text'
+  # ---------------------------------------------------------------------------
+  def text (self, gsField):
+    return "text"
 
-      sq = tablename + '_' + name + '_seq'
-      if len(sq) > self.MAX_NAME_LENGTH:
-        sq = tablename + "_seq_"
-        sq += str(object._parent._children.index(object))
-      if len(sq) > self.MAX_NAME_LENGTH:
-        sq = tablename + "_" + str(id(object)) + '_seq'
-      if len(sq) > self.MAX_NAME_LENGTH:
-        sq = 'seq_%s' % id(object)
+  # ---------------------------------------------------------------------------
+  # A number needs special treatment
+  # ---------------------------------------------------------------------------
+  def number (self, gsField):
+    if gsField.precision == 0:
+      if gsField.length <= 4:
+        return "smallint"
 
-      pretable.append ('CREATE SEQUENCE %s' % sq)
-      rv += " DEFAULT nextval('%s')" % sq
+      elif gsField.length <= 9:
+        return "integer"
 
-    elif object.defaultwith == "timestamp":
-      rv += " DEFAULT now()"
+      elif gsField.length <= 18:
+        return "bigint"
 
-    elif hasattr(object, 'default') and len(object.default):
-      rv += ' DEFAULT %s' % object.default
+      else:
+        return "decimal (%s,0)" % gsField.length
+    else:
+      return "numeric (%s,%s)" % (gsField.length + gsField.precision,
+                                  gsField.precision)
 
-    if not object.nullable:
-      rv += ' NOT NULL'
+  # ---------------------------------------------------------------------------
+  # Keep boolean as 'boolean'
+  # ---------------------------------------------------------------------------
+  def boolean (self, gsField):
+    return "boolean"
 
-    # Field def, Post-Field Def, Pre-Table command, Post-Table command
-    return (rv,None,pretable or None,posttable or None)
-





reply via email to

[Prev in Thread] Current Thread [Next in Thread]