sqlmod.py :  » Web-Frameworks » Zope » Zope-2.6.0 » lib » python » Products » ZGadflyDA » gadfly » 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 » Web Frameworks » Zope 
Zope » Zope 2.6.0 » lib » python » Products » ZGadflyDA » gadfly » sqlmod.py
"""Database modification statement semantics"""

import sqlsem

# ordering of ddef storage is important so, eg, index defs
# follow table defs.

class Ordered_DDF:
    """mixin for DDF statement sorting, subclass defines s.cmp(o)"""
    def __cmp__(self, other):
        try:
            #print "comparing", self.name, other.name
            try:
                sc = self.__class__
                oc = other.__class__
                #print sc, oc
            except:
                #print "punting 1", -1
                return -1
            if sc in ddf_order and oc in ddf_order:
                test = cmp(ddf_order.index(sc), ddf_order.index(oc))
                #print "ddforder", test
                if test: return test
                return self.cmp(other)
            else:
                test = cmp(sc, oc)
                #print "punting 2", test
                return test
        except:
            #import sys
            #print "exception!"
            #print sys.exc_type, sys.exc_value
            return -1
            
    def __coerce__(self, other):
        return (self, other)
    def cmp(self, other):
        """redefine if no name field"""
        return cmp(self.name, other.name)

CTFMT = """\
CREATE TABLE %s (
  %s 
  )"""
       
class CreateTable(Ordered_DDF):
   """create table operation"""
   
   def __init__(self, name, colelts):
       self.name = name
       self.colelts = colelts
       self.indb = None # db in which to create
       
   def initargs(self):
       return (self.name, [])
       
   def marshaldata(self):
       from sqlsem import serialize
       return map(serialize, self.colelts)
       
   def demarshal(self, args):
       from sqlsem import deserialize
       self.colelts = map(deserialize, args)
       
   def __repr__(self):
       from string import join
       elts = list(self.colelts)
       elts = map(repr, elts)
       return CTFMT % (self.name, join(elts, ",\n  "))
       
   def relbind(self, db):
       """check that table doesn't already exist"""
       if db.has_relation(self.name):
          raise NameError, "cannot create %s, exists" % (self.name,)
       self.indb = db
       return self
       
   def eval(self, dyn=None):
       "create the relation now"
       # datatypes currently happily ignored :)
       db = self.indb
       if db is None:
          raise ValueError, "unbound or executed"
       self.indb = None
       name = self.name
       if db.has_relation(self.name):
          raise NameError, "relation %s exists, cannot create" % (self.name,)
       db.touched = 1
       attnames = []
       for x in self.colelts:
           attnames.append(x.colid)
       from gfdb0 import Relation0
       r = Relation0(attnames)
       # must store if new (unset for reloads)
       r.touched = 1
       db[name] = r
       db.add_datadef(name, self)
       log = db.log
       if log is not None:
           log.log(self)
       
viewfmt = """\
CREATE VIEW %s (%s) AS
%s"""
       
class CreateView(sqlsem.SimpleRecursive, Ordered_DDF):
   """CREATE VIEW name (namelist) AS selection"""
   
   # note: no check for cross-references on drops!
   def __init__(self, name, namelist, selection):
       self.name = name
       self.namelist = namelist
       self.selection = selection
       self.indb = None
       
   def __repr__(self):
       return viewfmt % (self.name, self.namelist, self.selection)
       
   def initargs(self):
       return (self.name, self.namelist, self.selection)
       
   def relbind(self, db):
       self.indb = db
       name = self.name
       if db.has_datadef(name):
          raise NameError, "(view) datadef %s exists" % name
       # don't bind the selection yet
       return self
       
   def eval(self, dyn=None):
       "create the view"
       db = self.indb
       name = self.name
       if db is None:
           raise ValueError, "create view %s unbound or executed" % name
       self.indb = None
       if db.has_relation(name):
           raise ValueError, "create view %s, name exists" % name
       db.touched = 1
       from gfdb0 import View
       v = View(self.name, self.namelist, self.selection, db)
       db[name] = v
       db.add_datadef(name, self)
       log = db.log
       if log is not None:
           log.log(self)
       
CREATEINDEXFMT = """\
CREATE %sINDEX %s ON %s (
   %s
   )"""
       
class CreateIndex(sqlsem.SimpleRecursive, Ordered_DDF):
   """create index operation"""
   def __init__(self, name, tablename, atts, unique=0):
       self.name = name
       self.tablename=tablename
       self.atts = atts
       self.indb = None
       self.target = None
       self.unique = unique
       
   def initargs(self):
       return (self.name, self.tablename, self.atts, self.unique)
       
   def __cmp__(self, other):
       oc = other.__class__
       if oc is CreateTable:
          return 1 # after all create tables
       sc = self.__class__
       if oc is not sc:
          return cmp(sc, oc)
       else:
          return cmp(self.name, other.name)
          
   def __coerce__(self, other):
       return (self, other)
       
   def __repr__(self):
       from string import join
       un = ""
       if self.unique: un="UNIQUE "
       innards = join(self.atts, ",\n   ")
       return CREATEINDEXFMT % (un, self.name, self.tablename, innards)
       
   def relbind(self, db):
       name = self.name
       self.indb = db
       if db.has_datadef(name):
          raise NameError, `name`+": data def exists"
       try:
          self.target = db.get_for_update(self.tablename) #db[self.tablename]
       except:
          raise NameError, `self.tablename`+": no such relation"
       return self
          
   def eval(self, dyn=None):
       from gfdb0 import Index
       db = self.indb
       if db is None:
          raise ValueError, "create index unbound or executed"
       self.indb = None
       rel = self.target
       if rel is None:
          raise ValueError, "create index not bound to relation"
       db.touched = 1
       self.the_index = the_index = Index(self.name, self.atts, unique=self.unique)
       rel.add_index(the_index)
       name = self.name
       db.add_datadef(name, self)
       db.add_index(name, the_index)
       log = db.log
       if log is not None:
           log.log(self)
       
class DropIndex(sqlsem.SimpleRecursive):
   def __init__(self, name):
       self.name = name
       self.indb = None
       
   def initargs(self):
       return (self.name,)
       
   def __repr__(self):
       return "DROP INDEX %s" % (self.name,)
       
   def relbind(self, db):
       self.indb = db
       if not db.has_datadef(self.name):
          raise NameError, `self.name`+": no such index"
       return self
          
   def eval(self, dyn=None):
       db = self.indb
       self.indb=None
       if db is None:
          raise ValueError, "drop index executed or unbound"
       db.touched = 1
       indexname = self.name
       createindex = db.datadefs[indexname]
       index = createindex.the_index
       relname = createindex.tablename
       rel = db[relname]
       rel.drop_index(index)
       db.drop_datadef(indexname)
       db.drop_index(indexname)
       log = db.log
       if log is not None:
           log.log(self)
       
class DropTable(sqlsem.SimpleRecursive):
   def __init__(self, name):
       self.name = name
       self.indb = None
   def initargs(self):
       return (self.name,)
   def __repr__(self):
       return "DROP TABLE %s" % (self.name,)
   def relbind(self, db):
       self.indb = db
       name = self.name
       if not db.has_relation(name):
          raise NameError, `self.name` + ": cannot delete, no such table/view"
       self.check_kind(name, db)
       return self
   def check_kind(self, name, db):
       if db[name].is_view:
          raise ValueError, "%s is VIEW, can't DROP TABLE" % name
   def eval(self, dyn):
       db = self.indb
       if db is None:
          raise ValueError, "unbound or executed"
       db.touched = 1
       self.indb = None
       self.relbind(db)
       name = self.name
       rel = db[name]
       rel.drop_indices(db)
       db.drop_datadef(name)
       del db[name]
       log = db.log
       if log is not None:
           log.log(self)
            
class DropView(DropTable):
   """DROP VIEW name"""
   def __repr__(self):
       return "DROP VIEW %s" % self.name
   def check_kind(self, name, db):
       if not db[name].is_view:
          raise ValueError, "%s is TABLE, can't DROP VIEW" % name
       
COLDEFFMT = "%s %s %s %s"

class ColumnDef(sqlsem.SimpleRecursive):

   def __init__(self, colid, datatype, defaults, constraints):
       self.colid = colid
       self.datatype = datatype
       self.defaults = defaults
       self.constraints = constraints
       
   def initargs(self):
       return (self.colid, self.datatype, self.defaults, self.constraints)
       
   def __repr__(self):
       defaults = self.defaults
       if defaults is None: defaults=""
       constraints = self.constraints
       if constraints is None: constraints = ""
       return COLDEFFMT % (self.colid, self.datatype, defaults, constraints)
       


def evalcond(cond, eqs, target, dyn, rassns, translate, invtrans):
       """factored out shared op between Update and Delete."""
       if dyn:
          #print "dyn", dyn
          from sqlsem import dynamic_binding
          dynbind = dynamic_binding(len(dyn), dyn)
          if len(dynbind)>1:
             raise ValueError, "only one dynamic binding allowed for UPDATE"
          dynbind1 = dynbind = dynbind[0]
          if eqs is not None:
             dynbind1 = dynbind.remap(eqs)
          if dynbind1 is None:
             # inconsistent
             return
          dynbind = dynbind1 + dynbind
          if rassns is not None:
             rassns = rassns + invtrans * dynbind
             if rassns.Clean() is None:
                # inconsistent
                return
          else:
             rassns = invtrans * dynbind
          #print "dynbind", dynbind
          #print "rassn", rassns
       else:
          dynbind = None
       # get tuple set, try to use an index
       index = None
       if rassns is not None:
          known = rassns.keys()
          index = target.choose_index(known)
       if index is None:
          (tuples, seqnums) = target.rows(1)
       else:
          #print "using index", index.name
          (tuples, seqnums) = index.matches(rassns)
       ltuples = len(tuples)
       buffer = [0] * ltuples
       rtups = range(ltuples)
       for i in rtups:
           tup = tuples[i]
           #print tup
           ttup = translate * tup
           if dynbind:
              ttup = (ttup + dynbind).Clean()
           if ttup is not None:
              buffer[i] = ttup
       #print "buffer", buffer
       #print "cond", cond
       #for x in buffer:
           #print "before", x
       test = cond(buffer)
       #print "test", test
       return (test, rtups, seqnums, tuples)


UPDFMT = """\
UPDATE %s
SET %s
WHERE %s"""

# optimize to use indices and single call to "cond"
class UpdateOp(sqlsem.SimpleRecursive):
   def __init__(self, name, assns, condition):
       self.name = name
       self.assns = assns
       self.condition = condition
       
   def initargs(self):
       return (self.name, self.assns, self.condition)
       
   def __repr__(self):
       return UPDFMT % (self.name, self.assns, self.condition)
       
   def relbind(self, db):
       self.indb = db
       name = self.name
       target = self.target = db.get_for_update(name)
       (attb, relb, amb, ambatts) = db.bindings( [ (name, name) ] )
       assns = self.assns = self.assns.relbind(attb, db)
       cond = self.condition = self.condition.relbind(attb, db)
       constraints = cond.constraints
       if constraints is not None:
          eqs = self.eqs = constraints.eqs
          cassns = constraints.assns
       else:
          cassns = eqs = self.eqs = None
       #print constraints, eqs
       # check that atts of assns are atts of target
       #print dir(assns)
       resultatts = assns.attorder
       from sqlsem import kjbuckets
       kjSet = kjbuckets.kjSet
       kjGraph = kjbuckets.kjGraph
       resultatts = kjSet(resultatts)
       allatts = kjSet(target.attribute_names)
       self.preserved = allatts - resultatts
       huh = resultatts - allatts
       if huh:
          raise NameError, "%s lacks %s attributes" % (name, huh.items())
       # compute projection
       assnsatts = kjGraph(assns.domain().items()).neighbors(name)
       condatts = kjGraph(cond.domain().items()).neighbors(name)
       condatts = condatts+assnsatts
       #print "condatts", condatts
       translate = kjbuckets.kjDict()
       for att in condatts:
           translate[ (name, att) ] = att
       self.translate = translate
       invtrans= self.invtrans = ~translate
       if cassns is not None:
          self.rassns = invtrans * cassns
       else:
          self.rassns = None
       #print "cassns,rassns", cassns, self.rassns
       #print translate
       # compute domain of self.assns 
       # (do nothing with it, should add sanity check!)
       assns_domain = self.assns.domain()
       return self
       
   def eval(self, dyn=None):
       indb = self.indb
       name = self.name
       cond = self.condition
       cond.uncache()
       assns = self.assns
       assns.uncache()
       translate = self.translate
       preserved = self.preserved
       target = self.target
       rassns = self.rassns
       eqs = self.eqs
       invtrans = self.invtrans
       #print "assns", assns, assns.__class__
       #print "cond", cond
       #print "eqs", eqs
       #print "target", target
       #print "dyn", dyn
       #print "rassns", rassns
       #print "translate", translate
       #print "invtrans", invtrans
       (test, rtups, seqnums, tuples) = evalcond(
           cond, eqs, target, dyn, rassns, translate, invtrans)
       # shortcut
       if not test: return
       self.indb.touched = 1
       tt = type
       from types import IntType
       #print test
       (tps, attorder) = assns.map(test)
       count = 0
       newseqs = list(rtups)
       newtups = list(rtups)
       for i in rtups:
           new = tps[i]
           if tt(new) is not IntType and new is not None:
              seqnum = seqnums[i]
              old = tuples[i]
              if preserved:
                 new = new + preserved*old
              newtups[count] = new
              newseqs[count] = seqnum
              count = count + 1
       if count:
           newseqs = newseqs[:count]
           newtups = newtups[:count]
           target.reset_tuples(newtups, newseqs)
           log = indb.log
           if log is not None and not log.is_scratch:
               from sqlsem import Reset_Tuples
               op = Reset_Tuples(self.name)
               op.set_data(newtups, newseqs, target)
               log.log(op)


class DeleteOp(sqlsem.SimpleRecursive):

   def __init__(self, name, where):
       self.name = name
       self.condition = where
       
   def initargs(self):
       return (self.name, self.condition)
       
   def __repr__(self):
       return "DELETE FROM %s WHERE %s" % (self.name, self.condition)
       
   def relbind(self, db):
       self.indb = db
       name = self.name
       target = self.target = db.get_for_update(name)
       (attb, relb, amb, ambatts) = db.bindings( [ (name, name) ] )
       cond = self.condition = self.condition.relbind(attb, db)
       # compute domain of cond
       # do nothing with it (should add sanity check)
       cond_domain = cond.domain()
       constraints = cond.constraints
       if constraints is not None:
          cassns = constraints.assns
          self.eqs = constraints.eqs
       else:
          self.eqs = cassns = None
       # compute projection/rename
       from sqlsem import kjbuckets
       condatts = kjbuckets.kjGraph(cond.domain().items()).neighbors(name)
       translate = kjbuckets.kjDict()
       for att in condatts:
           translate[(name, att)] = att
       self.translate = translate
       invtrans = self.invtrans = ~translate
       if cassns is not None:
          self.rassns = invtrans * cassns
       else:
          self.rassns = None
       return self
       
   def eval(self, dyn=None):
       # note, very similar to update case...
       indb = self.indb
       name = self.name
       target = self.target
       tuples = target.tuples
       eqs = self.eqs
       rassns = self.rassns
       cond = self.condition
       cond.uncache()
       translate = self.translate
       invtrans = self.invtrans
       (test, rtups, seqnums, tuples) = evalcond(
          cond, eqs, target, dyn, rassns, translate, invtrans)
       # shortcut
       if not test: return
       indb.touched = 1
       tt = type
       from types import IntType
       count = 0
       newseqs = list(rtups)
       #print "rtups", rtups
       for i in rtups:
           new = test[i]
           if tt(new) is not IntType and new is not None:
              seqnum = seqnums[i]
              newseqs[count] = seqnum
              count = count + 1
       #print "newseqs", newseqs
       #print "count", count
       if count:
           newseqs = newseqs[:count]
           target.erase_tuples(newseqs)
           log = indb.log
           if log is not None and not log.is_scratch:
               from sqlsem import Erase_Tuples
               op = Erase_Tuples(self.name)
               op.set_data(newseqs, target)
               log.log(op)

INSFMT = """\
INSERT INTO %s 
%s
%s"""

class InsertOp(sqlsem.SimpleRecursive):

   def __init__(self, name, optcolids, insertspec):
       self.name = name
       self.optcolids = optcolids
       self.insertspec = insertspec
       self.target = None # target relation
       self.collector = None # name map for attribute translation
       
   def initargs(self):
       return (self.name, self.optcolids, self.insertspec)
       
   def __repr__(self):
       return INSFMT % (self.name, self.optcolids, self.insertspec)
       
   def relbind(self, db):
       self.indb = db
       name = self.name
       # determine target relation
       target = self.target = db.get_for_update(name)
       targetatts = target.attributes()
       from sqlsem import kjbuckets
       kjSet = kjbuckets.kjSet
       targetset = kjSet(targetatts)
       # check or set colid bindings
       colids = self.optcolids
       if colids is None:
          colids = self.optcolids = target.attributes()
       colset = kjSet(colids)
       ### for now all attributes must be in colset
       cdiff = colset-targetset
       if cdiff:
          raise NameError, "%s: no such attributes in %s" % (cdiff.items(), name)
       cdiff = targetset-colset
       ### temporary!!!
       if cdiff:
          raise NameError, "%s: not set in insert on %s" % (cdiff.items(), name)
       # bind the insertspec
       insertspec = self.insertspec
       self.insertspec = insertspec = insertspec.relbind(db)
       # create a collector for result
       from sqlsem import TupleCollector
       collector = self.collector = TupleCollector()
       # get ordered list of expressions to eval on bound attributes of insertspec
       resultexps = insertspec.resultexps()
       if len(resultexps)!=len(colset):
          raise ValueError, "result and colset of differing length %s:%s" % (colset,resultexps)
       pairs = map(None, colids, resultexps)
       for (col,exp) in pairs:
           collector.addbinding(col, exp)
       return self
       
   def eval(self, dyn=None):
       resultbts = self.insertspec.eval(dyn)
       #print "resultbts", resultbts
       # shortcut
       if not resultbts: return
       indb = self.indb
       indb.touched = 1
       (resulttups, resultatts) = self.collector.map(resultbts)
       #print "resulttups", resulttups
       if resulttups:
           target = self.target
           target.add_tuples(resulttups)
           #target.regenerate_indices()
           log = indb.log
           if log is not None and not log.is_scratch:
               from sqlsem import Add_Tuples
               op = Add_Tuples(self.name)
               op.set_data(resulttups, target)
               log.log(op)

       
Insert_dummy_arg = [ ( (1,1), 1 ) ]
                     
class InsertValues(sqlsem.SimpleRecursive):

   def __init__(self, List):
       self.list = List
       
   def initargs(self):
       return (self.list,)
       
   def __repr__(self):
       return "VALUES " +` tuple(self.list) `
       
   def resultexps(self):
       return self.list
       
   def relbind(self, db):
       l = self.list
       bindings = {}
       for i in xrange(len(self.list)):
           li = l[i]
           l[i] = li.relbind(bindings, db)
           # do nothing with domain, for now
           li_domain = li.domain()
       return self
       
   def eval(self, dyn=None):
       if dyn:
          from sqlsem import dynamic_binding
          dynbt = dynamic_binding(len(dyn), dyn)
       else:
          # dummy value to prevent triviality
          from sqlsem import kjbuckets
          dynbt = [kjbuckets.kjDict(Insert_dummy_arg)]
       #print "bindings", dynbt.assns
       return dynbt # ??
       
class InsertSubSelect(sqlsem.SimpleRecursive):

   def __init__(self, subsel):
       self.subsel = subsel
       
   def initargs(self):
       return (self.subsel,)
       
   def __repr__(self):
       return "[subsel] %s" % (self.subsel,)
       
   def resultexps(self):
       # get list of result bindings
       subsel = self.subsel
       atts = self.subsel.attributes()
       # bind each as "result.name"
       exps = []
       from sqlsem import BoundAttribute
       for a in atts:
           exps.append( BoundAttribute("result", a) )
       return exps # temp
       
   def relbind(self, db):
       subsel = self.subsel
       self.subsel = subsel.relbind(db)
       # do nothing with domain for now
       #subsel_domain = subsel.domain()
       return self
       
   def eval(self, dyn=None):
       subsel = self.subsel
       subsel.uncache()
       rel = subsel.eval(dyn)
       tups = rel.rows()
       from sqlsem import BoundTuple### temp
fromsqlsemkjbuckets
       kjDict = kjbuckets.kjDict
       for i in xrange(len(tups)):
           tupsi = tups[i]
           new = kjDict()
           for k in tupsi.keys():
               new[ ("result", k) ] = tupsi[k]
           tups[i] = new
       return tups
       
# ordering for archiving datadefs
ddf_order = [CreateTable, CreateIndex, CreateView]
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.