[Top][All Lists]
[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)
-
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- r5097 - in trunk/gnue-common/src/schema/scripter: . processors,
johannes <=