commit-gnue
[Top][All Lists]
Advanced

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

[gnue] r7560 - in trunk/gnue-common/src/datasources: . drivers/Base driv


From: johannes
Subject: [gnue] r7560 - in trunk/gnue-common/src/datasources: . drivers/Base drivers/Base/Schema/Creation drivers/DBSIG2 drivers/appserver drivers/appserver/Schema drivers/appserver/appserver drivers/file drivers/interbase drivers/interbase/Schema drivers/interbase/interbase drivers/mysql drivers/mysql/Schema drivers/mysql/mysql drivers/postgresql drivers/postgresql/Base drivers/postgresql/Schema drivers/sqlite drivers/sqlite/Schema drivers/sqlite/sqlite
Date: Wed, 1 Jun 2005 07:27:30 -0500 (CDT)

Author: johannes
Date: 2005-06-01 07:27:27 -0500 (Wed, 01 Jun 2005)
New Revision: 7560

Added:
   trunk/gnue-common/src/datasources/GSchema.py
   trunk/gnue-common/src/datasources/drivers/Base/Behavior.py
   trunk/gnue-common/src/datasources/drivers/DBSIG2/Behavior.py
   trunk/gnue-common/src/datasources/drivers/appserver/Behavior.py
   trunk/gnue-common/src/datasources/drivers/interbase/Behavior.py
   trunk/gnue-common/src/datasources/drivers/mysql/Behavior.py
   trunk/gnue-common/src/datasources/drivers/postgresql/Behavior.py
   trunk/gnue-common/src/datasources/drivers/sqlite/Behavior.py
Removed:
   trunk/gnue-common/src/datasources/drivers/appserver/Schema/Discovery/
   trunk/gnue-common/src/datasources/drivers/interbase/Schema/Discovery/
   trunk/gnue-common/src/datasources/drivers/mysql/Schema/Discovery/
   trunk/gnue-common/src/datasources/drivers/postgresql/Schema/Discovery/
   trunk/gnue-common/src/datasources/drivers/sqlite/Schema/Discovery/
Modified:
   trunk/gnue-common/src/datasources/GConnections.py
   trunk/gnue-common/src/datasources/GDataSource.py
   trunk/gnue-common/src/datasources/drivers/Base/Connection.py
   trunk/gnue-common/src/datasources/drivers/Base/Schema/Creation/Creation.py
   trunk/gnue-common/src/datasources/drivers/Base/__init__.py
   trunk/gnue-common/src/datasources/drivers/DBSIG2/__init__.py
   trunk/gnue-common/src/datasources/drivers/appserver/appserver/Connection.py
   trunk/gnue-common/src/datasources/drivers/file/Base.py
   trunk/gnue-common/src/datasources/drivers/file/csvfile.py
   trunk/gnue-common/src/datasources/drivers/file/dbffile.py
   trunk/gnue-common/src/datasources/drivers/interbase/interbase/Connection.py
   trunk/gnue-common/src/datasources/drivers/mysql/mysql/Connection.py
   trunk/gnue-common/src/datasources/drivers/postgresql/Base/Connection.py
   trunk/gnue-common/src/datasources/drivers/sqlite/sqlite/Connection.py
Log:
Transformed Schema/Discovery/Introspection into Behavior


Modified: trunk/gnue-common/src/datasources/GConnections.py
===================================================================
--- trunk/gnue-common/src/datasources/GConnections.py   2005-06-01 11:45:13 UTC 
(rev 7559)
+++ trunk/gnue-common/src/datasources/GConnections.py   2005-06-01 12:27:27 UTC 
(rev 7560)
@@ -239,24 +239,12 @@
       parameters = self.getConnectionParameters (connection_base)
 
       driver   = parameters ['provider'].lower ().replace ('/', '.')
-      behavior = parameters.get ('behavior','').lower ().replace ('/', '.')
       dbdriver = plugin.find (driver, 'gnue.common.datasources.drivers',
                               'Connection')
       conn = dbdriver.Connection (self, connection_name, parameters)
 
       self._openConnections [connection_name] = conn
 
-      # Create the introspection instance
-      # TODO: add support of the behavior parameter. Does it describe a path to
-      #       the introspection module or is it the name of a provider whose
-      #       introspector would be used?
-      if hasattr (conn, 'behavior'):
-        behavior = conn.behavior
-      else:
-        behavior = conn.defaultBehavior
-
-      conn.introspector = behavior (conn)
-
     if login:
       self.loginToConnection (conn)
 

Modified: trunk/gnue-common/src/datasources/GDataSource.py
===================================================================
--- trunk/gnue-common/src/datasources/GDataSource.py    2005-06-01 11:45:13 UTC 
(rev 7559)
+++ trunk/gnue-common/src/datasources/GDataSource.py    2005-06-01 12:27:27 UTC 
(rev 7560)
@@ -1086,7 +1086,7 @@
     self._toplevelParent = self._type
 
   def getIntrospector (self):
-    return self._connection.introspector
+    return self._connection.behavior
 
   def getSchemaCreator (self):
     return self._connection.getSchemaCreator ()

Added: trunk/gnue-common/src/datasources/GSchema.py
===================================================================
--- trunk/gnue-common/src/datasources/GSchema.py        2005-06-01 11:45:13 UTC 
(rev 7559)
+++ trunk/gnue-common/src/datasources/GSchema.py        2005-06-01 12:27:27 UTC 
(rev 7560)
@@ -0,0 +1,864 @@
+# GNU Enterprise Common Library - Datasources - Schema support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+import sets
+import copy
+
+from gnue.common.definitions.GObjects import GObj
+from gnue.common.definitions.GRootObj import GRootObj
+from gnue.common.formatting import GTypecast
+from gnue.common.definitions import GParser
+
+
+xmlElements = None
+
+# =============================================================================
+# The base class for GNUe Schema Definitions
+# =============================================================================
+
+class GSObject (GObj):
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self, parent, objType, leafNode = False, **kwargs):
+
+    global xmlElements
+
+    GObj.__init__ (self, parent, objType)
+    self.isLeafNode = leafNode
+
+    # All remaining keyword arguments are incorporated as own attributes.
+    self.__dict__.update (kwargs)
+
+    # Get the sequence of 'assignable' members
+    if xmlElements is None:
+      xmlElements = getXMLelements ()
+    okey = objType [2:].lower ()
+    if okey in xmlElements:
+      self.MEMBERS = xmlElements [okey].get ('Attributes', {}).keys ()
+    else:
+      self.MEMBERS = []
+
+
+  # ---------------------------------------------------------------------------
+  # Assign a given set of attributes from another instance
+  # ---------------------------------------------------------------------------
+
+  def assign (self, source, recursive = False):
+    """
+    """
+
+    if self.__class__ != source.__class__:
+      raise AssignmentTypeError, (self, source)
+
+    for item in self.MEMBERS:
+      if hasattr (source, item):
+        setattr (self, item, getattr (source, item))
+
+    if recursive:
+      self._children = []
+
+      for child in source._children:
+        new = child.__class__ (None)
+        self.addChild (new)
+        new.setParent (self)
+
+        new.assign (child, recursive)
+    
+
+  def _setAction (self, obj, newAction):
+    obj._action = newAction
+
+
+  # ---------------------------------------------------------------------------
+  #
+  # ---------------------------------------------------------------------------
+
+  def diff (self, goal, maxIdLength = None):
+
+    result = None
+
+    mine   = {}
+    others = {}
+
+    # Build a mapping of our children
+    for child in self._children:
+      mine [child._id_ (maxIdLength)] = child
+
+    # find the counterpart of this instance
+    buddy = goal.findChildOfType (self._type, True, True)
+
+    # If there is no such object, all items in self are 'old' then
+    if buddy is None:
+      pass
+    else:
+      for child in buddy._children:
+        others [child._id_ (maxIdLength)] = child
+
+    print "-" * 60
+    print "_ID_ :", self._id_ (maxIdLength), repr (self)
+    print "MINE :", mine
+    print "OTHER:", others
+
+    # Find out new and changed items
+    for (key, item) in others.items ():
+      childDiff = None
+
+      if not key in mine:
+        if item._children or item.isLeafNode:
+          childDiff = item.__class__ (None)
+          childDiff.assign (item, True)
+          childDiff.walk (self._setAction, "add")
+
+      else:
+        # we need to find out wether the instances are changed then
+        childDiff = mine [key].diff (item, maxIdLength)
+        if childDiff is not None:
+          childDiff._action = "change"
+
+      if childDiff is not None:
+        if result is None:
+          result = self.__class__ (None)
+          result.assign (self)
+
+        result.addChild (childDiff)
+        childDiff.setParent (result)
+
+    # Finally find out all 'obsolete' ones
+    for (key, item) in mine.items ():
+      if not key in others:
+        if result is None:
+          result = self.__class__ (None)
+          result.assign (self)
+
+        child = item.__class__ (None)
+        child.assign (item, True)
+        child.walk (self._setAction, "remove")
+
+        result.addChild (child)
+        child.setParent (result)
+
+    if result is not None:
+      result._action = "change"
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Return a 'unique' and 'compareable' representation of an instance
+  # ---------------------------------------------------------------------------
+
+  def _id_ (self, maxIdLength = None):
+
+    if hasattr (self, 'name'):
+      result = self.name.lower ()
+      if maxIdLength is not None:
+        result = result [:maxIdLength]
+    else:
+      result = self._type
+
+    return result
+
+
+class GSImmutableCollection (GSObject):
+
+  def diff (self, goal, maxIdLength = None):
+
+    result = GSObject.diff (self, goal, maxIdLength)
+    if result is not None:
+      for item in result._children [:]:
+        # We cannot change a constraint directly, instead we remove the
+        # original and add the changed one
+        if item._action == 'change':
+          result._children.remove (item)
+
+          for old in self._children:
+            if old._id_ (maxIdLength) == item._id_ (maxIdLength):
+              add = old.__class__ (result)
+              add.assign (old, True)
+              add.walk (self._setAction, "remove")
+              break
+
+          for new in goal._children:
+            if new._id_ (maxIdLength) == item._id_ (maxIdLength):
+              add = new.__class__ (result)
+              add.assign (new, True)
+              add.walk (self._setAction, "add")
+              break
+
+    return result
+
+
+# =============================================================================
+# This class implements the object tree for schema definitions
+# =============================================================================
+
+class GSSchema (GRootObj, GSObject):
+
+  def __init__(self, parent = None):
+
+    GRootObj.__init__(self, 'schema', getXMLelements, __name__)
+    GSObject.__init__(self, parent, 'GSSchema')
+
+
+class GSTables (GSObject):
+  def __init__(self, parent, **params):
+    GSObject.__init__(self, parent, 'GSTables', **params)
+
+  def _id_ (self, maxIdLength = None):
+    # Don't mix up different type of table-collections
+    return "%s" % self.type
+
+
+class GSTable (GSObject):
+  def __init__(self, parent, **params):
+    GSObject.__init__(self, parent, 'GSTable', **params)
+
+class GSFields (GSObject):
+  def __init__(self, parent, **params):
+    GSObject.__init__(self, parent, 'GSFields', **params)
+
+
+class GSField (GSObject):
+  def __init__(self, parent, **params):
+    GSObject.__init__(self, parent, 'GSField', True, **params)
+
+class GSPrimaryKey (GSImmutableCollection):
+  def __init__ (self, parent, **params):
+    GSImmutableCollection.__init__(self, parent, 'GSPrimaryKey', **params)
+
+class GSPKField (GSObject):
+  def __init__ (self, parent, **params):
+    GSObject.__init__(self, parent, 'GSPKField', True, **params)
+
+class GSConstraints (GSImmutableCollection):
+  def __init__ (self, parent, **params):
+    GSImmutableCollection.__init__(self, parent, 'GSConstraints', **params)
+
+
+class GSConstraint(GSObject):
+  def __init__ (self, parent, **params):
+    GSObject.__init__ (self, parent, 'GSConstraint', **params)
+    self._inits.append (self._validate)
+    self.__tables = None
+
+
+  # ---------------------------------------------------------------------------
+  # Check a constraint definition
+  # ---------------------------------------------------------------------------
+
+  def _validate (self):
+    self.type = self.type.lower ()
+
+    try:
+      if not self.type in ["unique", "foreignkey"]:
+        raise ConstraintTypeError, self.type
+
+      return
+
+      # TODO: verify which constraint-checks are still usefull in here
+      csFields = self.findChildrenOfType ('GSConstraintField')
+      self.__checkFields (None, csFields)
+
+      if self.type == "foreignkey":
+        refFields = self.findChildrenOfType ('GSConstraintRef')
+        if refFields is None:
+          raise ConstraintRefError, self.name
+
+        self.__checkFields (refFields [0].table, refFields)
+
+        if len (refFields) <> len (csFields):
+          raise ConstraintFieldsError, self.name
+
+        self.__typeCheck (csFields, refFields)
+
+
+    except Exception, message:
+      print message
+      setErrorFlag (self)
+
+
+  # ---------------------------------------------------------------------------
+  # find a table definition in the object hierachy for @tablename
+  # ---------------------------------------------------------------------------
+
+  def __findTable (self, tablename = None):
+    # if no tablename is given we're looking for our parent table
+    if tablename is None:
+      return self.findParentOfType ('GSTable')
+
+    if self.__tables is None:
+      self.__tables = self.findParentOfType ('GSTables')
+
+    if self.__tables is not None:
+      for table in self.__tables.findChildrenOfType ('GSTable'):
+        if table.name == tablename:
+          return table
+
+    return None
+
+
+  # ---------------------------------------------------------------------------
+  # Check if the table 'tablename' has all fields listed in 'cFields'
+  # ---------------------------------------------------------------------------
+
+  def __checkFields (self, tablename, cFields):
+    """
+    This function raises an exception if the table @tablename has not all
+    fields listed in @cFields.
+    """
+    table = self.__findTable (tablename)
+    if table is None:
+      # if the table is not available in the GSD we assume it's ok
+      return
+
+    tbFields = table.findChildrenOfType ('GSField', True, True)
+
+    if len (cFields) > len (tbFields):
+      raise errors.ApplicationError, \
+          u_("Constraint '%(name)s' has more fields than the table 
'%(table)s'")\
+          % {'name' : self.name,
+             'table': table.name}
+
+    for check in cFields:
+      try:
+        for field in tbFields:
+          if field.name == check.name:
+            raise Exception ('found')
+
+      except:
+        pass
+
+      else:
+        raise errors.ApplicationError, \
+            u_("Table '%(table)s' has no field '%(field)s'.") \
+            % {'table': table.name,
+               'field': check.name}
+
+
+  # ---------------------------------------------------------------------------
+  # Check if both sides of a reference matches in type
+  # ---------------------------------------------------------------------------
+
+  def __typeCheck (self, csFields, refFields):
+    csTable = self.__findTable ()
+    rfTable = self.__findTable (refFields [0].table)
+
+    # if the referenced table is not available in the gsd, we assume it's ok
+    if rfTable is None:
+      return
+
+    rfFields = {}
+    myFields = {}
+
+    for item in csTable.findChildrenOfType ('GSField', True, True):
+      myFields [item.name] = item
+
+    for item in rfTable.findChildrenOfType ('GSField', True, True):
+      rfFields [item.name] = item
+
+
+    for ix in range (0, len (csFields)):
+      if myFields [csFields [ix].name].type != \
+         rfFields [refFields [ix].name].type:
+        raise errors.ApplicationError, \
+            u_("Constraint '%(name)s': typemismatch in reference field "
+               "'%(field)s'.") \
+            % {'name' : self.name,
+               'field': csFields [ix].name}
+
+
+class GSConstraintField (GSObject):
+  def __init__ (self, parent, **params):
+    GSObject.__init__(self, parent, 'GSConstraintField', True, **params)
+
+class GSConstraintRef (GSObject):
+  def __init__ (self, parent, **params):
+    GSObject.__init__(self, parent, 'GSConstraintRef', True, **params)
+
+class GSIndexes (GSImmutableCollection):
+  def __init__ (self, parent, **params):
+    GSImmutableCollection.__init__(self, parent, 'GSIndexes', **params)
+
+class GSIndex (GSObject):
+  def __init__ (self, parent, **params):
+    GSObject.__init__(self, parent, 'GSIndex', **params)
+
+class GSIndexField (GSObject):
+  def __init__ (self, parent, **params):
+    GSObject.__init__(self, parent, 'GSIndexField', True, **params)
+
+
+class GSData(GSObject):
+  def __init__(self, parent):
+    GSObject.__init__(self, parent, 'GSData')
+
+class GSTableData(GSObject):
+  def __init__(self, parent):
+    GSObject.__init__(self, parent, 'GSTableData')
+
+class GSRows(GSObject):
+  def __init__(self, parent):
+    GSObject.__init__(self, parent, 'GSRows')
+
+class GSRow(GSObject):
+  def __init__(self, parent):
+    GSObject.__init__(self, parent, 'GSRow')
+
+# =============================================================================
+# Field-Values
+# =============================================================================
+
+class GSValue(GSObject):
+  """
+  This class implements a single data value of a row-collection. On
+  Phase-I-init the values' datatype is determined - either by a matching
+  column-definition, or by a direct type-argument - and a native python object
+  is created for it.
+  """
+  def __init__(self, parent):
+    GSObject.__init__(self, parent, 'GSValue', True)
+    self.dataType  = None
+    self.length    = None
+    self.precision = None
+    self.value     = None
+
+    self._inits.append (self._validate)
+
+
+  def _validate (self):
+    if hasattr (self, "field"):
+      column = self._findColumn (self.field)
+    else:
+      column = self._findColumn (self._getIndex ())
+
+    try:
+      self.value = valueToNative (self, column)
+
+    except Exception, message:
+      self.value = None
+
+      print message
+      setErrorFlag (self)
+
+
+  # ---------------------------------------------------------------------------
+  # Find a column definition either by it's index or by it's fieldname
+  # ---------------------------------------------------------------------------
+
+  def _findColumn (self, fieldSpec):
+    """
+    This function searches for a given field in a GSTableData's column
+    definition. If @fieldSpec is an integer this number will be used as index
+    to the column-definition collection. Ohterwise @fieldSpec is treated as the
+    field name of the column to be returned.
+    """
+    tableData = self.findParentOfType ('GSTableData')
+    if tableData is not None:
+      colDefs = tableData.findChildOfType ('GSDefinition')
+      if colDefs is not None:
+        return colDefs.getColumn (fieldSpec)
+
+    return None
+
+
+  # ---------------------------------------------------------------------------
+  # get the index of an instance in it's parent's child-collection
+  # ---------------------------------------------------------------------------
+
+  def _getIndex (self):
+    """
+    This function get's the objects index in it's parents child-collection
+    """
+    parent = self.getParent ()
+    if parent is not None:
+      for index in range (0, len (parent._children)):
+        if parent._children [index] == self:
+          return index
+
+    raise errors.ApplicationError, \
+        u_("GSD-Error: can't find myself in the XML tree?!")
+
+
+
+class GSDescription(GSObject):
+  def __init__(self, parent):
+    GSObject.__init__(self, parent, 'GSDescription', True)
+
+
+
+# =============================================================================
+# Column definitions
+# =============================================================================
+
+class GSDefinition (GSObject):
+  """
+  GSDefinition holds a collection of column definition instances. In
+  Phase-I-init a dictionary with all columns is created as well as a sequence
+  of columns (for index-based access).
+  """
+  def __init__ (self, parent):
+    GSObject.__init__ (self, parent, "GSDefinition")
+    self.columns = {}
+    self.collist = []
+    self._inits.append (self.__buildColumnDict)
+
+  def __buildColumnDict (self):
+    self.collist = self.findChildrenOfType ('GSColumn')
+    self.columns = {}
+
+    for col in self.collist:
+      self.columns [col.field] = col
+
+
+  def getColumn (self, fieldSpec):
+    """
+    If @fieldSpec is an integer type '@fieldSpec' is treated as an index to the
+    columns-sequence, otherwise @fieldSpec is used as key into the
+    columns-dictionary.
+    """
+    if isinstance (fieldSpec, types.IntType) and fieldSpec < len 
(self.collist):
+      return self.collist [fieldSpec]
+
+    elif self.columns.has_key (fieldSpec):
+      return self.columns [fieldSpec]
+
+    return None
+
+
+# =============================================================================
+# Column definitions
+# =============================================================================
+
+class GSColumn (GSObject):
+  """
+  This class implements a column definition. Phase-I-init verifies the datatype
+  of the column definition and set's the properties 'typename', 'length' and
+  'precision'.
+  """
+  def __init__ (self, parent):
+    GSObject.__init__ (self, parent, "GSColumn", True)
+    self.typename  = None
+    self.length    = None
+    self.precision = None
+    self._inits.append (self._validate)
+
+  def _validate (self):
+    (self.typename, self.length, self.precision) = verifyDataType (self)
+
+
+# -----------------------------------------------------------------------------
+# recursively set an error flag in a object hierarchy
+# -----------------------------------------------------------------------------
+
+def setErrorFlag (aObject):
+  """
+  This function sets the property 'foundErrors' in an object and all its
+  parents.
+  """
+  if aObject is not None:
+    aObject.foundErrors = True
+    setErrorFlag (aObject.getParent ())
+
+
+# =============================================================================
+# load an XML object tree from a given stream and return it's root object
+# =============================================================================
+
+def loadFile (buffer, app = None, initialize = True):
+  """
+  This function loads an XML object tree from a given stream and return it's
+  root object.
+  """
+
+  return GParser.loadXMLObject (buffer, xmlSchemaHandler, 'GSSchema', 'schema',
+           initialize, attributes = {'_app': app})
+
+
+# =============================================================================
+# Build a dictionary tree with all available XML elements
+# =============================================================================
+
+def getXMLelements ():
+  """
+  Create a dictionary tree with all valid xml elements. This dictionary tree is
+  available via global variable 'xmlElements'
+  """
+
+  global xmlElements
+
+  if xmlElements is None:
+    xmlElements = {
+      'schema':       {
+         'BaseClass': GSSchema,
+         'Required': True,
+         'SingleInstance': True,
+         'Attributes':  {
+            'title':       {
+               'Typecast': GTypecast.text },
+            'author':       {
+               'Typecast': GTypecast.text },
+            'version':       {
+               'Typecast': GTypecast.text },
+            'description':       {
+               'Typecast': GTypecast.text } } ,
+         'ParentTags':  None },
+
+      'tables':  {
+         'BaseClass': GSTables,
+         'SingleInstance': False,
+         'Attributes': {
+           'type': {'Typecast': GTypecast.name,
+                    'Default' : 'table'},
+           'name': {'Typecast': GTypecast.name,
+                    'Default' : 'tables'} },
+         'ParentTags':  ('schema',) },
+
+      'table':    {
+         'BaseClass': GSTable,
+         'Importable': 1,
+         'Attributes': {
+            'name': {
+               'Required': 1,
+               'Unique': 1,
+               'Typecast': GTypecast.name },
+            'description': {
+               'Typecast': GTypecast.text },
+            'type': {
+               'Typecast': GTypecast.name },
+            'action': {
+               'Typecast': GTypecast.text,
+               'ValueSet': {
+                  'create': {},
+                  'extend': {} },
+               'Default': 'create' }},
+         'ParentTags':  ('tables',) },
+
+      'fields':  {
+         'BaseClass': GSFields,
+         'Importable': 1,
+         'SingleInstance': 1,
+         'ParentTags':  ('table',) },
+
+      'field':   {
+         'BaseClass': GSField,
+         'Importable': 1,
+         'Attributes': {
+            'name':          {
+               'Required': 1,
+               'Unique': 1,
+               'Typecast': GTypecast.name },
+            'description': {
+               'Typecast': GTypecast.text },
+            'type': {
+               'Required': 1,
+               'Typecast': GTypecast.name },
+            'length': {
+               'Typecast': GTypecast.whole },
+            'precision': {
+               'Typecast': GTypecast.whole,
+               'Default': 0 },
+            'nullable':     {
+               'Typecast': GTypecast.boolean,
+               'Default': 1 },
+            'default':     {
+               'Typecast': GTypecast.text },
+            'defaultwith':     {
+               'Typecast': GTypecast.text,
+               'ValueSet': {
+                  'constant': {},
+                  'timestamp': {},
+                  'serial': {} },
+               'Default': 'constant' },
+               },
+         'ParentTags':  ('fields',) },
+
+      'primarykey':   {
+         'BaseClass': GSPrimaryKey,
+         'SingleInstance': 1,
+         'Attributes': {
+            'name':        {
+               'Required': 1,
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('table',) },
+
+      'pkfield':   {
+         'BaseClass': GSPKField,
+         'Attributes': {
+            'name':        {
+               'Required': 1,
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('primarykey',) },
+
+      'constraints':   {
+         'BaseClass': GSConstraints,
+         'SingleInstance': 1,
+         'ParentTags':  ('table',) },
+
+      'constraint':    {
+         'BaseClass': GSConstraint,
+         'Attributes': {
+            'name': {
+               'Required': 1,
+               'Typecast': GTypecast.name },
+            'type': {
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('constraints',) },
+
+      'constraintfield':   {
+         'BaseClass': GSConstraintField,
+         'Attributes': {
+            'name':        {
+               'Required': 1,
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('constraint',) },
+
+      'constraintref':   {
+         'BaseClass': GSConstraintRef,
+         'Attributes': {
+            'name':        {
+               'Required': 1,
+               'Typecast': GTypecast.name },
+             'table':        {
+               'Required': 1,
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('constraint',) },
+
+      'indexes':   {
+         'BaseClass': GSIndexes,
+         'SingleInstance': 1,
+         'ParentTags':  ('table',) },
+      'index':    {
+         'BaseClass': GSIndex,
+         'Attributes': {
+            'name': {
+               'Required': 1,
+               'Typecast': GTypecast.name },
+            'unique': {
+               'Default': 0,
+               'Typecast': GTypecast.boolean } },
+         'ParentTags':  ('indexes',) },
+
+      'indexfield':   {
+         'BaseClass': GSIndexField,
+         'Attributes': {
+            'name':        {
+               'Required': 1,
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('index',) },
+
+      'data':   {
+         'BaseClass': GSData,
+         'SingleInstance': 1,
+         'ParentTags':  ('schema',) },
+
+      'tabledata':   {
+         'BaseClass': GSTableData,
+         'Importable': 1,
+         'Attributes': {
+            'name':        {
+               'Required': 1,
+               'Typecast': GTypecast.name },
+            'tablename':        {
+               'Required': 1,
+               'Typecast': GTypecast.name } },
+         'ParentTags':  ('data',) },
+
+      'definition': {
+        'BaseClass': GSDefinition,
+        'SingleInstance': True,
+        'ParentTags': ('tabledata',) },
+
+      'column': {
+        'BaseClass': GSColumn,
+        'Attributes': {
+          'field': {
+            'Required': True,
+            'Typecast': GTypecast.name },
+          'type': {
+            'Required': True,
+            'Typecast': GTypecast.name },
+          'key': {
+            'Typecast': GTypecast.boolean,
+            'Default' : False},
+          },
+        'ParentTags': ('definition',)},
+
+      'rows':   {
+         'BaseClass': GSRows,
+         'SingleInstance': 1,
+         'ParentTags':  ('tabledata',) },
+
+      'row':   {
+         'BaseClass': GSRow,
+         'ParentTags':  ('rows',) },
+
+      'value':   {
+         'BaseClass': GSValue,
+         'Attributes': {
+            'field':        {
+               'Required': 0,
+               'Typecast': GTypecast.name },
+            'type':        {
+               'Required': 0,
+               'Typecast': GTypecast.name,
+               'Default':  'text' },
+            'key': {
+               'Required': 0,
+               'Typecast': GTypecast.boolean,
+               'Default' : 0}
+            },
+         'ParentTags':  ('row',),
+         'MixedContent': 1,
+         'KeepWhitespace': 1},
+
+      'description':   {
+         'BaseClass': GSDescription,
+         'SingleInstance': 1,
+         'MixedContent': 1,
+         'UsableBySiblings': 1,
+         'ParentTags':  ('schema',) },
+
+    }
+
+  return GParser.buildImportableTags ('schema', xmlElements)
+
+
+# =============================================================================
+# Class called by the XML parser to process the XML file
+# =============================================================================
+
+class xmlSchemaHandler (GParser.xmlHandler):
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self):
+
+    GParser.xmlHandler.__init__(self)
+    self.xmlElements = getXMLelements ()
+
+


Property changes on: trunk/gnue-common/src/datasources/GSchema.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/gnue-common/src/datasources/drivers/Base/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/Behavior.py  2005-06-01 
11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/Base/Behavior.py  2005-06-01 
12:27:27 UTC (rev 7560)
@@ -0,0 +1,101 @@
+# GNU Enterprise Common Library - Base DB Driver - Schema behavior
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+from gnue.common.datasources import GSchema
+
+# =============================================================================
+# This class implements the basic schema introspection and creation support
+# =============================================================================
+
+class Behavior:
+
+  _maxIdLength = None                   # Maximum length of an identifier
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self, connection):
+
+    self.__connection = connection
+
+
+  # ---------------------------------------------------------------------------
+  # Read schema information from connection and return it as GSchema tree
+  # ---------------------------------------------------------------------------
+
+  def readSchema (self):
+    """
+    Retrieve the connection's schema information and return it as GSchema
+    object tree.
+
+    @return: current schema as GSchema object tree
+    """
+
+    result = GSchema.GSSchema ()
+
+    self._readSchema_ (result)
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  #
+  # ---------------------------------------------------------------------------
+
+  def _readSchema_ (self, parent):
+    """
+    """
+
+    return None
+
+
+  # ---------------------------------------------------------------------------
+  # Update the current connection's schema with the given schema
+  # ---------------------------------------------------------------------------
+
+  def writeSchema (self, schema):
+    """
+    Generate a command sequence to integrate the given schema tree into the
+    connection's current schema.
+
+    @param schema: GSchema object tree to be integrated in the connection's
+        current schema
+
+    @return: sequence of statements to be executed on the connection in order
+        to create/update the schema
+    """
+
+    current = self.readSchema ()
+    delta   = current.diff (schema, self._maxIdLength)
+
+    return self._writeSchema_ (delta)
+
+
+  # ---------------------------------------------------------------------------
+  #
+  # ---------------------------------------------------------------------------
+
+  def _writeSchema_ (self, delta):
+
+    return None


Property changes on: trunk/gnue-common/src/datasources/drivers/Base/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/gnue-common/src/datasources/drivers/Base/Connection.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/Connection.py        
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/Base/Connection.py        
2005-06-01 12:27:27 UTC (rev 7560)
@@ -68,10 +68,11 @@
 
   def __init__ (self, connections, name, parameters):
 
-    self.manager = connections
-    self.name = name
+    self.manager    = connections
+    self.name       = name
     self.parameters = parameters
-    self.__pending = False
+    self.__pending  = False
+    self.behavior   = self._getBehavior ()
 
 
   # ---------------------------------------------------------------------------
@@ -351,6 +352,7 @@
       return result
 
     try:
+      current       = self.behavior.readSchema ()
       workingSet    = copy.copy (definition)
       constraintSet = {}
 
@@ -363,12 +365,17 @@
       # occurence.
       for table in workingSet:
         # Do we have already a table with that name?
-        res = self.introspector.find (name = table ['name'])
+        res = None
+        for item in current.findChildrenOfType ('GSTable', False, True):
+          if item.name.lower () == table ['name'].lower ():
+            res = item
+            break
 
         if res is not None:
           method = schemaCreator.modifyTable
           # Please note: we keep existingFields sequence in all lowercase
-          existingFields = [f.name.lower () for f in res [0].fields ()]
+          existingFields = [f.name.lower () for f in \
+                               res.findChildrenOfType ('GSField', False, True)]
 
           # keep only new fields
           keep = []
@@ -387,21 +394,21 @@
             keep = []
 
             # if driver supports index-introspection we'll use it
-            if hasattr (res [0], 'indices'):
+            if res.findChildOfType ('GSIndexes') is not None:
               for index in table ['indices']:
                 oldIndex = None
-                for (name, value) in res [0].indices.items ():
-                  if name.lower () == index ['name'].lower ():
+                for item in res.findChildrenOfType ('GSIndex', False, True):
+                  if item.name.lower () == index ['name'].lower ():
                     oldIndex = value
 
                 if oldIndex is None:
                   keep.append (index)
                 else:
-                  old = [f.lower () for f in oldIndex ['fields']]
+                  old = [f.lower () for f in \
+                      oldIndex.findChildrenOfType ('GSIndexField', False, 
True)]
                   new = [f.lower () for f in index ['fields']]
 
-                  if oldIndex ['unique'] != index ['unique'] or old != new:
-
+                  if oldIndex.unique != index ['unique'] or old != new:
                     # make sure the backend has a possibility to remove the old
                     # index before changing it
                     if not table.has_key ('old_indices'):
@@ -468,6 +475,47 @@
 
 
   # ---------------------------------------------------------------------------
+  # Return the current schema information of the connection's backend
+  # ---------------------------------------------------------------------------
+
+  def readSchema (self):
+    """
+    Return the schema information of the connection's backend or None if no
+    behavior instance is available.
+
+    @return: GSchema object tree with the current schema or None
+    """
+
+    if self.behavior is not None:
+      return self.behavior.readSchema ()
+    else:
+      return None
+
+
+  # ---------------------------------------------------------------------------
+  # Write a given schema to the connection's backend
+  # ---------------------------------------------------------------------------
+
+  def writeSchema (self, schema, codeOnly = False):
+    """
+    Update the connection's backend database schema with the given schema
+    object tree.
+
+    @param schema: GSchema object tree defining the schema to be integrated
+    @param codeOnly: if True, create only the command sequence. No integration
+        takes place.
+
+    @return: command sequence which could be used to integrate the given schema
+    """
+
+    if self.behavior is not None:
+      commands = self.behavior.writeSchema (schema)
+
+    else:
+      return []
+
+
+  # ---------------------------------------------------------------------------
   # Virtual methods to be implemented by descendants
   # ---------------------------------------------------------------------------
 
@@ -647,7 +695,37 @@
     """
     pass
 
+  # ---------------------------------------------------------------------------
 
+  def _getBehavior (self):
+    """
+    Create an apropriate behavior instance. If the connection defines a
+    '_behavior' attribute this class will be instanciated. If the connection's
+    parameters contain the key 'behavior' this class will be used. If no such
+    parameter is defined but the connection has a 'defaultBehavior' attribute
+    set, this class will be used.
+
+    @return: behavior instance or None if no such class is available
+    """
+
+    aClass = None
+
+    if hasattr (self, '_behavior'):
+      aClass = self._behavior
+
+    elif self.parameters.get ('behavior') is not None:
+      aClass = plugin.find (self.parameters ['behavior'],
+                         'gnue.common.datasources.drivers', 'Behavior')
+
+    elif self.defaultBehavior is not None:
+      aClass = self.defaultBehavior
+
+    if aClass:
+      return aClass (self)
+    else:
+      return None
+
+
   # ---------------------------------------------------------------------------
   # Nice string representation
   # ---------------------------------------------------------------------------

Modified: 
trunk/gnue-common/src/datasources/drivers/Base/Schema/Creation/Creation.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/Schema/Creation/Creation.py  
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/Base/Schema/Creation/Creation.py  
2005-06-01 12:27:27 UTC (rev 7560)
@@ -133,13 +133,13 @@
   # Constructor
   # ---------------------------------------------------------------------------
 
-  def __init__ (self, connection = None, introspector = None):
+  def __init__ (self, connection = None, behavior = None):
     self.connection   = connection
-    self.introspector = introspector
+    self.behavior = behavior
     self.lookup       = {}
 
-    if connection is not None and introspector is None:
-      self.introspector = connection.introspector
+    if connection is not None and behavior is None:
+      self.behavior = connection.behavior
 
 
   # ---------------------------------------------------------------------------
@@ -293,26 +293,6 @@
 
 
   # ---------------------------------------------------------------------------
-  # Check wether an element exists or not
-  # ---------------------------------------------------------------------------
-
-  def exists (self, elementName, elementType = None):
-    """
-    This function examines, wether an element exists in a datamodel or not.
-    It's doing this using the given introspector. If no introspecor is
-    available the result is FALSE.
-
-    @param elementName: name of the element to be examined
-    @param elementType: type of the element to be examined (optional)
-    @return: TRUE if the element was found, otherwise FALSE
-    """
-    if self.introspector is not None:
-      return self.introspector.find (name = elementName, type = elementType)
-    else:
-      return False
-
-
-  # ---------------------------------------------------------------------------
   # Validate a given table definition
   # ---------------------------------------------------------------------------
 
@@ -349,7 +329,7 @@
     This function releases all circular references held by the creator instance
     """
 
-    self.introspector = None
+    self.behavior = None
     self.connection   = None
     self.lookup       = {}
 

Modified: trunk/gnue-common/src/datasources/drivers/Base/__init__.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/__init__.py  2005-06-01 
11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/Base/__init__.py  2005-06-01 
12:27:27 UTC (rev 7560)
@@ -31,3 +31,4 @@
 from Connection import Connection
 from ResultSet import ResultSet
 from RecordSet import RecordSet
+from Behavior import Behavior

Added: trunk/gnue-common/src/datasources/drivers/DBSIG2/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/DBSIG2/Behavior.py        
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/DBSIG2/Behavior.py        
2005-06-01 12:27:27 UTC (rev 7560)
@@ -0,0 +1,29 @@
+# GNU Enterprise Common Library - DBSIG2 drivers - Schema Support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+from gnue.common.datasources.drivers import Base
+from gnue.common.datasources import GSchema
+
+class Behavior (Base.Behavior):
+  pass
+


Property changes on: 
trunk/gnue-common/src/datasources/drivers/DBSIG2/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/gnue-common/src/datasources/drivers/DBSIG2/__init__.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/DBSIG2/__init__.py        
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/DBSIG2/__init__.py        
2005-06-01 12:27:27 UTC (rev 7560)
@@ -24,3 +24,4 @@
 
 from Connection import *
 from ResultSet import *
+from Behavior import *

Added: trunk/gnue-common/src/datasources/drivers/appserver/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/appserver/Behavior.py     
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/appserver/Behavior.py     
2005-06-01 12:27:27 UTC (rev 7560)
@@ -0,0 +1,180 @@
+# GNU Enterprise Common Library - Application Server Driver - Schema Support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+from gnue.common.datasources.drivers import Base
+from gnue.common.datasources import GSchema
+
+
+# =============================================================================
+#
+# =============================================================================
+
+class Behavior (Base.Behavior):
+  """
+  Limitations:
+
+  * Appserver cannot reproduce indices
+  """
+
+  _RELTYPE = {'type': 'object', 'name': u_('Business Object Class')}
+
+  # ---------------------------------------------------------------------------
+  # Read the connection's schema
+  # ---------------------------------------------------------------------------
+
+  def _readSchema_ (self, parent):
+    """
+    Read the connection's schema and build a GSchema object tree connected to
+    the given parent object (which is of type GSSchema).
+    """
+
+    tables = self.__readTables (parent)
+    self.__readFields (tables)
+    self.__addConstraints (tables)
+
+    # We read calculated fields after building the constraints, because this
+    # way a field having a reference type does not introduce a non-existing
+    # constraint.
+    self.__readCalculatedFields (tables)
+
+
+  # ---------------------------------------------------------------------------
+  # Read all classes
+  # ---------------------------------------------------------------------------
+
+  def __readTables (self, parent):
+
+    sm  = self.__connection._sm
+    sid = self.__connection._sess_id
+    lid = sm.request (sid, u"gnue_class", [], [u"gnue_module.gnue_name",
+                  u"gnue_name"], [u"gnue_module.gnue_name", u"gnue_name"])
+
+    result = {}
+    for (gid, module, name) in sm.fetch (sid, lid, 0, sm.count (sid, lid)):
+      fullname = "%s_%s" % (module, name)
+      master = parent.findChildOfType ('GSTables') or \
+                                     GSchema.GSTables (parent, **self._RELTYPE)
+      result [gid] = GSchema.GSTable (master, name = fullname, id = gid)
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read all properties
+  # ---------------------------------------------------------------------------
+
+  def __readFields (self, tables):
+    
+    sm  = self.__connection._sm
+    sid = self.__connection._sess_id
+
+    sort  = [u"gnue_module", u"gnue_name"]
+    props = [u"gnue_module.gnue_name", u"gnue_class", u"gnue_name",
+             u"gnue_length", u"gnue_nullable", u"gnue_scale", u"gnue_type"]
+    lid = sm.request (sid, u"gnue_property", [], sort, props)
+
+    result = {}
+    for record in sm.fetch (sid, lid, 0, sm.count (sid, lid)):
+      (gid, module, cid, name, length, nullable, scale, ftype) = record
+
+      result [gid] = self.__createField (tables, gid, module, cid, name,
+                                         length, nullable, scale, ftype)
+
+
+  # ---------------------------------------------------------------------------
+  # Add all kind of constraints
+  # ---------------------------------------------------------------------------
+
+  def __addConstraints (self, tables):
+
+    for (gid, table) in tables.items ():
+      # Add the primary key constraint, as it is 'well known'
+      pk = GSchema.GSPrimaryKey (table, name = u"pk_%s" % table.name)
+      GSchema.GSPKField (pk, name = u"gnue_id")
+
+      # Iterate over all fields and create a foreign key constraint for
+      # reference type properties
+      for field in table.findChildrenOfType ('GSField', False, True):
+        if '_' in field.nativetype:
+          master = table.findChildOfType ('GSConstraints') or \
+                                                  GSchema.GSConstraints (table)
+          constraint = GSchema.GSConstraint (master,
+              name = "fk_%s_%s" % (table.name, field.nativetype),
+              type = 'foreignkey')
+
+          GSchema.GSConstraintField (constraint, name = field.name)
+          GSchema.GSConstraintRef (constraint, name = u"gnue_id",
+                                   table = field.nativetype)
+
+
+  # ---------------------------------------------------------------------------
+  # Read all calculated fields
+  # ---------------------------------------------------------------------------
+
+  def __readCalculatedFields (self, tables):
+    
+    sm  = self.__connection._sm
+    sid = self.__connection._sess_id
+
+    sort  = [u"gnue_module", u"gnue_class", u"gnue_name"]
+    props = [u"gnue_module.gnue_name", u"gnue_class", u"gnue_name",
+             u"gnue_length", u"gnue_nullable", u"gnue_scale", u"gnue_type"]
+    cond  = ['and', ['like',    ['field', u'gnue_name'], ['const', u'get%']],
+                    ['notnull', ['field', u'gnue_type']]]
+    lid = sm.request (sid, u"gnue_procedure", cond, sort, props)
+
+    result = {}
+    for record in sm.fetch (sid, lid, 0, sm.count (sid, lid)):
+      (gid, module, cid, name, length, nullable, scale, ftype) = record
+
+      result [gid] = self.__createField (tables, gid, module, cid, name [3:],
+                                         length, nullable, scale, ftype)
+
+
+  # ---------------------------------------------------------------------------
+  # Create a new GSField instance from the given arguments
+  # ---------------------------------------------------------------------------
+
+  def __createField (self, tables, gid, module, cid, name, length, nullable,
+                     scale, ftype):
+
+    table  = tables [cid]
+    master = table.findChildOfType ('GSFields') or GSchema.GSFields (table)
+
+    if '_' in ftype or ftype == 'id':
+      dtype  = 'string'
+      length = 32
+    else:
+      dtype = ftype
+
+    attrs = {'id'        : gid,
+             'name'      : "%s_%s" % (module, name),
+             'type'      : dtype,
+             'nativetype': ftype,
+             'nullable'  : nullable}
+    if length:
+      attrs ['length'] = length
+    if scale:
+      attrs ['precision'] = scale
+
+    return GSchema.GSField (master, **attrs)


Property changes on: 
trunk/gnue-common/src/datasources/drivers/appserver/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: 
trunk/gnue-common/src/datasources/drivers/appserver/appserver/Connection.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/appserver/appserver/Connection.py 
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/appserver/appserver/Connection.py 
2005-06-01 12:27:27 UTC (rev 7560)
@@ -30,7 +30,7 @@
 
 from ResultSet import ResultSet
 
-from gnue.common.datasources.drivers.appserver.Schema.Discovery.Introspection 
import Introspection
+from gnue.common.datasources.drivers.appserver import Behavior
 
 
 # =============================================================================
@@ -43,8 +43,8 @@
   """
 
   _resultSetClass   = ResultSet
-  defaultBehavior   = Introspection
   _primarykeyFields = [u'gnue_id']
+  _behavior         = Behavior.Behavior
 
 
   # ---------------------------------------------------------------------------
@@ -56,7 +56,7 @@
     Base.Connection.__init__ (self, connections, name, parameters)
     self.__filters = None
     self.__server  = None
-    self._sm      = None
+    self._sm       = None
 
 
   # ---------------------------------------------------------------------------

Modified: trunk/gnue-common/src/datasources/drivers/file/Base.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/Base.py      2005-06-01 
11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/file/Base.py      2005-06-01 
12:27:27 UTC (rev 7560)
@@ -29,57 +29,51 @@
 from gnue import paths
 from gnue.common.datasources import GIntrospection
 from gnue.common.datasources.drivers import Base
+from gnue.common.datasources import GSchema
 
 
 # =============================================================================
-# Introspection class
+# Schema support class
 # =============================================================================
 
-class Introspection (GIntrospection.Introspection):
+class Behavior (Base.Behavior):
 
   # ---------------------------------------------------------------------------
-  # Constructor
+  # Read the connection's schema
   # ---------------------------------------------------------------------------
 
-  def __init__ (self, connection):
+  def _readSchema_ (self, parent):
+    """
+    Read the connection's schema and build a GSchema object tree connected to
+    the given parent object (which is of type GSSchema).
+    """
 
-    GIntrospection.Introspection.__init__ (self, connection)
-    self.types = [('table', _('Tables'), 1)]
+    filename = self.__connection.getFilename ('*')
 
+    for fname in glob.glob (filename):
+      tables = self.__connection._listTables (fname)
+      if tables is not None:
+        master = parent.findChildOfType ('GSTables')
+        if master is None:
+          parent.addChild (tables)
+          tables.setParent (parent)
+        else:
+          for table in tables.findChildrenOfType ('GSTable', False, True):
+            master.addChild (table)
+            table.setParent (master)
 
-  # ---------------------------------------------------------------------------
-  # Find a schema element by name and/or type
-  # ---------------------------------------------------------------------------
+    # Add all fields, indices and constraints to the tables
+    for table in parent.findChildrenOfType ('GSTable', False, True):
+      for item in [self.__connection._listFields (table),
+                   self.__connection._listPrimaryKey (table),
+                   self.__connection._listIndices (table),
+                   self.__connection._listConstraints (table)]:
+        if item is not None:
+          table.addChild (item)
+          item.setParent (table)
 
-  def find (self, name = None, type = None):
+  
 
-    if name:
-      filename = self._connection.getFilename (name)
-    else:
-      filename = self._connection.getFilename ('*')
-
-    result = []
-
-    for file in glob.glob (filename):
-      for table in self._connection._listTables (file):
-        attrs = {'id': table, 'name': table, 'type': 'table', 'file': file}
-        result.append (GIntrospection.Schema (
-          attrs = attrs, getChildSchema = self.__getChildSchema))
-    return result
-
-
-  # ---------------------------------------------------------------------------
-  # Get all fields of a table
-  # ---------------------------------------------------------------------------
-
-  def __getChildSchema (self, parent):
-
-    result = []
-    for attrs in self._connection._listFields (parent.file, parent.name):
-      result.append (GIntrospection.Schema (attrs = attrs))
-    return result
-
-
 # =============================================================================
 # ResultSet class
 # =============================================================================
@@ -126,7 +120,7 @@
   """
 
   _resultSetClass = ResultSet
-  defaultBehavior = Introspection
+  _behavior       = Behavior
 
 
   # ---------------------------------------------------------------------------
@@ -193,9 +187,11 @@
     """
     List all the table names contained in the given file.
 
-    This function must return a list (or any other iterable object) with table
-    names. Table names can be mixed case.
+    This function must return either None if there are no tables or a GSTables
+    instance containing all tables available.
 
+    Table names can be mixed case.
+
     The default behaviour of this function is to extract the table name from
     the file name (if the filename parameter of the connection contains a
     '%(table)s') and return 'data' otherwise.
@@ -206,44 +202,101 @@
     This method is used for introspection.
 
     @param filename: Filename
-    @return: List of table names
+    @return: GSTables instance or None
     """
+
     f = self.getFilename ('\n')
     if '\n' in f:
       (prefix, postfix) = f.split ('\n', 1)
       if filename [:len (prefix)] == prefix and \
          filename [-len (postfix):] == postfix:
-        return [filename [len (prefix):-len (postfix)]]
+        table = filename [len (prefix):-len (postfix)]
       else:
-        return [filename]
+        table = filename
     else:
-      return ['data']
+      table = 'data'
 
+    result = GSchema.GSTables (None, type = 'table', name = _('Tables'))
+    GSchema.GSTable (result, id = table, name = table, filename = filename)
+
+    return result
+
   # ---------------------------------------------------------------------------
 
-  def _listFields (self, filename, table):
+  def _listFields (self, table):
     """
-    List all the field names.
+    List all the field names available for a table.
 
-    This function must return a list (or any other iterable object) with a
-    dictionary per field.  The dictionary must have the following keys:
-    id (unique field identifier), name (field name), type (field type, usually
-    'field'), nativetype (native data type), datatype (GNUe data type), and
-    required (Boolean with True if the field is non-nullable).
+    This function must return either None if there are no fields available, or
+    a GSFields instance containing all fields. The GSFields instance must *not*
+    have a parent.
 
     Field names can be mixed case.
 
     This method is used for introspection.
 
-    @param filename: Filename
-    @param table: Table name (only useful if a file can contain more than one
-      table)
-    @return: List of dictionaries as explained above
+    @param table: GSTable instance to get the fields for
+    @return: GSFields instance or None
     """
-    return []
 
+    return None
+
   # ---------------------------------------------------------------------------
 
+  def _listIndices (self, table):
+    """
+    List all available indices for a table.
+
+    This function must return a GSIndexes instance with all available indices
+    or None if there are no indices at all. The GSIndexes instance must *not*
+    have a parent.
+
+    This method is used for introspection.
+
+    @param table: GSTable instance to get the fields for
+    @return: GSIndexes instance or None
+    """
+
+    return None
+
+  # ---------------------------------------------------------------------------
+
+  def _listPrimaryKey (self, table):
+    """
+    List the primary key for a table.
+
+    This function must return a GSPrimaryKey instance holding the primary key
+    definition for the table or None if it has no such key. The GSPrimaryKey
+    instance must *not* have a parent.
+
+    This method is used for introspection.
+
+    @param table: GSTable instance to get the fields for
+    @return: GSPrimaryKey instance or None
+    """
+
+    return None
+
+  # ---------------------------------------------------------------------------
+
+  def _listConstraints (self, table):
+    """
+    List all available constraints for a table.
+
+    This function must return a GSConstraints instance with all available
+    constraints or None if there are no constraints at all. The GSConstraints
+    instance must *not* have a parent.
+
+    This method is used for introspection.
+
+    @param table: GSTable instance to get the fields for
+    @return: GSConstraints instance or None
+    """
+
+    return None
+
+  # ---------------------------------------------------------------------------
+
   def _loadFile (self, filename, table):
     """
     Load data from a file.

Modified: trunk/gnue-common/src/datasources/drivers/file/csvfile.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/csvfile.py   2005-06-01 
11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/file/csvfile.py   2005-06-01 
12:27:27 UTC (rev 7560)
@@ -24,6 +24,7 @@
 import csv
 
 from gnue.common.datasources.drivers.file import Base
+from gnue.common.datasources import GSchema
 
 
 # =============================================================================
@@ -45,13 +46,13 @@
 
     # Let the sniffer determine the file format
     sniffer = csv.Sniffer ()
-    dialect = sniffer.sniff (f.read ())
+    dialect = sniffer.sniff (f.readline ())
 
     # Rewind file
     f.seek (0)
 
     # Read the first row to get the field names
-    fieldnames = (csv.reader (f, dialect = dialect)).next ()
+    fieldnames = (csv.reader (f, dialect)).next ()
 
     return (f, dialect, fieldnames)
 
@@ -60,19 +61,21 @@
   # Iterate through the list of field names
   # ---------------------------------------------------------------------------
 
-  def _listFields (self, filename, table):
+  def _listFields (self, table):
 
-    (f, dialect, fieldnames) = self.__prepareFile (filename)
+    result = GSchema.GSFields (None)
+    (f, dialect, fieldnames) = self.__prepareFile (table.filename)
 
     for fieldname in fieldnames:
-      yield {'id':         fieldname,
-             'name':       fieldname,
-             'type':       'field',
-             'nativetype': 'text',
-             'datatype':   'text',
-             'required':    False}
+      GSchema.GSField (result, id = fieldname,
+                               name = fieldname,
+                               type = 'string',
+                               nativetype = 'string',
+                               nullable = True)
 
+    return result
 
+
   # ---------------------------------------------------------------------------
   # Load the file
   # ---------------------------------------------------------------------------

Modified: trunk/gnue-common/src/datasources/drivers/file/dbffile.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/dbffile.py   2005-06-01 
11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/file/dbffile.py   2005-06-01 
12:27:27 UTC (rev 7560)
@@ -22,6 +22,7 @@
 # $Id$
 
 from gnue.common.datasources.drivers.file import Base
+from gnue.common.datasources import GSchema
 from gnue.common.utils import dbf
 
 
@@ -34,7 +35,7 @@
   Connection class for DBF file backends.
   """
 
-  __datatypes = {'C': 'text',
+  __datatypes = {'C': 'string',
                  'N': 'number',
                  'D': 'date',
                  'L': 'boolean'}
@@ -44,20 +45,28 @@
   # Iterate through the list of field names
   # ---------------------------------------------------------------------------
 
-  def _listFields (self, filename, table):
+  def _listFields (self, table):
 
-    f = dbf.dbf (filename)
+    result = GSchema.GSFields (None)
 
-    for field in f.fields:
-      yield {'id':         field [0],
-             'name':       field [0],
-             'type':       'field',
-             'nativetype': field [1],
-             'datatype':   self.__datatypes [field [1]],
-             'length':     field [2],
-             'required':    False}
+    f = dbf.dbf (table.filename)
 
+    for (name, ftype, length, scale, index) in f.fields:
+      field = GSchema.GSField (result, id = name,
+                                       name = name,
+                                       nativetype = ftype,
+                                       type = self.__datatypes [ftype],
+                                       nullable = True)
+      # Date  and boolean types have no length and scale
+      if not ftype in ['D', 'L']:
+        if length:
+          field.length = length
+        if scale:
+          field.precision = scale
 
+    return result
+
+
   # ---------------------------------------------------------------------------
   # Load the file
   # ---------------------------------------------------------------------------

Added: trunk/gnue-common/src/datasources/drivers/interbase/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/interbase/Behavior.py     
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/interbase/Behavior.py     
2005-06-01 12:27:27 UTC (rev 7560)
@@ -0,0 +1,311 @@
+# GNU Enterprise Common Library - Interbase/Firebird driver - Schema support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+import re
+
+from gnue.common.datasources.drivers import Base
+from gnue.common.datasources import GSchema
+
+# =============================================================================
+# This class implements schema support for Interbase/Firebird database backends
+# =============================================================================
+
+class Behavior (Base.Behavior):
+  """
+  Limitations:
+
+  * Interbase/Firebird has no native boolean datatype. That's why this
+    introspection module treats the domain 'BOOLEAN' as boolean data types.
+  """
+
+  _RELTYPE  = {False: {'type': 'table', 'name': _('Tables')},
+               True : {'type': 'view' , 'name': _('Views')}}
+
+  _TYPEMAP  = {'DATE': 'date', 'TIME': 'time', 'TIMESTAMP': 'datetime'}
+  _NOW      = re.compile ("'(NOW\s*\(\)\s*)'", re.IGNORECASE)
+  _GENFIELD = re.compile ('^.*NEW\.(\w+)\s*=\s*GEN_ID\s*\(.*\)', re.IGNORECASE)
+
+
+  # ---------------------------------------------------------------------------
+  # Read the current connection's schema
+  # ---------------------------------------------------------------------------
+
+  def _readSchema_ (self, parent):
+    """
+    """
+
+    tables = self.__readTables (parent)
+    fields = self.__readFields (tables)
+    constr = self.__readConstraints (tables)
+    self.__readKeys (tables, constr)
+    self.__readSerials (tables)
+
+
+  # --------------------------------------------------------------------------
+  # Read all tables and views
+  # --------------------------------------------------------------------------
+
+  def __readTables (self, parent):
+
+    cmd = u"SELECT rdb$relation_name, rdb$view_source FROM RDB$RELATIONS " \
+           "WHERE rdb$system_flag = 0 ORDER BY rdb$relation_name"
+
+    result  = {}
+    masters = {}
+    cursor  = self.__connection.makecursor (cmd)
+
+    for rs in cursor.fetchall ():
+      (name, source) = self.__stripStrings (rs)
+      reltype = self._RELTYPE [source is not None]
+      if not reltype ['type'] in masters:
+        masters [reltype ['type']] = GSchema.GSTables (parent, **reltype)
+
+      result [name] = GSchema.GSTable (masters [reltype ['type']], name = name)
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read all fields of the given tables
+  # ---------------------------------------------------------------------------
+
+  def __readFields (self, tables):
+
+    result = {}
+
+    cmd = u"SELECT rf.rdb$relation_name, rf.rdb$field_name, tp.rdb$type_name," 
\
+           "  rf.rdb$null_flag, rf.rdb$default_source, fs.rdb$field_length, " \
+           "  fs.rdb$field_scale, fs.rdb$field_precision, " \
+           "  fs.rdb$character_length, rf.rdb$field_source " \
+           "FROM rdb$relation_fields rf, rdb$fields fs, rdb$types tp " \
+           "WHERE " \
+             "fs.rdb$field_name = rf.rdb$field_source AND " \
+             "tp.rdb$type = fs.rdb$field_type AND " \
+             "tp.rdb$field_name = 'RDB$FIELD_TYPE'" \
+           "ORDER BY rf.rdb$relation_name, rf.rdb$field_position"
+
+    cursor = self.__connection.makecursor (cmd)
+    try:
+      for rs in cursor.fetchall ():
+        (table, name, ftype, null, default, flen, scale, prec, clen, fsrc) = \
+            self.__stripStrings (rs)
+
+        if not table in tables:
+          continue
+
+        nativetype = ftype
+        attrs = {'id'        : "%s.%s" % (table, name),
+                 'name'      : name,
+                 'nativetype': nativetype,
+                 'nullable'  : not null}
+
+        if fsrc == 'BOOLEAN':
+          attrs ['type'] = 'boolean'
+
+        elif nativetype in self._TYPEMAP:
+          attrs ['type'] = self._TYPEMAP [nativetype]
+
+        elif nativetype in ['DOUBLE', 'FLOAT', 'INT64', 'LONG', 'QUAD', \
+            'SHORT']:
+          attrs ['type']      = 'number'
+          if prec == 0 and scale == 0:
+            attrs ['length'] = len ("%s" % 2L ** (flen * 8))
+          else:
+            attrs ['length']    = prec
+            attrs ['precision'] = abs (scale)
+
+        else:
+          attrs ['type']   = 'string'
+          attrs ['length'] = clen
+
+        if default is not None:
+          if self._NOW.search (default) is not None:
+            attrs ['defaultwith'] = 'timestamp'
+          else:
+            attrs ['defaultwith'] = 'constant'
+            attrs ['defaultl']    = default [8:]
+
+        fields = tables [table].findChildOfType ('GSFields') or \
+                 GSchema.GSFields (tables [table])
+
+        result [attrs ['id']] = GSchema.GSField (fields, **attrs)
+
+    finally:
+      cursor.close ()
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read all relation constraints (pk/fk only)
+  # ---------------------------------------------------------------------------
+
+  def __readConstraints (self, tables):
+
+    cmd = u"SELECT rdb$relation_name, rdb$constraint_name, " \
+           "   rdb$constraint_type, rdb$index_name " \
+           "FROM rdb$relation_constraints " \
+           "WHERE rdb$constraint_type IN ('PRIMARY KEY', 'FOREIGN KEY') "
+
+    result = {}
+    cursor = self.__connection.makecursor (cmd)
+    try:
+      for rs in cursor.fetchall ():
+        (tname, name, ctype, index) = self.__stripStrings (rs)
+
+        table = tables.get (tname)
+        if table is None:
+          continue
+
+        if ctype == 'PRIMARY KEY':
+          item = GSchema.GSPrimaryKey (table, name = name)
+        else:
+          cons = table.findChildOfType ('GSConstraints') or \
+                           GSchema.GSConstraints (table)
+          item = GSchema.GSConstraint (cons, name = name, type = 'foreignkey')
+
+        result ["%s.%s" % (tname, index)] = item
+
+    finally:
+      cursor.close ()
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read indices and populate constraints
+  # ---------------------------------------------------------------------------
+
+  def __readKeys (self, tables, constraints):
+
+    fields = {}    # Map index to fields
+    relmap = {}    # Map index to relations
+    update = {}    # Map FK-Relations to be updated after the first run
+
+    # First build a mapping of all index fields
+    cmd = u"SELECT rdb$index_name, rdb$field_name FROM rdb$index_segments " \
+           "ORDER BY rdb$index_name, rdb$field_position"
+
+    cursor = self.__connection.makecursor (cmd)
+
+    try:
+      for (index, field) in cursor.fetchall ():
+        seq = fields.setdefault (index.strip (), [])
+        seq.append (field.strip ())
+
+    finally:
+      cursor.close ()
+
+    # Now build up the indices and populate the constraints
+    cmd = u"SELECT rdb$index_name, rdb$relation_name, rdb$unique_flag, " \
+           "  rdb$foreign_key " \
+           "FROM rdb$indices " \
+           "WHERE (rdb$index_inactive IS NULL or rdb$index_inactive = 0) " \
+           "ORDER BY rdb$relation_name, rdb$index_id"
+
+    cursor = self.__connection.makecursor (cmd)
+    try:
+      for rs in cursor.fetchall ():
+        (iname, tname, unique, fkey) = self.__stripStrings (rs)
+        if not tname in tables:
+          continue
+
+        relmap [iname] = tname
+        table = tables [tname]
+
+        constraint = constraints.get ("%s.%s" % (tname, iname))
+        if constraint is None:
+          ind = table.findChildOfType ('GSIndexes') or GSchema.GSIndexes 
(table)
+          index = GSchema.GSIndex (ind, name = iname, unique = unique == 1)
+
+          for field in fields [iname]:
+            GSchema.GSIndexField (index, name = field)
+        else:
+          if isinstance (constraint, GSchema.GSPrimaryKey):
+            for field in fields [iname]:
+              GSchema.GSPKField (constraint, name = field)
+          else:
+            for field in fields [iname]:
+              GSchema.GSConstraintField (constraint, name = field)
+
+            for field in fields [fkey]:
+              item = GSchema.GSConstraintRef (constraint, name = field)
+
+            update.setdefault (fkey, []).append (item)
+
+      # Finally make sure all constraint reference fields pointing to a table
+      for (fkey, crefs) in update.items ():
+        for item in crefs:
+          item.table = relmap [fkey]
+
+    finally:
+      cursor.close ()
+
+
+  # ---------------------------------------------------------------------------
+  # Read all 'before insert'-triggers to discover Generator based fields
+  # ---------------------------------------------------------------------------
+
+  def __readSerials (self, tables):
+
+    cmd = u"SELECT rdb$relation_name, rdb$trigger_source " \
+           "FROM rdb$triggers " \
+           "WHERE rdb$trigger_type = 1 " \
+           "ORDER BY rdb$trigger_sequence"
+    cursor = self.__connection.makecursor (cmd)
+
+    try:
+      for rs in cursor.fetchall ():
+        (relname, source) = self.__stripStrings (rs)
+        if not relname in tables:
+          continue
+
+        match = self._GENFIELD.match (source)
+        if match is not None:
+          fieldname = match.groups () [0].upper ()
+          fields = tables [relname].findChildrenOfType ('GSField', False, True)
+          for item in fields:
+            if item.name == fieldname:
+              item.defaultwith = 'serial'
+
+    finally:
+      cursor.close ()
+
+
+  # ---------------------------------------------------------------------------
+  # Strip all 'stripable' elements in a given sequence
+  # ---------------------------------------------------------------------------
+
+  def __stripStrings (self, seq):
+
+    result = []
+    append = result.append
+
+    for item in seq:
+      if hasattr (item, 'strip'):
+        append (item.strip ())
+      else:
+        append (item)
+
+    return result
+


Property changes on: 
trunk/gnue-common/src/datasources/drivers/interbase/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: 
trunk/gnue-common/src/datasources/drivers/interbase/interbase/Connection.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/interbase/interbase/Connection.py 
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/interbase/interbase/Connection.py 
2005-06-01 12:27:27 UTC (rev 7560)
@@ -25,7 +25,7 @@
 
 from gnue.common.datasources.drivers import DBSIG2
 from gnue.common.datasources.drivers.interbase.Schema.Creation.Creation import 
Creation
-from gnue.common.datasources.drivers.interbase.Schema.Discovery.Introspection 
import Introspection
+from gnue.common.datasources.drivers.interbase import Behavior
 
 
 # =============================================================================
@@ -41,8 +41,8 @@
 
   _broken_rowcount = True
 
-  defaultBehavior = Introspection
   defaultCreator  = Creation
+  _behavior       = Behavior.Behavior
 
 
   # ---------------------------------------------------------------------------

Added: trunk/gnue-common/src/datasources/drivers/mysql/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/mysql/Behavior.py 2005-06-01 
11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/mysql/Behavior.py 2005-06-01 
12:27:27 UTC (rev 7560)
@@ -0,0 +1,203 @@
+# GNU Enterprise Common Library - MySQL driver - Schema Support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+from gnue.common.datasources.drivers import Base
+from gnue.common.datasources import GSchema
+
+# =============================================================================
+# This class implements schema support for MySQL database backends
+# =============================================================================
+
+class Behavior (Base.Behavior):
+  """
+  Known bugs/problems:
+
+  1. MySQL does not support booleans, nor does it have column checks. So a
+     'boolean' column will be transformed into a 'number' silently.
+
+  2. MySQL does not give us a chance to find out anything about foreign key
+     constraints. (Even with InnoDB it's quite hard to parse it from SHOW
+     CREATE TABLE or SHOW TABLE STATUS)
+
+  3. MySQL does *not* store the name of a primary key, instead this is always
+     'PRIMARY'. That's why we loos the original primary key's names.
+  """
+
+  _TYPEMAP = {'string'   : ('string', 'string'),
+              'date'     : ('date', 'date'),
+              'time'     : ('date', 'time'),
+              'timestamp': ('date', 'timestamp'),
+              'datetime' : ('date', 'datetime')}
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self, *args, **kwargs):
+
+    Base.Behavior.__init__ (self, *args, **kwargs)
+
+    # Update the typemap with numeric types
+    for t in ['int','integer','bigint','mediumint',
+              'smallint','tinyint','float','real', 'double','decimal']:
+      self._TYPEMAP [t] = ('number', 'number')
+
+
+  # ---------------------------------------------------------------------------
+  # Read the current connection's schema
+  # ---------------------------------------------------------------------------
+
+  def _readSchema_ (self, parent):
+    """
+    Read the connection's schema and build a GSchema object tree connected to
+    the given parent object (which is of type GSSchema).
+    """
+
+    tables = self.__readTables (parent)
+    fields = self.__readFields (tables)
+    self.__readIndices (tables)
+
+
+  # ---------------------------------------------------------------------------
+  # Read all tables available
+  # ---------------------------------------------------------------------------
+
+  def __readTables (self, parent):
+
+    tables = None
+    result = {}
+
+    cursor = self.__connection.makecursor (u"SHOW TABLES")
+    try:
+      for (tablename,) in cursor.fetchall ():
+        if tables is None:
+          tables = GSchema.GSTables (parent, type = 'table', name = 
_("Tables"))
+
+        result [tablename] = GSchema.GSTable (tables, name = tablename)
+
+    finally:
+      cursor.close ()
+
+    return result
+
+  
+  # ---------------------------------------------------------------------------
+  # Read all fields for the given tables
+  # ---------------------------------------------------------------------------
+
+  def __readFields (self, tables):
+
+    result = {}
+
+    for (tablename, table) in tables.items ():
+      fields = table.findChildOfType ('GSFields')
+      if fields is None:
+        fields = GSchema.GSFields (table)
+
+      cmd = u"SHOW COLUMNS FROM %s" % tablename
+      cursor = self.__connection.makecursor (cmd)
+
+      try:
+        for (name, ftype, null, key, default, extra) in cursor.fetchall ():
+          nativetype = ftype.replace (')', '').split ('(')
+          properties = {'id'        : "%s.%s" % (tablename, name),
+                        'name'      : name,
+                        'nativetype': ftype,
+                        'nullable'  : null == 'YES',
+                        'type'      : 'string'}
+
+          if nativetype [0] in self._TYPEMAP:
+            (group, properties ['type']) = self._TYPEMAP [nativetype [0]]
+          else:
+            group = 'string'
+
+          if len (nativetype) == 2:
+            parts = []
+            for item in nativetype [1].split (','):
+              parts.extend (item.split ())
+
+            if parts [0].strip ().isdigit ():
+              properties ['length'] = int (parts [0].strip ())
+
+            if len (parts) > 1 and parts [1].strip ().isdigit ():
+              properties ['precision'] = int (parts [1].strip ())
+
+          if default not in ('NULL', '0000-00-00 00:00:00', '', None):
+            properties ['defaultwith'] = 'constant'
+            properties ['default']     = default
+
+          if extra == 'auto_increment':
+            properties ['defaultwith'] = 'serial'
+
+          elif nativetype [0] == 'timestamp':
+            properties ['defaultwith'] = 'timestamp'
+
+          result [properties ['id']] = GSchema.GSField (fields, **properties)
+
+      finally:
+        cursor.close ()
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read all indices per table given
+  # ---------------------------------------------------------------------------
+
+  def __readIndices (self, tables):
+
+    for (tablename, table) in tables.items ():
+      indices = {}
+      cursor  = self.__connection.makecursor (u"SHOW INDEX FROM %s" % 
tablename)
+
+      try:
+        for rs in cursor.fetchall ():
+          (nonUnique, name, seq, column) = rs [1:5]
+
+          index = indices.setdefault (name, [])
+          index.append ((seq, column, nonUnique))
+
+      finally:
+        cursor.close ()
+  
+      for (name, parts) in indices.items ():
+        parts.sort ()
+
+        if name == 'PRIMARY':
+          fClass = GSchema.GSPKField
+          index = table.findChildOfType ('GSPrimaryKey')
+          if index is None:
+            index = GSchema.GSPrimaryKey (table, name = name)
+        else:
+          fClass = GSchema.GSIndexField
+          parent = table.findChildOfType ('GSIndexes')
+          if parent is None:
+            parent = GSchema.GSIndexes (table)
+
+          index = GSchema.GSIndex (parent, name = name,
+                                   unique = not parts [0][2])
+
+        for (seq, column, nonUnique) in parts:
+          fClass (index, name = column)
+
+


Property changes on: trunk/gnue-common/src/datasources/drivers/mysql/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/gnue-common/src/datasources/drivers/mysql/mysql/Connection.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/mysql/mysql/Connection.py 
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/mysql/mysql/Connection.py 
2005-06-01 12:27:27 UTC (rev 7560)
@@ -25,7 +25,7 @@
 
 from gnue.common.datasources.drivers import DBSIG2
 from gnue.common.datasources.drivers.mysql.Schema.Creation.Creation import 
Creation
-from gnue.common.datasources.drivers.mysql.Schema.Discovery.Introspection 
import Introspection
+from gnue.common.datasources.drivers.mysql import Behavior
 
 
 # =============================================================================
@@ -41,8 +41,8 @@
   _boolean_true   = 1
   _boolean_false  = 0
 
-  defaultBehavior = Introspection
   defaultCreator  = Creation
+  _behavior       = Behavior.Behavior
 
 
   # ---------------------------------------------------------------------------

Modified: 
trunk/gnue-common/src/datasources/drivers/postgresql/Base/Connection.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/postgresql/Base/Connection.py     
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/postgresql/Base/Connection.py     
2005-06-01 12:27:27 UTC (rev 7560)
@@ -27,7 +27,9 @@
 from gnue.common.datasources.drivers.postgresql.Schema.Creation.Creation 
import Creation
 from gnue.common.datasources.drivers.postgresql.Schema.Discovery.Introspection 
import Introspection
 
+from gnue.common.datasources.drivers.postgresql import Behavior
 
+
 # =============================================================================
 # Generic PostgreSQL Connection class
 # =============================================================================
@@ -41,6 +43,7 @@
   defaultCreator  = Creation
 
   _rowidField = 'oid'
+  _behavior   = Behavior.Behavior
 
 
   # ---------------------------------------------------------------------------

Added: trunk/gnue-common/src/datasources/drivers/postgresql/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/postgresql/Behavior.py    
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/postgresql/Behavior.py    
2005-06-01 12:27:27 UTC (rev 7560)
@@ -0,0 +1,302 @@
+# GNU Enterprise Common Library - PostgreSQL driver - Schema Support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+from gnue.common.datasources.drivers import DBSIG2
+from gnue.common.datasources import GSchema
+
+
+class Behavior (DBSIG2.Behavior):
+
+  _RELKIND = {'v': {'type': 'view',  'name': _("Views")},
+              'r': {'type': 'table', 'name': _("Tables")}}
+
+  # ---------------------------------------------------------------------------
+  # Constructor
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self, *args, **kwargs):
+
+    DBSIG2.Behavior.__init__ (self, *args, **kwargs)
+
+    # Build  typemap: {nativetype: (group, fieldtype)}
+    self._TYPEMAP = {'date'  : ('date', 'date'),
+                     'bool'  : ('boolean', 'boolean'),
+                     'string': ('string', 'string')}
+
+    for item in ['numeric', 'float4', 'float8', 'money', 'int8',
+                 'int2', 'int4', 'serial']:
+      self._TYPEMAP [item] = ('number', 'number')
+
+    for item in ['time', 'reltime']:
+      self._TYPEMAP [item] = ('date', 'time')
+
+    for item in ['timestamp', 'abstime']:
+      self._TYPEMAP [item] = ('date', 'datetime')
+
+
+  # ---------------------------------------------------------------------------
+  # Read the current connection's schema
+  # ---------------------------------------------------------------------------
+
+  def _readSchema_ (self, parent):
+    """
+    Read the connection's schema and build a GSchema object tree connected to
+    the given parent object (which is of type GSSchema).
+    """
+
+    tables = self.__readTables (parent)
+    fields = self.__readFields (tables)
+    self.__readDefaults (fields)
+    self.__readKeys (tables)
+    self.__readConstraints (tables)
+
+
+  # ---------------------------------------------------------------------------
+  # Read all table-like elements
+  # ---------------------------------------------------------------------------
+
+  def __readTables (self, parent):
+    
+    mapping = {}    # Maps OIDs to GSTable instances
+    tables  = None
+    views   = None
+
+    cmd = u"SELECT c.oid, c.relname, c.relkind " \
+           "FROM pg_class c, pg_namespace n " \
+           "WHERE n.nspname = 'public' AND n.oid = c.relnamespace AND " \
+           "      c.relkind in (%s) " \
+           "ORDER BY c.relname" \
+           % ','.join (["%r" % kind for kind in self._RELKIND.keys ()])
+
+    cursor = self.__connection.makecursor (cmd)
+
+    try:
+      for (oid, relname, relkind) in cursor.fetchall ():
+
+        kind = self._RELKIND [relkind] ['type']
+        properties = {'id': oid, 'name': relname, 'kind': kind}
+
+        if relkind == 'v':
+          if views is None:
+            views = GSchema.GSTables (parent, **self._RELKIND [relkind])
+          master = views
+        else:
+          if tables is None:
+            tables = GSchema.GSTables (parent, **self._RELKIND [relkind])
+          master = tables
+
+        table = GSchema.GSTable (master, **properties)
+
+        # Maintain a temporary mapping from OID's to GSTable instances so
+        # adding fields afterwards runs faster
+        mapping [oid] = table
+
+    finally:
+      cursor.close ()
+
+    return mapping
+
+
+  # ---------------------------------------------------------------------------
+  # Find all fields
+  # ---------------------------------------------------------------------------
+
+  def __readFields (self, tables):
+
+    cmd = u"SELECT attrelid, attname, t.typname, attnotnull, " \
+           "       atthasdef, atttypmod, attnum, attlen " \
+           "FROM pg_attribute a " \
+           "LEFT OUTER JOIN pg_type t ON t.oid = a.atttypid " \
+           "WHERE attnum >= 0 AND attisdropped = False " \
+           "ORDER BY attrelid, attnum"
+
+    cursor = self.__connection.makecursor (cmd)
+    fields = None
+    result = {}
+
+    try:
+      for rs in cursor.fetchall ():
+        (relid, name, typename, notnull, hasdef, typemod, attnum, attlen) = rs
+
+        # only process attributes from tables we've listed before
+        if not relid in tables:
+          continue
+
+        attrs = {'id'        : "%s.%s" % (relid, attnum),
+                 'name'      : name,
+                 'nativetype': typename,
+                 'nullable'  : hasdef or not notnull}
+
+        if typename.lower () in self._TYPEMAP:
+          (group, attrs ['type']) = self._TYPEMAP [typename.lower ()]
+        else:
+          (group, attrs ['type']) = self._TYPEMAP ['string']
+
+        if group == 'number':
+          if typemod != -1:
+            value = typemod - 4
+            attrs ['length']    = value >> 16
+            attrs ['precision'] = value &  0xFFFF
+
+          elif attlen > 0:
+            attrs ['length'] = len ("%s" % 2L ** (attlen * 8))
+
+        elif typemod != -1:
+          attrs ['length'] = typemod - 4
+
+        # Remove obsolete attributes
+        if group in ['date', 'boolean']:
+          for item in ['length', 'precision']:
+            if item in attrs:
+              del attrs [item]
+
+        elif group in ['string']:
+          if 'precision' in attrs:
+            del attrs ['precision']
+
+        table = tables [relid]
+        fields = table.findChildOfType ('GSFields')
+        if fields is None:
+          fields = GSchema.GSFields (table)
+
+        result [attrs ['id']] = GSchema.GSField (fields, **attrs)
+
+    finally:
+      cursor.close ()
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read defaults and apply them to the given fields
+  # ---------------------------------------------------------------------------
+
+  def __readDefaults (self, fields):
+
+    cmd = u"SELECT adrelid, adnum, adsrc FROM pg_attrdef ORDER BY adrelid"
+
+    cursor = self.__connection.makecursor (cmd)
+
+    try:
+      for (relid, fieldnum, source) in cursor.fetchall ():
+        field = fields.get ("%s.%s" % (relid, fieldnum))
+        
+        # Skip all defaults of not listed fields
+        if field is None:
+          continue
+
+        if source [:8] == 'nextval(':
+          field.defaultwith = 'serial'
+
+        elif source == 'now()':
+          field.defaultwith = 'timestamp'
+
+        else:
+          field.defaultwith = 'constant'
+          field.default = source.split ('::') [0].strip ("'")
+
+    finally:
+      cursor.close ()
+
+
+  # ---------------------------------------------------------------------------
+  # Read all indices and associate them with their table/view
+  # ---------------------------------------------------------------------------
+
+  def __readKeys (self, tables):
+
+    cmd = u"SELECT indrelid, indkey, indisunique, indisprimary, c.relname " \
+           "FROM pg_index i LEFT OUTER JOIN pg_class c ON c.oid = indexrelid"
+
+    cursor = self.__connection.makecursor (cmd)
+
+    try:
+      for (relid, fieldvec, isUnique, isPrimary, name) in cursor.fetchall ():
+
+        # Skip functional indices. A functional index is an index that is built
+        # upon a fuction manipulating a field upper(userid) vs userid
+        fields = [int (i) - 1 for i in fieldvec.split ()]
+        if not fields:
+          continue
+
+        # only process keys of listed tables
+        table = tables.get (relid)
+        if table is None:
+          continue
+
+        if isPrimary:
+          index  = GSchema.GSPrimaryKey (table, name = name)
+          fClass = GSchema.GSPKField
+        else:
+          indices = table.findChildOfType ('GSIndexes')
+          if indices is None:
+            indices = GSchema.GSIndexes (table)
+
+          index  = GSchema.GSIndex (indices, unique = isUnique, name = name)
+          fClass = GSchema.GSIndexField
+
+        fieldList = table.findChildrenOfType ('GSField', False, True)
+        for find in fields:
+          fClass (index, name = fieldList [find].name)
+
+    finally:
+      cursor.close ()
+
+
+  # ---------------------------------------------------------------------------
+  # Read all constraints
+  # ---------------------------------------------------------------------------
+
+  def __readConstraints (self, tables):
+
+    cmd = u"SELECT conname, conrelid, confrelid, conkey, confkey " \
+           "FROM pg_constraint WHERE contype = 'f'"
+
+    cursor = self.__connection.makecursor (cmd)
+    try:
+      for (name, relid, fkrel, key, fkey) in cursor.fetchall ():
+        table = tables.get (relid)
+        fktable = tables.get (fkrel)
+
+        # We need both ends of a relation to be a valid constraint
+        if table is None or fktable is None:
+          continue
+
+        parent = table.findChildOfType ('GSConstraints')
+        if parent is None:
+          parent = GSchema.GSConstraints (table)
+
+        constr = GSchema.GSConstraint (parent, name = name, type = 
'foreignkey')
+
+        keyfields = table.findChildrenOfType ('GSField', False, True)
+        for ix in [int (i) - 1 for i in key [1:-1].split (',')]:
+          GSchema.GSConstraintField (constr, name = keyfields [ix].name)
+
+        fkfields  = fktable.findChildrenOfType ('GSField', False, True)
+        for ix in [int (i) - 1 for i in fkey [1:-1].split (',')]:
+          GSchema.GSConstraintRef (constr, name = fkfields [ix].name,
+                                           table = fktable.name)
+
+    finally:
+      cursor.close ()
+


Property changes on: 
trunk/gnue-common/src/datasources/drivers/postgresql/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: trunk/gnue-common/src/datasources/drivers/sqlite/Behavior.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/sqlite/Behavior.py        
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/sqlite/Behavior.py        
2005-06-01 12:27:27 UTC (rev 7560)
@@ -0,0 +1,285 @@
+# GNU Enterprise Common Library - SQLite driver - Schema Support
+#
+# Copyright 2001-2005 Free Software Foundation
+#
+# 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.
+#
+# $Id$
+
+import re
+
+from gnue.common.datasources.drivers import Base
+from gnue.common.datasources import GSchema
+
+
+# ===========================================================================
+# Regular expressions and constants
+# ===========================================================================
+
+_REPCOMMAS   = re.compile ('\(\s*(\d+)\s*,\s*(\d+)\s*\)')
+_ALIGN       = re.compile ('\s*\(\s*(.*?)\s*\)')
+_LEN_SCALE   = re.compile ('^\s*(\w+)\s*\((\d+)[;]{0,1}(\d*)\)\s*')
+_TEXTTYPE    = re.compile ('.*(BLOB|CHAR|CLOB|TEXT){1}.*')
+_BLANKS      = re.compile ('\s+')
+_NOTNULL     = re.compile ('(.*)(NOT NULL)(.*)', re.I)
+_CONSTRAINTS = re.compile ('.*?((UNIQUE|CHECK|PRIMARY KEY)\s*\(.*?\)).*', re.I)
+_PRIMARYKEY  = re.compile ('.*?PRIMARY KEY\s*\((.*?)\).*', re.I)
+_PKFIELD     = re.compile ('.*?PRIMARY\s+KEY\s*', re.I)
+_INDEX       = re.compile ('CREATE\s*(\w+){0,1}\s*INDEX\s*(\w+)\s*ON\s*\w+\s*'\
+                           '\((.*?)\).*', re.I)
+_VIEWCODE    = re.compile ('^\s*CREATE\s+VIEW\s+\w+\s+AS\s+(.*)\s*$', re.I)
+_DEFAULT     = re.compile ('.*\s+DEFAULT\s+(.*)', re.I)
+_SQLCODE     = re.compile ('\s*SELECT\s+(.*)\s+FROM\s+(\w+).*', re.I)
+
+RELTYPE = {'table': {'type': 'table', 'name': _("Tables")},
+           'view' : {'type': 'view',  'name': _("Views")}}
+
+# =============================================================================
+#
+# =============================================================================
+
+class Behavior (Base.Behavior):
+  """
+  Limitations:
+
+  * Since SQLite is typeless we cannot derive a 'length' for columns specified
+    as 'integer' or 'text' without any further information.
+  * SQLite does not support referential constraints
+  * SQLite has no real concept of a serial
+  * SQLite has no concept of a 'default with timestamp'
+  * Name of Primary Keys is not available
+  """
+
+  # ---------------------------------------------------------------------------
+  # Read the current connection's schema
+  # ---------------------------------------------------------------------------
+
+  def _readSchema_ (self, parent):
+    """
+    Read the connection's schema and build a GSchema object tree connected to
+    the given parent object (which is of type GSSchema).
+    """
+
+    tables = self.__readTables (parent)
+    self.__readIndices (tables)
+
+
+  # ---------------------------------------------------------------------------
+  # Read all tables
+  # ---------------------------------------------------------------------------
+
+  def __readTables (self, parent):
+    
+    cmd = u"SELECT type, name, sql FROM sqlite_master ORDER BY lower (name)"
+
+    result  = {}
+    masters = {}
+    views   = {}
+    cursor  = self.__connection.makecursor (cmd)
+
+    try:
+      for (reltype, name, sql) in cursor.fetchall ():
+        if not reltype in RELTYPE:
+          continue
+
+        if not reltype in masters:
+          masters [reltype] = GSchema.GSTables (parent, **RELTYPE [reltype])
+
+        key = name.lower ()
+        result [key] = GSchema.GSTable (masters [reltype], name = name)
+
+        if reltype == 'table':
+          self.__parseFields (result [key], sql)
+
+          pkm = _PRIMARYKEY.match (sql)
+          if pkm is not None:
+            pk = GSchema.GSPrimaryKey (result [key], name = 'pk_%s' % name)
+
+            for field in [f.strip () for f in pkm.groups () [0].split (',')]:
+              GSchema.GSPKField (pk, name = field)
+
+        else:
+          views [key] = sql
+
+    finally:
+      cursor.close ()
+
+    self.__parseViews (result, views)
+      
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Parse all fields from a given SQL code
+  # ---------------------------------------------------------------------------
+
+  def __parseFields (self, table, sql):
+
+    result = {}
+
+    # Replace all newlines by a single whitespace and take all the code in
+    # between the first and last bracket
+    code = ' '.join (sql.splitlines ())
+    code = code [code.find ('(') + 1:code.rfind (')')]
+
+    # Reduce multiple blanks to a single blank
+    code = _BLANKS.sub (' ', code)
+    # Make sure to have numeric arugments like '( 5 , 2)' given as '(5;2)'
+    code = _REPCOMMAS.sub (r'(\1;\2)', code)
+    # Realign arguments in parenthesis, i.e. from 'char(  7 )' to 'char (7)'
+    code = _ALIGN.sub (r' (\1)', code)
+
+    # we currently skip all constraints (primary key, unique, check)
+    cma = _CONSTRAINTS.match (code)
+    while cma is not None:
+      constraint = cma.groups () [0]
+      code = code.replace (constraint, '')
+      cma = _CONSTRAINTS.match (code)
+
+    for item in [i.strip () for i in code.split (',')]:
+      if not len (item):
+        continue
+
+      parts = item.split ()
+
+      if _PKFIELD.match (item) is not None:
+        pk = table.findChildOfType ('GSPrimaryKey') or \
+                      GSchema.GSPrimaryKey (table, name = 'pk_%s' % table.name)
+        GSchema.GSPKField (pk, name = parts [0])
+
+      attrs = {'id'  : "%s.%s" % (table.name, parts [0]),
+               'name': parts [0]}
+
+      datatype = ' '.join (parts [1:])
+
+      lsmatch = _LEN_SCALE.match (datatype)
+      if lsmatch is not None:
+        (typename, length, scale) = lsmatch.groups ()
+      else:
+        typename = parts [1]
+        length   = 0
+        scale    = 0
+
+      nativetype = typename
+      add = filter (None, [length, scale])
+      nativetype += add and "(%s)" % ','.join (add) or ''
+
+      attrs ['nativetype'] = nativetype
+
+      if length:
+        attrs ['length'] = length
+      if scale:
+        attrs ['precision'] = scale
+
+      attrs ['nullable']   = _NOTNULL.match (item) is None
+
+      if _TEXTTYPE.match (typename.upper ()):
+        attrs ['type'] = 'string'
+
+      elif typename.lower () in ['date', 'datetime', 'time']:
+        attrs ['type'] = typename.lower ()
+
+      else:
+        attrs ['type'] = 'number'
+
+      fields = table.findChildOfType ('GSFields') or GSchema.GSFields (table)
+      result [attrs ['id']] = GSchema.GSField (fields, **attrs)
+
+      match = _DEFAULT.match (item)
+      if match is not None:
+        text = match.groups () [0].strip ()
+        if text [0] in ["'", '"']:
+          default = text [1:text.find (text [0], 1)]
+        else:
+          default = text.split () [0]
+
+        result [attrs ['id']].defaultwith = 'constant'
+        result [attrs ['id']].default     = default
+
+    return result
+
+
+  # ---------------------------------------------------------------------------
+  # Read all indices for the given tables
+  # ---------------------------------------------------------------------------
+
+  def __readIndices (self, tables):
+
+    cmd = u"SELECT lower (tbl_name), name, sql FROM sqlite_master " \
+           "WHERE type = 'index' AND sql IS NOT NULL"
+
+    cursor = self.__connection.makecursor (cmd)
+
+    try:
+      for (tname, name, sql) in cursor.fetchall ():
+        table = tables [tname]
+
+        ixm = _INDEX.match (sql)
+        if ixm is not None:
+          (unique, name, fields) = ixm.groups ()
+
+          top = table.findChildOfType ('GSIndexes') or GSchema.GSIndexes 
(table)
+          index = GSchema.GSIndex (top, name = name,
+                   unique = unique is not None and unique.lower () == 'unique')
+
+          for field in [f.strip () for f in fields.split (',')]:
+            GSchema.GSIndexField (index, name = field)
+
+    finally:
+      cursor.close ()
+
+
+  # ---------------------------------------------------------------------------
+  # Populate the view objects
+  # ---------------------------------------------------------------------------
+
+  def __parseViews (self, tables, views):
+
+    for (viewname, sql) in views.items ():
+      code = ' '.join (sql.splitlines ())
+      code = _VIEWCODE.match (code)
+      if not code:
+        continue
+
+      match = _SQLCODE.match (code.groups () [0])
+      if match:
+        (fieldseq, relname) = match.groups ()
+        tablename = relname.lower ()
+        if not viewname in tables or not tablename in tables:
+          continue
+
+        view    = tables [viewname]
+        table   = tables [tablename]
+        tfields = table.findChildrenOfType ('GSField', False, True)
+        vfields = view.findChildOfType ('GSFields') or GSchema.GSFields (view)
+
+        for item in [f.strip ().lower () for f in fieldseq.split (',')]:
+          tf = None
+          for f in tfields:
+            if f.name.lower () == item:
+              tf = f
+              break
+
+          if tf is None: continue
+
+          vf = GSchema.GSField (vfields, name = tf.name, type = tf.type)
+          for key in ['length', 'precision', 'nullable', 'default',
+                      'defaultwith']:
+            if hasattr (tf, key):
+              setattr (vf, key, getattr (tf, key))
+


Property changes on: 
trunk/gnue-common/src/datasources/drivers/sqlite/Behavior.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: trunk/gnue-common/src/datasources/drivers/sqlite/sqlite/Connection.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/sqlite/sqlite/Connection.py       
2005-06-01 11:45:13 UTC (rev 7559)
+++ trunk/gnue-common/src/datasources/drivers/sqlite/sqlite/Connection.py       
2005-06-01 12:27:27 UTC (rev 7560)
@@ -25,7 +25,7 @@
 
 from gnue.common.datasources.drivers import DBSIG2
 from gnue.common.datasources.drivers.sqlite.Schema.Creation.Creation import 
Creation
-from gnue.common.datasources.drivers.sqlite.Schema.Discovery.Introspection 
import Introspection
+from gnue.common.datasources.drivers.sqlite import Behavior
 
 
 # =============================================================================
@@ -40,8 +40,8 @@
   _boolean_true  = 1
   _boolean_false = 0
 
-  defaultBehavior = Introspection
-  defaultCreator  = Creation
+  defaultCreator = Creation
+  _behavior      = Behavior.Behavior
 
 
   # ---------------------------------------------------------------------------





reply via email to

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