[Top][All Lists]
[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
# ---------------------------------------------------------------------------
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [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,
johannes <=