home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / SQLMOD.PY < prev    next >
Encoding:
Python Source  |  1999-07-28  |  22.9 KB  |  734 lines

  1. """Database modification statement semantics"""
  2.  
  3. import sqlsem
  4.  
  5. # ordering of ddef storage is important so, eg, index defs
  6. # follow table defs.
  7.  
  8. class Ordered_DDF:
  9.     """mixin for DDF statement sorting, subclass defines s.cmp(o)"""
  10.     def __cmp__(self, other):
  11.         try:
  12.             #print "comparing", self.name, other.name
  13.             try:
  14.                 sc = self.__class__
  15.                 oc = other.__class__
  16.                 #print sc, oc
  17.             except:
  18.                 #print "punting 1", -1
  19.                 return -1
  20.             if sc in ddf_order and oc in ddf_order:
  21.                 test = cmp(ddf_order.index(sc), ddf_order.index(oc))
  22.                 #print "ddforder", test
  23.                 if test: return test
  24.                 return self.cmp(other)
  25.             else:
  26.                 test = cmp(sc, oc)
  27.                 #print "punting 2", test
  28.                 return test
  29.         except:
  30.             #import sys
  31.             #print "exception!"
  32.             #print sys.exc_type, sys.exc_value
  33.             return -1
  34.             
  35.     def __coerce__(self, other):
  36.         return (self, other)
  37.     def cmp(self, other):
  38.         """redefine if no name field"""
  39.         return cmp(self.name, other.name)
  40.  
  41. CTFMT = """\
  42. CREATE TABLE %s (
  43.   %s 
  44.   )"""
  45.        
  46. class CreateTable(Ordered_DDF):
  47.    """create table operation"""
  48.    
  49.    def __init__(self, name, colelts):
  50.        self.name = name
  51.        self.colelts = colelts
  52.        self.indb = None # db in which to create
  53.        
  54.    def initargs(self):
  55.        return (self.name, [])
  56.        
  57.    def marshaldata(self):
  58.        from sqlsem import serialize
  59.        return map(serialize, self.colelts)
  60.        
  61.    def demarshal(self, args):
  62.        from sqlsem import deserialize
  63.        self.colelts = map(deserialize, args)
  64.        
  65.    def __repr__(self):
  66.        from string import join
  67.        elts = list(self.colelts)
  68.        elts = map(repr, elts)
  69.        return CTFMT % (self.name, join(elts, ",\n  "))
  70.        
  71.    def relbind(self, db):
  72.        """check that table doesn't already exist"""
  73.        if db.has_relation(self.name):
  74.           raise NameError, "cannot create %s, exists" % (self.name,)
  75.        self.indb = db
  76.        return self
  77.        
  78.    def eval(self, dyn=None):
  79.        "create the relation now"
  80.        # datatypes currently happily ignored :)
  81.        db = self.indb
  82.        if db is None:
  83.           raise ValueError, "unbound or executed"
  84.        self.indb = None
  85.        name = self.name
  86.        if db.has_relation(self.name):
  87.           raise NameError, "relation %s exists, cannot create" % (self.name,)
  88.        db.touched = 1
  89.        attnames = []
  90.        for x in self.colelts:
  91.            attnames.append(x.colid)
  92.        from gfdb0 import Relation0
  93.        r = Relation0(attnames)
  94.        # must store if new (unset for reloads)
  95.        r.touched = 1
  96.        db[name] = r
  97.        db.add_datadef(name, self)
  98.        log = db.log
  99.        if log is not None:
  100.            log.log(self)
  101.        
  102. viewfmt = """\
  103. CREATE VIEW %s (%s) AS
  104. %s"""
  105.        
  106. class CreateView(sqlsem.SimpleRecursive, Ordered_DDF):
  107.    """CREATE VIEW name (namelist) AS selection"""
  108.    
  109.    # note: no check for cross-references on drops!
  110.    def __init__(self, name, namelist, selection):
  111.        self.name = name
  112.        self.namelist = namelist
  113.        self.selection = selection
  114.        self.indb = None
  115.        
  116.    def __repr__(self):
  117.        return viewfmt % (self.name, self.namelist, self.selection)
  118.        
  119.    def initargs(self):
  120.        return (self.name, self.namelist, self.selection)
  121.        
  122.    def relbind(self, db):
  123.        self.indb = db
  124.        name = self.name
  125.        if db.has_datadef(name):
  126.           raise NameError, "(view) datadef %s exists" % name
  127.        # don't bind the selection yet
  128.        return self
  129.        
  130.    def eval(self, dyn=None):
  131.        "create the view"
  132.        db = self.indb
  133.        name = self.name
  134.        if db is None:
  135.            raise ValueError, "create view %s unbound or executed" % name
  136.        self.indb = None
  137.        if db.has_relation(name):
  138.            raise ValueError, "create view %s, name exists" % name
  139.        db.touched = 1
  140.        from gfdb0 import View
  141.        v = View(self.name, self.namelist, self.selection, db)
  142.        db[name] = v
  143.        db.add_datadef(name, self)
  144.        log = db.log
  145.        if log is not None:
  146.            log.log(self)
  147.        
  148. CREATEINDEXFMT = """\
  149. CREATE %sINDEX %s ON %s (
  150.    %s
  151.    )"""
  152.        
  153. class CreateIndex(sqlsem.SimpleRecursive, Ordered_DDF):
  154.    """create index operation"""
  155.    def __init__(self, name, tablename, atts, unique=0):
  156.        self.name = name
  157.        self.tablename=tablename
  158.        self.atts = atts
  159.        self.indb = None
  160.        self.target = None
  161.        self.unique = unique
  162.        
  163.    def initargs(self):
  164.        return (self.name, self.tablename, self.atts, self.unique)
  165.        
  166.    def __cmp__(self, other):
  167.        oc = other.__class__
  168.        if oc is CreateTable:
  169.           return 1 # after all create tables
  170.        sc = self.__class__
  171.        if oc is not sc:
  172.           return cmp(sc, oc)
  173.        else:
  174.           return cmp(self.name, other.name)
  175.           
  176.    def __coerce__(self, other):
  177.        return (self, other)
  178.        
  179.    def __repr__(self):
  180.        from string import join
  181.        un = ""
  182.        if self.unique: un="UNIQUE "
  183.        innards = join(self.atts, ",\n   ")
  184.        return CREATEINDEXFMT % (un, self.name, self.tablename, innards)
  185.        
  186.    def relbind(self, db):
  187.        name = self.name
  188.        self.indb = db
  189.        if db.has_datadef(name):
  190.           raise NameError, `name`+": data def exists"
  191.        try:
  192.           self.target = db.get_for_update(self.tablename) #db[self.tablename]
  193.        except:
  194.           raise NameError, `self.tablename`+": no such relation"
  195.        return self
  196.           
  197.    def eval(self, dyn=None):
  198.        from gfdb0 import Index
  199.        db = self.indb
  200.        if db is None:
  201.           raise ValueError, "create index unbound or executed"
  202.        self.indb = None
  203.        rel = self.target
  204.        if rel is None:
  205.           raise ValueError, "create index not bound to relation"
  206.        db.touched = 1
  207.        self.the_index = the_index = Index(self.name, self.atts, unique=self.unique)
  208.        rel.add_index(the_index)
  209.        name = self.name
  210.        db.add_datadef(name, self)
  211.        db.add_index(name, the_index)
  212.        log = db.log
  213.        if log is not None:
  214.            log.log(self)
  215.        
  216. class DropIndex(sqlsem.SimpleRecursive):
  217.    def __init__(self, name):
  218.        self.name = name
  219.        self.indb = None
  220.        
  221.    def initargs(self):
  222.        return (self.name,)
  223.        
  224.    def __repr__(self):
  225.        return "DROP INDEX %s" % (self.name,)
  226.        
  227.    def relbind(self, db):
  228.        self.indb = db
  229.        if not db.has_datadef(self.name):
  230.           raise NameError, `self.name`+": no such index"
  231.        return self
  232.           
  233.    def eval(self, dyn=None):
  234.        db = self.indb
  235.        self.indb=None
  236.        if db is None:
  237.           raise ValueError, "drop index executed or unbound"
  238.        db.touched = 1
  239.        indexname = self.name
  240.        createindex = db.datadefs[indexname]
  241.        index = createindex.the_index
  242.        relname = createindex.tablename
  243.        rel = db[relname]
  244.        rel.drop_index(index)
  245.        db.drop_datadef(indexname)
  246.        db.drop_index(indexname)
  247.        log = db.log
  248.        if log is not None:
  249.            log.log(self)
  250.        
  251. class DropTable(sqlsem.SimpleRecursive):
  252.    def __init__(self, name):
  253.        self.name = name
  254.        self.indb = None
  255.    def initargs(self):
  256.        return (self.name,)
  257.    def __repr__(self):
  258.        return "DROP TABLE %s" % (self.name,)
  259.    def relbind(self, db):
  260.        self.indb = db
  261.        name = self.name
  262.        if not db.has_relation(name):
  263.           raise NameError, `self.name` + ": cannot delete, no such table/view"
  264.        self.check_kind(name, db)
  265.        return self
  266.    def check_kind(self, name, db):
  267.        if db[name].is_view:
  268.           raise ValueError, "%s is VIEW, can't DROP TABLE" % name
  269.    def eval(self, dyn):
  270.        db = self.indb
  271.        if db is None:
  272.           raise ValueError, "unbound or executed"
  273.        db.touched = 1
  274.        self.indb = None
  275.        self.relbind(db)
  276.        name = self.name
  277.        rel = db[name]
  278.        rel.drop_indices(db)
  279.        db.drop_datadef(name)
  280.        del db[name]
  281.        log = db.log
  282.        if log is not None:
  283.            log.log(self)
  284.             
  285. class DropView(DropTable):
  286.    """DROP VIEW name"""
  287.    def __repr__(self):
  288.        return "DROP VIEW %s" % self.name
  289.    def check_kind(self, name, db):
  290.        if not db[name].is_view:
  291.           raise ValueError, "%s is TABLE, can't DROP VIEW" % name
  292.        
  293. COLDEFFMT = "%s %s %s %s"
  294.  
  295. class ColumnDef(sqlsem.SimpleRecursive):
  296.  
  297.    def __init__(self, colid, datatype, defaults, constraints):
  298.        self.colid = colid
  299.        self.datatype = datatype
  300.        self.defaults = defaults
  301.        self.constraints = constraints
  302.        
  303.    def initargs(self):
  304.        return (self.colid, self.datatype, self.defaults, self.constraints)
  305.        
  306.    def __repr__(self):
  307.        defaults = self.defaults
  308.        if defaults is None: defaults=""
  309.        constraints = self.constraints
  310.        if constraints is None: constraints = ""
  311.        return COLDEFFMT % (self.colid, self.datatype, defaults, constraints)
  312.        
  313.  
  314.  
  315. def evalcond(cond, eqs, target, dyn, rassns, translate, invtrans):
  316.        """factored out shared op between Update and Delete."""
  317.        if dyn:
  318.           #print "dyn", dyn
  319.           from sqlsem import dynamic_binding
  320.           dynbind = dynamic_binding(len(dyn), dyn)
  321.           if len(dynbind)>1:
  322.              raise ValueError, "only one dynamic binding allowed for UPDATE"
  323.           dynbind1 = dynbind = dynbind[0]
  324.           if eqs is not None:
  325.              dynbind1 = dynbind.remap(eqs)
  326.           if dynbind1 is None:
  327.              # inconsistent
  328.              return
  329.           dynbind = dynbind1 + dynbind
  330.           if rassns is not None:
  331.              rassns = rassns + invtrans * dynbind
  332.              if rassns.Clean() is None:
  333.                 # inconsistent
  334.                 return
  335.           else:
  336.              rassns = invtrans * dynbind
  337.           #print "dynbind", dynbind
  338.           #print "rassn", rassns
  339.        else:
  340.           dynbind = None
  341.        # get tuple set, try to use an index
  342.        index = None
  343.        if rassns is not None:
  344.           known = rassns.keys()
  345.           index = target.choose_index(known)
  346.        if index is None:
  347.           (tuples, seqnums) = target.rows(1)
  348.        else:
  349.           #print "using index", index.name
  350.           (tuples, seqnums) = index.matches(rassns)
  351.        ltuples = len(tuples)
  352.        buffer = [0] * ltuples
  353.        rtups = range(ltuples)
  354.        for i in rtups:
  355.            tup = tuples[i]
  356.            #print tup
  357.            ttup = translate * tup
  358.            if dynbind:
  359.               ttup = (ttup + dynbind).Clean()
  360.            if ttup is not None:
  361.               buffer[i] = ttup
  362.        #print "buffer", buffer
  363.        #print "cond", cond
  364.        #for x in buffer:
  365.            #print "before", x
  366.        test = cond(buffer)
  367.        #print "test", test
  368.        return (test, rtups, seqnums, tuples)
  369.  
  370.  
  371. UPDFMT = """\
  372. UPDATE %s
  373. SET %s
  374. WHERE %s"""
  375.  
  376. # optimize to use indices and single call to "cond"
  377. class UpdateOp(sqlsem.SimpleRecursive):
  378.    def __init__(self, name, assns, condition):
  379.        self.name = name
  380.        self.assns = assns
  381.        self.condition = condition
  382.        
  383.    def initargs(self):
  384.        return (self.name, self.assns, self.condition)
  385.        
  386.    def __repr__(self):
  387.        return UPDFMT % (self.name, self.assns, self.condition)
  388.        
  389.    def relbind(self, db):
  390.        self.indb = db
  391.        name = self.name
  392.        target = self.target = db.get_for_update(name)
  393.        (attb, relb, amb, ambatts) = db.bindings( [ (name, name) ] )
  394.        assns = self.assns = self.assns.relbind(attb, db)
  395.        cond = self.condition = self.condition.relbind(attb, db)
  396.        constraints = cond.constraints
  397.        if constraints is not None:
  398.           eqs = self.eqs = constraints.eqs
  399.           cassns = constraints.assns
  400.        else:
  401.           cassns = eqs = self.eqs = None
  402.        #print constraints, eqs
  403.        # check that atts of assns are atts of target
  404.        #print dir(assns)
  405.        resultatts = assns.attorder
  406.        from sqlsem import kjbuckets
  407.        kjSet = kjbuckets.kjSet
  408.        kjGraph = kjbuckets.kjGraph
  409.        resultatts = kjSet(resultatts)
  410.        allatts = kjSet(target.attribute_names)
  411.        self.preserved = allatts - resultatts
  412.        huh = resultatts - allatts
  413.        if huh:
  414.           raise NameError, "%s lacks %s attributes" % (name, huh.items())
  415.        # compute projection
  416.        assnsatts = kjGraph(assns.domain().items()).neighbors(name)
  417.        condatts = kjGraph(cond.domain().items()).neighbors(name)
  418.        condatts = condatts+assnsatts
  419.        #print "condatts", condatts
  420.        translate = kjbuckets.kjDict()
  421.        for att in condatts:
  422.            translate[ (name, att) ] = att
  423.        self.translate = translate
  424.        invtrans= self.invtrans = ~translate
  425.        if cassns is not None:
  426.           self.rassns = invtrans * cassns
  427.        else:
  428.           self.rassns = None
  429.        #print "cassns,rassns", cassns, self.rassns
  430.        #print translate
  431.        # compute domain of self.assns 
  432.        # (do nothing with it, should add sanity check!)
  433.        assns_domain = self.assns.domain()
  434.        return self
  435.        
  436.    def eval(self, dyn=None):
  437.        indb = self.indb
  438.        name = self.name
  439.        cond = self.condition
  440.        cond.uncache()
  441.        assns = self.assns
  442.        assns.uncache()
  443.        translate = self.translate
  444.        preserved = self.preserved
  445.        target = self.target
  446.        rassns = self.rassns
  447.        eqs = self.eqs
  448.        invtrans = self.invtrans
  449.        #print "assns", assns, assns.__class__
  450.        #print "cond", cond
  451.        #print "eqs", eqs
  452.        #print "target", target
  453.        #print "dyn", dyn
  454.        #print "rassns", rassns
  455.        #print "translate", translate
  456.        #print "invtrans", invtrans
  457.        (test, rtups, seqnums, tuples) = evalcond(
  458.            cond, eqs, target, dyn, rassns, translate, invtrans)
  459.        # shortcut
  460.        if not test: return
  461.        self.indb.touched = 1
  462.        tt = type
  463.        from types import IntType
  464.        #print test
  465.        (tps, attorder) = assns.map(test)
  466.        count = 0
  467.        newseqs = list(rtups)
  468.        newtups = list(rtups)
  469.        for i in rtups:
  470.            new = tps[i]
  471.            if tt(new) is not IntType and new is not None:
  472.               seqnum = seqnums[i]
  473.               old = tuples[i]
  474.               if preserved:
  475.                  new = new + preserved*old
  476.               newtups[count] = new
  477.               newseqs[count] = seqnum
  478.               count = count + 1
  479.        if count:
  480.            newseqs = newseqs[:count]
  481.            newtups = newtups[:count]
  482.            target.reset_tuples(newtups, newseqs)
  483.            log = indb.log
  484.            if log is not None and not log.is_scratch:
  485.                from sqlsem import Reset_Tuples
  486.                op = Reset_Tuples(self.name)
  487.                op.set_data(newtups, newseqs, target)
  488.                log.log(op)
  489.  
  490.  
  491. class DeleteOp(sqlsem.SimpleRecursive):
  492.  
  493.    def __init__(self, name, where):
  494.        self.name = name
  495.        self.condition = where
  496.        
  497.    def initargs(self):
  498.        return (self.name, self.condition)
  499.        
  500.    def __repr__(self):
  501.        return "DELETE FROM %s WHERE %s" % (self.name, self.condition)
  502.        
  503.    def relbind(self, db):
  504.        self.indb = db
  505.        name = self.name
  506.        target = self.target = db.get_for_update(name)
  507.        (attb, relb, amb, ambatts) = db.bindings( [ (name, name) ] )
  508.        cond = self.condition = self.condition.relbind(attb, db)
  509.        # compute domain of cond
  510.        # do nothing with it (should add sanity check)
  511.        cond_domain = cond.domain()
  512.        constraints = cond.constraints
  513.        if constraints is not None:
  514.           cassns = constraints.assns
  515.           self.eqs = constraints.eqs
  516.        else:
  517.           self.eqs = cassns = None
  518.        # compute projection/rename
  519.        from sqlsem import kjbuckets
  520.        condatts = kjbuckets.kjGraph(cond.domain().items()).neighbors(name)
  521.        translate = kjbuckets.kjDict()
  522.        for att in condatts:
  523.            translate[(name, att)] = att
  524.        self.translate = translate
  525.        invtrans = self.invtrans = ~translate
  526.        if cassns is not None:
  527.           self.rassns = invtrans * cassns
  528.        else:
  529.           self.rassns = None
  530.        return self
  531.        
  532.    def eval(self, dyn=None):
  533.        # note, very similar to update case...
  534.        indb = self.indb
  535.        name = self.name
  536.        target = self.target
  537.        tuples = target.tuples
  538.        eqs = self.eqs
  539.        rassns = self.rassns
  540.        cond = self.condition
  541.        cond.uncache()
  542.        translate = self.translate
  543.        invtrans = self.invtrans
  544.        (test, rtups, seqnums, tuples) = evalcond(
  545.           cond, eqs, target, dyn, rassns, translate, invtrans)
  546.        # shortcut
  547.        if not test: return
  548.        indb.touched = 1
  549.        tt = type
  550.        from types import IntType
  551.        count = 0
  552.        newseqs = list(rtups)
  553.        #print "rtups", rtups
  554.        for i in rtups:
  555.            new = test[i]
  556.            if tt(new) is not IntType and new is not None:
  557.               seqnum = seqnums[i]
  558.               newseqs[count] = seqnum
  559.               count = count + 1
  560.        #print "newseqs", newseqs
  561.        #print "count", count
  562.        if count:
  563.            newseqs = newseqs[:count]
  564.            target.erase_tuples(newseqs)
  565.            log = indb.log
  566.            if log is not None and not log.is_scratch:
  567.                from sqlsem import Erase_Tuples
  568.                op = Erase_Tuples(self.name)
  569.                op.set_data(newseqs, target)
  570.                log.log(op)
  571.  
  572. INSFMT = """\
  573. INSERT INTO %s 
  574. %s
  575. %s"""
  576.  
  577. class InsertOp(sqlsem.SimpleRecursive):
  578.  
  579.    def __init__(self, name, optcolids, insertspec):
  580.        self.name = name
  581.        self.optcolids = optcolids
  582.        self.insertspec = insertspec
  583.        self.target = None # target relation
  584.        self.collector = None # name map for attribute translation
  585.        
  586.    def initargs(self):
  587.        return (self.name, self.optcolids, self.insertspec)
  588.        
  589.    def __repr__(self):
  590.        return INSFMT % (self.name, self.optcolids, self.insertspec)
  591.        
  592.    def relbind(self, db):
  593.        self.indb = db
  594.        name = self.name
  595.        # determine target relation
  596.        target = self.target = db.get_for_update(name)
  597.        targetatts = target.attributes()
  598.        from sqlsem import kjbuckets
  599.        kjSet = kjbuckets.kjSet
  600.        targetset = kjSet(targetatts)
  601.        # check or set colid bindings
  602.        colids = self.optcolids
  603.        if colids is None:
  604.           colids = self.optcolids = target.attributes()
  605.        colset = kjSet(colids)
  606.        ### for now all attributes must be in colset
  607.        cdiff = colset-targetset
  608.        if cdiff:
  609.           raise NameError, "%s: no such attributes in %s" % (cdiff.items(), name)
  610.        cdiff = targetset-colset
  611.        ### temporary!!!
  612.        if cdiff:
  613.           raise NameError, "%s: not set in insert on %s" % (cdiff.items(), name)
  614.        # bind the insertspec
  615.        insertspec = self.insertspec
  616.        self.insertspec = insertspec = insertspec.relbind(db)
  617.        # create a collector for result
  618.        from sqlsem import TupleCollector
  619.        collector = self.collector = TupleCollector()
  620.        # get ordered list of expressions to eval on bound attributes of insertspec
  621.        resultexps = insertspec.resultexps()
  622.        if len(resultexps)!=len(colset):
  623.           raise ValueError, "result and colset of differing length %s:%s" % (colset,resultexps)
  624.        pairs = map(None, colids, resultexps)
  625.        for (col,exp) in pairs:
  626.            collector.addbinding(col, exp)
  627.        return self
  628.        
  629.    def eval(self, dyn=None):
  630.        resultbts = self.insertspec.eval(dyn)
  631.        #print "resultbts", resultbts
  632.        # shortcut
  633.        if not resultbts: return
  634.        indb = self.indb
  635.        indb.touched = 1
  636.        (resulttups, resultatts) = self.collector.map(resultbts)
  637.        #print "resulttups", resulttups
  638.        if resulttups:
  639.            target = self.target
  640.            target.add_tuples(resulttups)
  641.            #target.regenerate_indices()
  642.            log = indb.log
  643.            if log is not None and not log.is_scratch:
  644.                from sqlsem import Add_Tuples
  645.                op = Add_Tuples(self.name)
  646.                op.set_data(resulttups, target)
  647.                log.log(op)
  648.  
  649.        
  650. Insert_dummy_arg = [ ( (1,1), 1 ) ]
  651.                      
  652. class InsertValues(sqlsem.SimpleRecursive):
  653.  
  654.    def __init__(self, List):
  655.        self.list = List
  656.        
  657.    def initargs(self):
  658.        return (self.list,)
  659.        
  660.    def __repr__(self):
  661.        return "VALUES " +` tuple(self.list) `
  662.        
  663.    def resultexps(self):
  664.        return self.list
  665.        
  666.    def relbind(self, db):
  667.        l = self.list
  668.        bindings = {}
  669.        for i in xrange(len(self.list)):
  670.            li = l[i]
  671.            l[i] = li.relbind(bindings, db)
  672.            # do nothing with domain, for now
  673.            li_domain = li.domain()
  674.        return self
  675.        
  676.    def eval(self, dyn=None):
  677.        if dyn:
  678.           from sqlsem import dynamic_binding
  679.           dynbt = dynamic_binding(len(dyn), dyn)
  680.        else:
  681.           # dummy value to prevent triviality
  682.           from sqlsem import kjbuckets
  683.           dynbt = [kjbuckets.kjDict(Insert_dummy_arg)]
  684.        #print "bindings", dynbt.assns
  685.        return dynbt # ??
  686.        
  687. class InsertSubSelect(sqlsem.SimpleRecursive):
  688.  
  689.    def __init__(self, subsel):
  690.        self.subsel = subsel
  691.        
  692.    def initargs(self):
  693.        return (self.subsel,)
  694.        
  695.    def __repr__(self):
  696.        return "[subsel] %s" % (self.subsel,)
  697.        
  698.    def resultexps(self):
  699.        # get list of result bindings
  700.        subsel = self.subsel
  701.        atts = self.subsel.attributes()
  702.        # bind each as "result.name"
  703.        exps = []
  704.        from sqlsem import BoundAttribute
  705.        for a in atts:
  706.            exps.append( BoundAttribute("result", a) )
  707.        return exps # temp
  708.        
  709.    def relbind(self, db):
  710.        subsel = self.subsel
  711.        self.subsel = subsel.relbind(db)
  712.        # do nothing with domain for now
  713.        #subsel_domain = subsel.domain()
  714.        return self
  715.        
  716.    def eval(self, dyn=None):
  717.        subsel = self.subsel
  718.        subsel.uncache()
  719.        rel = subsel.eval(dyn)
  720.        tups = rel.rows()
  721.        from sqlsem import BoundTuple ### temp
  722.        from sqlsem import kjbuckets
  723.        kjDict = kjbuckets.kjDict
  724.        for i in xrange(len(tups)):
  725.            tupsi = tups[i]
  726.            new = kjDict()
  727.            for k in tupsi.keys():
  728.                new[ ("result", k) ] = tupsi[k]
  729.            tups[i] = new
  730.        return tups
  731.        
  732. # ordering for archiving datadefs
  733. ddf_order = [CreateTable, CreateIndex, CreateView]
  734.