dbschema.py :  » Database » PyTable » pytable-0.8.20a » pytable » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Database » PyTable 
PyTable » pytable 0.8.20a » pytable » dbschema.py
"""Data-description of table structure"""
from __future__ import generators
from basicproperty import propertied,common,basic,weak
from basictypes import list_types,callable
from pytable import sqlquery

class Schema( propertied.Propertied ):
  """Base-class for Schema objects"""
  actions = common.ListProperty(
    'actions', """Actions defined for use with this schema-item

    Some possible actions:
      selectAll/refreshView
      revert:
        all
        row(s)
        row,field
      save data:
        update all rows
        update row
        commit
        append row
      insert row
      delete row
    """,
    baseType = callable.Callables,
    defaultFunction = lambda prop,client: [],
  )
  comment = common.StringProperty(
    "comment", """Optional comment/documentation for this schema item""",
    defaultValue = "",
    setDefaultOnGet = 0,
    dataType = 'str.long',
  )
  _Schema__namespace = weak.WeakProperty(
    "_Schema__namespace", """Weak ref to namespace in which we are defined""",
  )
  _Schema__names = common.DictionaryProperty(
    "_Schema__names", """Mapping from name: object for the schema""",
    defaultFunction = lambda property,client: client.buildNamespace(),
  )
  
  def actionByName( self, name ):
    """Retrieve a particular action by name"""
    for action in self.actions:
      if hasattr( action, 'name'):
        actionName = action.name
      else:
        actionName = action.__name__
      if actionName == name:
        return action
    return None
  __repr__ = propertied.Propertied.toString
  def buildNamespace( self, ignoreDuplicates=1 ):
    """Attempt to build namespace for this object"""
  def lookupName( self, name=None, requiredType=None, alreadySeen=None ):
    """Try to find object of the given name/type

    name -- if specified, look for an object with the
      given name (only)
    requiredType -- if specified, look for an object
      with the given type.

    lookupName searches up the _Schema__names containment
    hierarchy looking for matches to the given name/type
    pair.  Note: the search is *and* if both are specified,
    that is, both must be satisfied to match.
    """
    if isinstance( name, (str,unicode)):
      name = name.lower()
    elif name is None:
      if requiredType is None:
        return self
      if isinstance( self, requiredType ):
        return self
      elif self._Schema__namespace:
        if not alreadySeen:
          alreadySeen = {}
        alreadySeen[ id(self)] = True
        if not alreadySeen.has_key( id(self._Schema__namespace)):
          return self._Schema__namespace.lookupName( 
            requiredType=requiredType,
            alreadySeen = alreadySeen,
          )
      raise NameError( """Couldn't find any object of type %r in this namespace"""%(requiredType,))
    if hasattr(self, '_Schema__namespace'):
      if not alreadySeen:
        alreadySeen = {}
      alreadySeen[ id(self)] = True
      if not alreadySeen.has_key( id(self._Schema__namespace)):
        return self._Schema__namespace.lookupName( 
          name, requiredType =requiredType,
          alreadySeen = alreadySeen,
        )
    raise NameError( """Couldn't find name %r in the namespace(s) of %r object %r"""%(
      name,
      type(self),
      getattr(self, 'name',''),
    ))
  def __iter__( self ):
    """Iterate over our sub-elements"""
    if 0: yield self
  def findParent( self, requiredType=None ):
    """Find our last parent of the given type, or None"""
    current = self
    lastParent = None
    while 1:
      # XXX is there any point in guarding against loops?
      current = getattr( current, '_Schema__namespace', None )
      if current is None:
        break
      else:
        if requiredType is not None and isinstance(current, requiredType):
          lastParent = current
        elif requiredType is None:
          lastParent = current
    return lastParent

class ParentSchema( Schema ):
  """Base class for all Schemas which have children"""
  def lookupName( self, name=None, requiredType=None, alreadySeen=None ):
    """Try to find object of the given name/type

    name -- if specified, look for an object with the
      given name (only)
    requiredType -- if specified, look for an object
      with the given type.

    lookupName searches up the _Schema__names containment
    hierarchy looking for matches to the given name/type
    pair.  Note: the search is *and* if both are specified,
    that is, both must be satisfied to match.
    """
    if not self._Schema__names:
      self.buildNamespace()
    if isinstance( name, (str,unicode)):
      name = name.lower()
    elif name is None:
      if requiredType is None:
        return self
      if isinstance( self, requiredType ):
        return self
      elif self._Schema__namespace:
        if not alreadySeen:
          alreadySeen = {}
        alreadySeen[ id(self)] = True
        return self._Schema__namespace.lookupName( 
          requiredType =requiredType,
          alreadySeen = alreadySeen,
        )
      else:
        raise NameError( """Couldn't find any object of type %r in this namespace"""%(requiredType,))
    test = self._Schema__names.get( name )
    if test is not None:
      if not requiredType or (requiredType and isinstance(test, requiredType)):
        return test
    if hasattr(self, '_Schema__namespace'):
      if not alreadySeen:
        alreadySeen = {}
      if not alreadySeen.has_key( id(self._Schema__namespace)):
        alreadySeen[ id(self)] = True
        return self._Schema__namespace.lookupName( 
          name, requiredType =requiredType,
          alreadySeen = alreadySeen,
        )
    raise NameError( """Couldn't find name %r in the namespace(s) of %r object %r"""%(
      name,
      type(self),
      getattr(self, 'name',''),
    ))
  def buildNamespace( self, ignoreDuplicates=1 ):
    """Attempt to build namespace for this object"""
    self._Schema__names = names = {}
    for item in self:
      if names.has_key(item.name) and item.name:
        if not ignoreDuplicates:
          raise KeyError( """Item with name %r already exists in this namespace: conflict between %r and %r"""%(
            item.name, item, names.get(item.name),
          ))
      if item.name:
        names[item.name.lower()] = item
      item.buildNamespace()
    return names

class ConstraintSchema( Schema ):
  """Representation of a field or table constraint"""
  dbObjectType = "CONSTRAINT"
  name = common.StringProperty(
    'name', """Name of the constraint, often not specified""",
    defaultValue = "",
    setDefaultOnGet = 0,
  )
  fields = common.StringsProperty(
    "fields", """The list of fields affected by the constraint""",
  )
  deferrable = common.BooleanProperty(
    'deferrable', """Whether checking can be deferred to the end of the transaction""",
    defaultValue = 0,
  )
  deferred = common.BooleanProperty(
    'deferred', """Whether constraint is currently deferred""",
    defaultValue = 0,
  )
  def localFields( self ):
    """Get field objects for the affected field(s)"""
    return [ self.lookupName(name) for name in self.fields]
  def resolve( self ):
    """Attempt to resolve all field references"""
    for name in self.fields:
      self.lookupName( name )

class NotNullConstraint( ConstraintSchema ):
  """A field's not-null constraint"""
  dbConstraintType = "NOT NULL"
class UniqueConstraint( ConstraintSchema ):
  """(Possibly Multi-field) unique-value constraint"""
  dbConstraintType = "UNIQUE"
class PrimaryConstraint( UniqueConstraint ):
  """(Possibly multi-field) primary-key unique constraint

  Implies both unique and NotNull
  """
  dbConstraintType = "PRIMARY KEY"

def isPrimary( constraint ):
  """Is the given object a primary-key constraint?"""
  return isinstance( constraint, PrimaryConstraint )
def isUnique( schema ):
  """Does the given index/constraint create a unique or primary constraint?"""
  if isinstance( schema, (UniqueConstraint, PrimaryConstraint) ):
    return 1
  elif isinstance( schema, IndexSchema ) and schema.unique:
    return 1
  return 0

class CheckConstraint( ConstraintSchema ):
  """An SQL-statement CHECK constraint"""
  dbConstraintType = "CHECK"
  expression = common.StringProperty(
    "expression", """The SQL-formatted expression to test on insertions/updates""",
    defaultValue = "",
  )

from basictypes import enumeration
actions = enumeration.EnumerationSet()
actions.new(
  value="NO ACTION",
  name = "noAction",
  friendlyName = "Do Nothing",
)
actions.new(
  value="CASCADE",
  name = "cascade",
  friendlyName = "Cascade Deletion",
)
actions.new(
  value="SET NULL",
  name = "null",
  friendlyName = "Set to NULL",
)
actions.new(
  value="SET DEFAULT",
  name = "default",
  friendlyName = "Set to Default",
)
class ActionProperty( enumeration.EnumerationProperty, basic.BasicProperty ):
  """Class representing a constraint's action operation"""
  set = actions
del actions  
  

class ForeignKeyConstraint( ConstraintSchema ):
  """Foreign-key constraint for a field or table"""
  dbConstraintType = "FOREIGN KEY"
  foreignTable = common.StringProperty(
    "foreignTable", """The name of the table which constrains us""",
    defaultValue = "",
  )
  foreignFields = common.StringsProperty(
    "foreignFields", """The fields which constrain us""",
  )
  onDelete = ActionProperty(
    "onDelete", """What to do when the constraint is violated

  CASCADE causes the local row to be deleted
  
  default is normally "NO ACTION", which raises
  errors.
  """,
  )
  onUpdate = ActionProperty(
    "onUpdate", """What to do when the referenced table.column changes

  CASCADE copies the new value
  
  default is normally "NO ACTION", which raises
  errors.
    """,
  )
  #queryAllForeignValues( )
  #viewForeign( )
  def resolve( self ):
    """Attempt to resolve all field references"""
    super( ForeignKeyConstraint, self).resolve()
    table = self.lookupName( self.foreignTable, requiredType=BaseTableSchema )
    if self.foreignFields:
      for field in self.foreignFields:
        table.lookupName( field )
    elif hasattr( table, 'getUniqueKeys' ):
      assert table.getUniqueKeys(), """Foreign Key references table %r without specifying a field-name, table has no primary key"""%(
        self.foreignTable,
      )
    else:
      raise RuntimeError(
        """Reference to foreign table %r retrieved a non-table object %r"""%(
          self.foreignTable,
          type(table),
        )
      )
  def getForeignFields( self ):
    """Find the foreign field-name for the referenced table"""
    if self.foreignFields:
      return self.foreignFields
    else:
      table = self.lookupName( self.foreignTable )
      keys = table.getUniqueKeys()
      assert keys, """Foreign-key constraint %r references table %r which has no unique keys!"""%(
        self, table.name,
      )
      return keys[0]
      
  
def constraint_factories( cls ):
  return [ ForeignKeyConstraint, PrimaryConstraint, UniqueConstraint, CheckConstraint ]
ConstraintSchemas = list_types.listof(
  ConstraintSchema,
  name = "ConstraintSchemas",
  dataType = "list.ConstraintSchemas",
  factories = classmethod( constraint_factories ),
)

class SequenceSchema( Schema ):
  """A sequence object used for implementing e.g. serial fields"""
  dbObjectType = "SEQUENCE"
  name = common.StringProperty(
    'name', """Name of the sequence""",
  )
  increment = common.IntegerProperty(
    'increment', """Amount to increment on each "next" call""",
    defaultValue = 1,
    setDefaultOnGet = 0,
  )
  ascending = basic.BasicProperty(
    "ascending", """Whether this is an ascending value""",
    _getValue = lambda property, client: client.increment > 0,
  )
  def defaultMinimum( property, client ):
    if client.ascending:
      return 1
    else:
      return -(2L**63)-1
  def defaultMaximum( property, client ):
    if client.ascending:
      return 2L**63-1
    else:
      return -1
  def defaultStart( property, client ):
    if client.ascending:
      return 1
    else:
      return -1
  minimumValue = common.IntegerProperty(
    'minimumValue', """Minimum value to which we will traverse""",
    defaultFunction = defaultMinimum,
    setDefaultOnGet = 0,
  )
  maximumValue = common.IntegerProperty(
    'maximumValue', """Maximum value to which we will traverse""",
    defaultFunction = defaultMaximum,
    setDefaultOnGet = 0,
  )
  start = common.IntegerProperty(
    'start', """Initial starting value""",
    defaultFunction = defaultStart,
    setDefaultOnGet = 0,
  )
  del defaultMinimum
  del defaultMaximum
  del defaultStart
  cycle = common.BooleanProperty(
    "cycle", """Whether to loop when reach end or produce errors""",
    defaultValue = 0,
  )
  
  
SequenceSchemas = list_types.listof(
  SequenceSchema,
  name = "SequenceSchemas",
  dataType = "list.SequenceSchema",
)

class FieldSchema(  ParentSchema ):
  """Schema for a particular field of a table

  A field schema describes a particular field of
  a particular table (or view).  It is used to create
  descriptors (properties) which affect table rows
  (among other uses).
  """
  dbObjectType = "COLUMN"
  name = common.StringProperty(
    'name', """Name of the field (required)""",
    defaultValue = "",
    setDefaultOnGet = 0,
  )
  def defaultFriendlyName( property, fieldSchema ):
    """Get a reasonable property name for property of table"""
    try:
      tableSchema = fieldSchema.lookupName( requiredType = BaseTableSchema )
    except:
      return fieldSchema.name
    else:
      # okay, need to kludge it...
      base = fieldSchema.name.lower()
      table = tableSchema.name.lower()
      if base.startswith( table ):
        if len(base) != len(table):
          base = base[len(table):]
      base = base.replace( "_", " ")
      base = base.title()
      return base
  friendlyName = common.StringProperty(
    'friendlyName', """Name of the field (required)""",
    defaultFunction = defaultFriendlyName,
    setDefaultOnGet = 0,
  )
  del defaultFriendlyName
  index = common.IntegerProperty(
    'index', """Index of the field in its table/view""",
    defaultValue = -1,
    setDefaultOnGet = 0,
  )
  displaySize = basic.BasicProperty(
    'displaySize', """Text-mode display size of the value (can be a tuple)""",
    defaultValue = -1,
    setDefaultOnGet = 0,
  )
  internalSize = basic.BasicProperty(
    'internalSize', """Binary internal size of the value""",
    defaultValue = 1,
    setDefaultOnGet = 0,
  )
  def defaultSequenceName( prop, client ):
    """Get default sequence name for the field"""
    if getattr( client, 'dbDataType', None ) in ('serial','bigserial'):
      try:
        tableSchema = client.lookupName( requiredType = BaseTableSchema )
      except:
        return '%s_seq'%(client.name,)
      else:
        # okay, need to kludge it...
        base = client.name.lower().replace( ".", "_")
        table = tableSchema.name.lower()
        return '%(table)s_%(base)s_seq'%locals()
    raise AttributeError( """Don't have a sequence name for %s"""%(
      client.name,
    ))
  sequenceName = common.StringProperty(
    'sequenceName', """Sequence name for serial field data-types""",
    defaultFunction = defaultSequenceName,
  )
  del defaultSequenceName
  
  def defaultNullOk( property, client ):
    """Search constraints for a NotNullConstraint"""
    for constraint in client.constraints:
      if isinstance( constraint, (NotNullConstraint,PrimaryConstraint) ):
        return 0
    return 1
  nullOk = common.BooleanProperty(
    'nullOk', """Whether value can be left null when inserting/updating

  This is actually just mirroring the presence of a
  NotNullConstraint in our constraints set.
  """,
    defaultFunction = defaultNullOk,
    setDefaultOnGet = 0,
  )
  del defaultNullOk
  defaultValue = basic.BasicProperty(
    "defaultValue","""A string representation of the default value for the field""",
  )
  constraints = common.ListProperty(
    'constraints', """Constraint objects which constrain this field""",
    baseType = ConstraintSchemas,
  )

  def defaultDataType( prop, client ):
    """Try to get a default data-type for the field"""
    foreign = client.foreign()
    if foreign:
      # datatype is foreign.tablename.rest_of_type
      base = 'foreign.%s.'%( foreign.foreignTable, )
    else:
      base = ''
    try:
      databaseSchema = client.findParent( requiredType = DatabaseSchema )
    except NameError:
      databaseSchema = None
    else:
      if (
        databaseSchema and
        hasattr( databaseSchema, 'driver') and
        databaseSchema.driver
      ):
        driver = databaseSchema.driver
        if driver and getattr( client, "dbDataType", None):
          return base + driver.sqlToDataType( client.dbDataType )
    if base:
      return base[:-1]
    raise AttributeError( """Schema %r doesn't define a dataType"""%(client,))
  dataType = common.StringProperty(
    'dataType', """wxoo string dataType declaration

  If not explicitly set, this value can normally be
  inferred from the database itself.  Explicitly
  setting the value allows for defining "special"
  dataTypes such as for a choice from a referenced
  table.
  """,
    defaultFunction = defaultDataType,
    setDefaultOnGet = 1,
  )
  del defaultDataType
  
  dbDataType = common.StringProperty(
    'dbDataType', """database's SQL dataType declaration""",
    defaultValue = "",
    setDefaultOnGet = 0,
  )
  def defaultPropertyClass( property, client ):
    """Try to find the appropriate property class for given field"""
    if client.foreign():
      return "pytable.dbproperty.ReferenceProperty"
    return "pytable.dbproperty.DBProperty"
  propertyClass = common.ClassByNameProperty (
    "propertyClass","""The class used to represent this property object""",
    defaultFunction = defaultPropertyClass,
    setDefaultOnGet = 0,
  )
  del defaultPropertyClass
  baseClass = common.ClassByNameProperty (
    "baseClass","""The class used to represent items, also used for dataType and the like""",
  )
  control = common.ClassByNameProperty (
    "control","""Explicitly-set control class to use for this field""",
  )
  gridViewer = common.ClassByNameProperty (
    "gridViewer","""Explicitly-set gridViewer class to use for this field""",
  )
  gridEditor = common.ClassByNameProperty (
    "gridEditor","""Explicitly-set gridEditor class to use for this field

  Note: if this property is not set, but the control property
    is set, the control will be auto-wrapped as a grid editor.
  """,
  )
  
  def buildProperty( self ):
    """Build a BasicProperty which uses this schema"""
    return self.propertyClass(
      name = self.name,
      schema = self,
    )
  def checkConstraint( self, constraint ):
    """Check/ensure that constraint refers only to us"""
    if constraint.fields:
      if not constraint.fields == [self.name]:
        raise ValueError( """Constraint %r does not refer to the field in which it is defined %r"""%(
          constraint,
          self,
        ))
    else:
      constraint.fields = [self.name]
    if hasattr( self, '_Schema__namespace'):
      constraint._Schema__namespace = self._Schema__namespace
  def __iter__( self ):
    """Iterate over the sub-elements of this schema"""
    for constraint in self.constraints:
      self.checkConstraint( constraint )
      yield constraint

  def foreign( self ):
    """Determine whether we have a foreign-key constraint"""
    for c in self.constraints:
      if c.dbConstraintType == ForeignKeyConstraint.dbConstraintType:
        return c
    return None


FieldSchemas = list_types.listof(
  FieldSchema,
  name = "FieldSchemas",
  dataType = "list.FieldSchemas",
)

class BaseTableSchema( ParentSchema ):
  """Base-class for table-like schemas"""
  name = common.StringProperty(
    'name', """Name of the table (required)""",
    setDefaultOnGet = 0,
  )
  fields = common.ListProperty(
    'fields', """FieldSchema objects for the table""",
    baseType = FieldSchemas,
  )
  properties = common.DictionaryProperty(
    'properties', """Table/View's property-descriptor objects

    Created from fields via wrapping
    """,
    setDefaultOnGet = 0,
    defaultFunction = lambda property,client: client.buildProperties()
  )
  propertiesCalculated = common.BooleanProperty(
    'propertiesCalculated',
    """Whether property-set was automatically calculated

  Used to determine whether to re-calculate if necessary
  """,
    defaultValue = 0,
    setDefaultOnGet = 0,
  )

  collectionClass = common.ClassByNameProperty(
    'collectionClass', """The class used to wrap result-sets as collection objects""",
    defaultValue = 'pytable.dbresultset.DBResultSet',
  )
  itemClass = common.ClassByNameProperty(
    'itemClass', """The class used to wrap result-set-row objects""",
    defaultFunction = lambda property,client: client.buildRowClass(),
    setDefaultOnGet = 1,
  )
  baseItemClass = common.ClassByNameProperty(
    'baseItemClass', """The default base-class for itemclass""",
    defaultValue = 'pytable.dbrow.DBRow',
    setDefaultOnGet = 0,
  )

  def buildRowClass( self ):
    """Build a new row-class if we don't have an explicitly-specified one"""
    baseClasses = (self.baseItemClass,)
    if self.name:
      name = self.name
    else:
      name = 'QueryRow'
    properties = self.properties.copy()
    properties['schema'] = self
    for badchar in ('.','-'):
      name = name.replace( badchar, '_' )
    return type( str(name), baseClasses, properties)
    
  def buildProperties( self ):
    """Build dbdescriptors for this table's properties/fields"""
    properties = {}
    for field in self.fields:
      new = field.buildProperty()
      properties[ new.name ] = new
    return properties



class TableSchema(BaseTableSchema):
  """High-level representation of a table's structure/schema

  The TableSchema object encodes information about
  a particular table in the database.  It does not
  include information about, for instance, view-level
  joins or interdependencies, though it does (if
  possible) include information about relational
  integrity constraints such as references to other
  tables.
  """
  dbObjectType = "TABLE"
  indices = common.ListProperty(
    'indices', """IndexSchema objects for indices on this table""",
    #baseType = IndexSchemas, # set below due to mutual dependencies
  )
  constraints = common.ListProperty(
    'constraints', """Constraint objects which constrain this table""",
    baseType = ConstraintSchemas,
  )
  # triggers
  inherits = common.StringsProperty(
    "inherits", """List of tables from which this table inherits
    
  Inherits is a PostgreSQL extension that creates an
  object-relational system with single and multiple
  inheritence types.  See PostgreSQL docs for details.
  """,
  )
  temporary = common.BooleanProperty(
    'temporary',
    """Whether this table should be created in temporary storage

  Temporary tables do not persist across connections
  """,
    defaultValue = 0,
    setDefaultOnGet = 0,
  )
  withOIDs = common.BooleanProperty(
    "withOIDs", """Whether table should explicitly use OIDs (postgres only)""",
    defaultValue = False,
    setDefaultOnGet = False,
  )
  defaultRecords = common.ListProperty(
    "defaultRecords", """Set of default records to be included in table on creation""",
  )
  def defaultFriendlyNameField( property, client ):
    """Try to figure out if there's a good candidate for friendlyName"""
    possibleNames = [ client.name + '_name', client.name+'name', 'name' ]
    for field in client.fields:
      if field.name.lower() in possibleNames:
        return field.name
    raise AttributeError( """Couldn't find a default friendlyName for table %r"""%(getattr(client, 'name', "<unknown table name>"),))
  friendlyNameField = common.StringProperty(
    "friendlyNameField", """Field to be used as friendly name for rows (i.e. used in UIs)""",
    defaultFunction = defaultFriendlyNameField,
  )
  del defaultFriendlyNameField

  _uniqueKeys = None
  def getUniqueKeys( self ):
    """Get unique key-sets for this table

    Any of these key-sets can be used to uniquely
    identify a row in the table.
    """
    if self._uniqueKeys is not None:
      return self._uniqueKeys
    primary = None
    sets = []
    for item in self:
      if isUnique( item ):
        if isPrimary( item ):
          primary = tuple( item.fields )
        else:
          sets.append( tuple( item.fields ))
    if primary:
      sets.insert(0, primary)
    self._uniqueKeys = sets
    return sets
  def __iter__( self ):
    """Iterate over the sub-elements of this schema"""
    for field in self.fields:
      yield field
      for item in field:
        yield item
    for index in self.indices:
      index.table = self.name
      yield index
    for constraint in self.constraints:
      yield constraint
    

  def fromConnection( cls, connection, tableName, **named):
    """Use introspection to build a table-structure object

    Relies on the connection's driver's action queries
    to provide the introspection features required for
    building the various sub-elements of the table
    structure.
    """
    if hasattr( connection.driver, "tableStructure"):
      new = connection.driver.tableStructure(
        cursor = connection.cursor(),
        tableName = tableName,
        tableClass = cls,
        descriptorClass = FieldSchema,
        indexClass = IndexSchema,
        **named
      )
      new.propertiesCalculated = 1
      return new
    else:
      raise NotImplementedError("""The connection %r does not implement table structure queries"""%(connection,))
  fromConnection = classmethod(fromConnection)
  def resolve( self ):
    """Attempt to resolve all references (check consistency)

    This will scan through each sub-element looking for
    name-references.  Each sub-element will have a pointer
    created for their active namespace, and each name will
    be checked in that namespace to determine that it does
    actually exist.  Sub-elements may perform additional
    checks (such as data-type agreement) as well.
    """
    for item in self:
      item.buildNamespace()
    for item in self:
      item._Schema__namespace = self
      if hasattr( item, 'resolve'):
        item.resolve()
    if hasattr( self, 'friendlyNameField'):
      self.lookupName( self.friendlyNameField )
      
  actions = [
    sqlquery.SQLQuery(
      name="query",
      sql = """SELECT * FROM %(tableName)s;"""
    ),
  ]
  def query( self, query=None, connection = None, **named ):
    """Do query returning records from thistableview import 

    query -- the sqlquery.SQLQuery to be run
    connection -- the connection to use to run the query
    named -- named arguments to pass to the query
    """
    if query is None:
      query = self.actionByName( 'query' )
    elif isinstance( query, (str,unicode)):
      queryObject = self.actionByName( query )
      if queryObject:
        query = queryObject
      else:
        query = sqlquery.SQLQuery(sql = query, debug=named.get('debug',0))
    if not connection:
      raise TypeError( """Query %r called with a NULL connection %r"""%(query, connection,))
    if query:
      if not hasattr( named, 'tableName'):
        named['tableName'] = self.name
      cursor = query( connection, **named )
      return self.collectionClass(
        schema = self,
        cursor=cursor,
      )
    else:
      raise TypeError("""Called query on a view %r without a query action defined"""%(
        self,
      ))


TableSchemas = list_types.listof(
  TableSchema,
  name = "TableSchemas",
  dataType = "list.TableSchemas",
)

class IndexSchema( Schema ):
  """Schema for defining a particular index (on a particular table)

  properties are the participating properties for the index
  """
  dbObjectType = "INDEX"
  name = common.StringProperty(
    'name', """Name of the index""",
    setDefaultOnGet = 0,
    defaultValue = "",
  )
  table = common.StringProperty(
    "table", """The table on which we are going to operate""",
  )
  fields = common.StringsProperty(
    'fields', """Set of field-names to index""",
  )
  unique = common.BooleanProperty(
    'unique', """Whether index values must be unique""",
    defaultValue = 1,
  )
  primary = common.BooleanProperty(
    'primary', """Whether index is a primary-key index""",
    defaultValue = 0,
  )
  accessMethod = common.StringProperty(
    "accessMethod", """The internal access method for the table

  Internally will likely default to "BTREE", but we
  specify default as "" to use the DB's default.

  This is not necessarily supported by your db, of course.
  """,
  )
  functionName = common.StringProperty(
    "functionName", """SQL function name taking fields and returning indexable value""",
  )
  where = common.StringProperty(
    "where", """SQL-formatted string defining a test expression

  Only records matching the where expression will be
  included in the index.
  """,
  )
  def resolve( self ):
    """Attempt to resolve all references (check consistency)

    This will scan through each sub-element looking for
    name-references.  Each sub-element will have a pointer
    created for their active namespace, and each name will
    be checked in that namespace to determine that it does
    actually exist.  Sub-elements may perform additional
    checks (such as data-type agreement) as well.
    """
    table = self.lookupName( self.table, requiredType=BaseTableSchema )
    assert table, """Couldn't get table for index %r"""%(self,)
    assert isinstance( table, BaseTableSchema ), """Table %r referenced by index %r appears not to be a table: %r!"""%(
      self.table, self, table,
    )
    for field in self.fields:
      try:
        table.lookupName( field )
      except NameError:
        raise NameError(
          """Couldn't get field %r for index %r from tablerknownfieldssSchemaadatabasenamespacesubschema import 

  AFAIK only postgresql actually supports this
  feature, which creates a new namespace for views,
  indices, tables and sequences
  """
  dbObjectType = "SCHEMA"
  name = common.StringProperty(
    'name', """Name of the schema/namespace""",
    defaultValue = "",
  )
  tables = common.ListProperty(
    'tables', """Tables in the schema""",
    baseType = TableSchemas,
  )
  sequences = common.ListProperty(
    "sequences", """Sequence/counter objects in the schema""",
    baseType = SequenceSchemas,
  )
  def __iter__( self ):
    """Iterate over our sub-elements"""
    for table in self.tables:
      table._Schema__namespace = self
      yield table
    for sequence in self.sequences:
      sequence._Schema__namespace = self
      yield sequence
  def resolve( self ):
    """Attempt to resolve all references (check consistency)

    This will scan through each sub-element looking for
    name-references.  Each sub-element will have a pointer
    created for their active namespace, and each name will
    be checked in that namespace to determine that it does
    actually exist.  Sub-elements may perform additional
    checks (such as data-type agreement) as well.
    """
    # we can hold sequences, tables, views, sub-schemas
    for item in self:
      if not item.name.startswith( '%s.'%(self.name,)):
        item.name = '%s.%s'%(self.name, item.name)
    for item in self:
      item.buildNamespace()
    for item in self:
      item._Schema__namespace = self
      if hasattr( item, 'resolve'):
        item.resolve()
NamespaceSchemas = list_types.listof(
  NamespaceSchema,
  name = "NamespaceSchemas",
  dataType = "list.NamespaceSchemas",
)


class DatabaseSchema( NamespaceSchema ):
  """Schema for an overall database object"""
  dbObjectType = "DATABASE"
  name = common.StringProperty(
    'name', """Name of the database""",
    defaultValue = "",
  )
  namespaces = common.ListProperty(
    "namespaces", """Sub-namespaces of the database""",
    baseType= NamespaceSchemas,
  )
  readOnly = common.BooleanProperty (
    "readOnly","""Disable all writing to the database if true""",
    defaultValue = 0,
  )
  driver = basic.BasicProperty(
    "driver", """Optional binding to the driver with which the schema works""",
  )
  def resolve( self, driver=None ):
    """Attempt to resolve all references (check consistency)

    This will scan through each sub-element looking for
    name-references.  Each sub-element will have a pointer
    created for their active namespace, and each name will
    be checked in that namespace to determine that it does
    actually exist.  Sub-elements may perform additional
    checks (such as data-type agreement) as well.
    """
    if driver:
      self.driver = driver
    self.buildNamespace()
    for item in self:
      item._Schema__namespace = self
      if hasattr( item, 'resolve'):
        item.resolve()
  def __iter__( self ):
    """Iterate over our sub-elements"""
    for namespace in self.namespaces:
      yield namespace
    for value in super( DatabaseSchema, self ).__iter__():
      yield value
  def lookupName( self, name=None, requiredType=None, alreadySeen=None ):
    """Lookup the name with schema-aware resolution"""
    if isinstance( name, (str,unicode)):
      if '.' in name:
        try:
          if alreadySeen is None:
            alreadySeen = {}
          alreadySeen[id(self)] = True
          namespace = super( DatabaseSchema, self ).lookupName(
            name = name.split( '.' )[0],
            alreadySeen = alreadySeen,
          )
        except NameError, err:
          pass
        else:
          return namespace.lookupName( 
            name=name, requiredType = requiredType,
            alreadySeen = alreadySeen 
          )
    return super( DatabaseSchema, self ).lookupName( 
      name=name, requiredType = requiredType,
      alreadySeen = alreadySeen
    )
  # procedures
  # users, groups, permissions
  # table-mapping name:schema
  def __repr__( self ):
    return """%s( name=%r, tables=[%s])"""%(
      self.__class__.__name__,
      self.name,
      ",".join( [x.name for x in self.tables]),
    )
  __str__ = __repr__
  
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.