Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 119

Show
Ignore:
Timestamp:
08/06/07 15:47:54
Author:
fumanchu
Message:

Optimizations:

  1. All adapter.pull methods now must do their own None checking.
  2. Cached boolean expressions.
  3. New SelectWriter?.result_table method for lazily forming the result Table.
  4. Optimized DatasetIterator? loop (cached col data).
  5. Optimized postgres pull methods (no need to check for unicode).
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/ast/geniusql/adapters.py

    r118 r119  
    159159        # sqlite 3 will return an int. 
    160160        # This construction should handle both. 
     161        if value is None: 
     162            return None 
    161163        return bool(int(value)) 
    162164 
     
    172174     
    173175    def pull(self, value, dbtype): 
     176        if value is None: 
     177            return None 
    174178        return bool(value) 
    175179 
     
    188192     
    189193    def pull(self, value, dbtype): 
     194        if value is None: 
     195            return None 
    190196        if isinstance(value, datetime.datetime): 
    191197            return value 
     
    204210     
    205211    def pull(self, value, dbtype): 
     212        if value is None: 
     213            return None 
    206214        # These are in order for a reason: datetime is a subclass of date! 
    207215        if isinstance(value, datetime.datetime): 
     
    223231     
    224232    def pull(self, value, dbtype): 
     233        if value is None: 
     234            return None 
    225235        if isinstance(value, datetime.time): 
    226236            return value 
     
    251261     
    252262    def pull(self, value, dbtype): 
     263        if value is None: 
     264            return None 
    253265        days, seconds = divmod(long(value), 86400) 
    254266        return datetime.timedelta(int(days), int(seconds)) 
     
    265277     
    266278    def pull(self, value, dbtype): 
     279        if value is None: 
     280            return None 
    267281        return float(value) 
    268282 
     
    287301     
    288302    def pull(self, value, dbtype): 
     303        if value is None: 
     304            return None 
    289305        return self.pytype(value) 
    290306 
     
    315331     
    316332    def pull(self, value, dbtype): 
     333        if value is None: 
     334            return None 
    317335        if isinstance(value, unicode): 
    318336            return value.encode(dbtype.encoding) 
     
    324342     
    325343    def pull(self, value, dbtype): 
     344        if value is None: 
     345            return None 
    326346        if isinstance(value, unicode): 
    327347            return value 
     
    349369     
    350370    def pull(self, value, dbtype): 
     371        if value is None: 
     372            return None 
    351373        # Coerce to str for pickle.loads restriction. 
    352374        if isinstance(value, unicode): 
     
    371393     
    372394    def pull(self, value, dbtype): 
     395        if value is None: 
     396            return None 
    373397        return self.pytype(value) 
    374398     
     
    410434     
    411435    def pull(self, value, dbtype): 
     436        if value is None: 
     437            return None 
    412438        return self.pytype(value) 
    413439 
     
    426452         
    427453        def pull(self, value, dbtype): 
     454            if value is None: 
     455                return None 
    428456            # pywin32 build 205 began support for returning 
    429457            # COM Currency objects as decimal objects. 
     
    462490         
    463491        def pull(self, value, dbtype): 
     492            if value is None: 
     493                return None 
    464494            if isinstance(value, float): 
    465495                value = repr(value) 
     
    478508            return str(value) 
    479509        def pull(self, value, dbtype): 
     510            if value is None: 
     511                return None 
    480512            if (isinstance(value, basestring) or 
    481513                (typerefs.decimal and 
     
    518550         
    519551        def pull(self, value, dbtype): 
     552            if value is None: 
     553                return None 
    520554            if isinstance(value, float): 
    521555                value = repr(value) 
  • branches/ast/geniusql/dbtypes.py

    r118 r119  
    551551        return self.known_types['bool'][0]() 
    552552     
     553    # These are not adapter.push(bool) (which are used on one side of  
     554    # a comparison). Instead, these are used when the whole (sub)expression 
     555    # is True or False, e.g. "WHERE TRUE", or "WHERE TRUE and 'a'.'b' = 3". 
     556    expr_true = "TRUE" 
     557    expr_false = "FALSE" 
     558     
     559    # Because True and False get used so much in deparsing, 
     560    # we include a memoized function for obtaining SQLExpressions. 
     561    _adapted_bools = None 
     562    def bool_exprs(self, exprclass): 
     563        """Return SQLExpressions for expr_true, expr_false, comp_true and comp_false.""" 
     564        t = self.database_type(bool) 
     565        adapter = t.default_adapter(bool) 
     566         
     567        expr_true = exprclass(self.expr_true, '', t, bool) 
     568        expr_true.adapter = adapter 
     569        expr_true.value = True 
     570         
     571        expr_false = exprclass(self.expr_false, '', t, bool) 
     572        expr_false.adapter = adapter 
     573        expr_false.value = False 
     574         
     575        if self._adapted_bools is None: 
     576            comp_true = adapter.push(True, t) 
     577            comp_false = adapter.push(False, t) 
     578            self._adapted_bools = (comp_true, comp_false) 
     579        else: 
     580            comp_true, comp_false = self._adapted_bools 
     581         
     582        comp_true = exprclass(comp_true, '', t, bool) 
     583        comp_true.adapter = adapter 
     584        comp_true.value = True 
     585         
     586        comp_false = exprclass(comp_false, '', t, bool) 
     587        comp_false.adapter = adapter 
     588        comp_false.value = False 
     589         
     590        return (expr_true, expr_false, comp_true, comp_false) 
     591     
    553592    def dbtype_for_datetime_datetime(self, hints): 
    554593        return self.known_types['datetime'][0]() 
  • branches/ast/geniusql/deparse.py

    r118 r119  
    127127    sql_bin_op = dict([(v, v) for v in astwalk.codewalk.binary_repr.itervalues()]) 
    128128     
    129     # These are not adapter.push(bool) (which are used on one side of  
    130     # a comparison). Instead, these are used when the whole (sub)expression 
    131     # is True or False, e.g. "WHERE TRUE", or "WHERE TRUE and 'a'.'b' = 3". 
    132     bool_true = "TRUE" 
    133     bool_false = "FALSE" 
    134      
    135129    def __init__(self, tables, expr, typeset): 
    136130        self.tables = tables 
     
    141135         
    142136        # Cache coerced booleans and None 
    143         self.true_expr = self.const(True, self.bool_true) 
    144         self.false_expr = self.const(False, self.bool_false) 
    145         booldbtype = self.typeset.database_type(bool) 
    146         booladapter = booldbtype.default_adapter(bool) 
    147         self.T = self.const(True, booladapter.push(True, booldbtype)) 
    148         self.F = self.const(False, booladapter.push(False, booldbtype)) 
     137        b = self.typeset.bool_exprs(SQLExpression) 
     138        self.expr_true, self.expr_false, self.comp_true, self.comp_false = b 
     139        for boolexpr in b: 
     140            self.exprcount += 1 
     141            boolexpr.name = "expr%s" % self.exprcount 
    149142        self.none_expr = SQLExpression("NULL", "expr0", None, type(None)) 
    150143         
     
    196189        if result is cannot_represent: 
    197190            # The entire expression could not be evaluated. 
    198             result = self.true_expr 
     191            result = self.expr_true 
    199192            self.imperfect = True 
    200         elif result == self.T
    201             result = self.true_expr 
    202         elif result == self.F
    203             result = self.false_expr 
     193        elif result == self.comp_true
     194            result = self.expr_true 
     195        elif result == self.comp_false
     196            result = self.expr_false 
    204197         
    205198        # Cache the result 
     
    240233                self.imperfect = True 
    241234                # Use TRUE for the term, so all records are returned. 
    242                 term = self.true_expr 
    243                 # Blurg. SQL Server is *so* picky. 
    244                 if term == self.T
    245                     term = self.true_expr 
    246                 elif term == self.F
    247                     term = self.false_expr 
     235                term = self.expr_true 
     236##                # Blurg. SQL Server is *so* picky. 
     237##                if term == self.comp_true
     238##                    term = self.expr_true 
     239##                elif term == self.comp_false
     240##                    term = self.expr_false 
    248241            newterms.append("(%s)" % term.sql) 
    249242        clause = self.get_expr(" AND ".join(newterms), bool) 
     
    261254                self.imperfect = True 
    262255                # Use TRUE for the term, so all records are returned. 
    263                 term = self.true_expr 
    264                 # Blurg. SQL Server is *so* picky. 
    265                 if term == self.T
    266                     term = self.true_expr 
    267                 elif term == self.F
    268                     term = self.false_expr 
     256                term = self.expr_true 
     257##                # Blurg. SQL Server is *so* picky. 
     258##                if term == self.comp_true
     259##                    term = self.expr_true 
     260##                elif term == self.comp_false
     261##                    term = self.expr_false 
    269262            newterms.append("(%s)" % term.sql) 
    270263        clause = self.get_expr(" OR ".join(newterms), bool) 
     
    327320    def visit_CallFunc(self, func, *args): 
    328321        # e.g. CallFunc(Name('min'), [Getattr(Name('v'), 'Date')], None, None) 
    329         args = list(args) 
    330         dstar_args = args.pop() 
    331         star_args = args.pop() 
     322        dstar_args = args[-1] 
     323        star_args = args[-2] 
    332324         
    333325        posargs = [] 
    334326        kwargs = {} 
    335         for arg in args
     327        for arg in args[:-2]
    336328            if isinstance(arg, astwalk.ast.Keyword): 
    337329                key, val = arg.name, self.walk(arg.value) 
     
    380372            return cannot_represent 
    381373         
    382         ops = list(ops) 
    383          
    384374        newterms = [] 
    385         while ops: 
    386             op = ops.pop(0) 
    387             op2 = self.walk(ops.pop(0)) 
     375        i = 0 
     376        while i < len(ops): 
     377            op, op2 = ops[i:i+2] 
     378            i += 2 
     379            op2 = self.walk(op2) 
    388380            if op2 is cannot_represent: 
    389381                return cannot_represent 
     
    408400                else: 
    409401                    raise ValueError("Non-equality Null comparisons not allowed.") 
    410             elif op in ('<', '<=', '==', '!=', '>', '>=')
     402            elif op in reverseop
    411403                try: 
    412404                    sql = op1.adapter.compare_op(op1, op, self.sql_cmp_op[op], op2) 
     
    491483            else: 
    492484                # Nothing will match the empty list, so return none. 
    493                 return self.false_expr 
     485                return self.expr_false 
    494486     
    495487    def builtins_icontainedby(self, op1, op2): 
  • branches/ast/geniusql/logic.py

    r118 r119  
    359359        e = logic.Expression(lambda x: x.a == 3 and x.b == 1) 
    360360    """ 
    361     co, names, consts = [], ['x', ], [None, ] 
    362     i = 0 
    363     for key, val in kwargs.iteritems(): 
    364         i += 1 
    365         names.append(key) 
    366         consts.append(val) 
     361    items = kwargs.items() 
     362    co = [] 
     363    kwlen = len(kwargs) 
     364    jump_target = (16 * kwlen) - 7 
     365    for i in xrange(1, kwlen + 1): 
    367366        co += [124, 0, 0, 
    368367               105, i, 0, 
    369368               100, i, 0, 
    370369               106, 2, 0, 
    371                111, 0, 0, 
     370               # Point all jump (111) instructions at len(co) - 4 
     371               111, jump_target - (((i - 1) * 16) + 12), 0, 
    372372               1, 
    373373               ] 
     
    377377    co.append(83) 
    378378     
    379     # Figure JUMP targets 
    380     for op in range(len(co)): 
    381         if co[op] == 111: 
    382             co[op + 1] = (len(co) - 4) - op 
     379    items = [('x', None)] + items 
     380    names, consts = zip(*items) 
    383381     
    384382    # Form code object and function. 
     
    387385    #      filename, name, firstlineno, lnotab[, freevars[, cellvars]]) 
    388386    co = CodeType(1, 1, 2, 67, ''.join(map(chr, co)), 
    389                   tuple(consts), tuple(names), ('x', ), 
    390                   '', '<lambda>', 1, '') 
     387                  consts, names, ('x', ), '', '<lambda>', 1, '') 
    391388    func = FunctionType(co, {}) 
    392389    return Expression(func, earlybind=False) 
  • branches/ast/geniusql/objects.py

    r118 r119  
    909909            (as positional arguments). 
    910910        """ 
    911         conn = self.connections.get() 
    912          
    913911        if not isinstance(query, select.Query): 
    914912            query = select.Query(*query) 
     
    916914        sel = self.selectwriter(self, query) 
    917915        sql = sel.sql(distinct, limit) 
    918         data, _ = self.fetch(sql, conn
     916        data, _ = self.fetch(sql
    919917         
    920918        d = Dataset(sel, data) 
     
    937935         
    938936        sel = self.selectwriter(self, query) 
    939         newtable = sel.result 
    940          
    941         # The selectwriter doesn't give the result table a name. 
    942         newtable.name = newtable.schema.table_name(name) 
    943         newtable.qname = self.quote(newtable.name) 
    944          
     937        newtable = sel.result_table(name) 
    945938        # CREATE TABLE 
    946939        newtable.schema[name] = newtable 
     
    965958        else: 
    966959            sql = ("INSERT INTO %s (%s) %s" % 
    967                    (newtable.qname, ", ".join(sel.output_list), selsql)) 
     960                   (newtable.qname, ", ".join(sel.output_qnames), selsql)) 
    968961            self.execute(sql) 
    969962         
     
    999992        self.dataset = dataset 
    1000993        self.dataiter = iter(dataset.data) 
     994         
     995        # pre-fetch cols and cache in an optimal format. 
     996        c = dataset.selector.output_cols 
     997        self.cols = [c[key] for key in dataset.selector.output_keys] 
    1001998     
    1002999    def __iter__(self): 
     
    10121009        under the assumption that the caller will do so itself. 
    10131010        """ 
    1014         d = self.dataset 
    1015          
    10161011        raw_row = self.dataiter.next() 
    1017          
    1018         row = [] 
    1019         for i, colkey in enumerate(d.selector.output_keys): 
    1020             val = raw_row[i] 
    1021             col = d.selector.result[colkey] 
    1022             # Any column value can be None. Don't coerce it. 
    1023             if val is not None: 
    1024                 val = col.adapter.pull(val, col.dbtype) 
    1025             row.append(val) 
    1026          
    1027         return row 
    1028  
     1012        return [col.adapter.pull(val, col.dbtype) for val, col 
     1013                in zip(raw_row, self.cols)] 
     1014 
  • branches/ast/geniusql/providers/postgres.py

    r118 r119  
    4444     
    4545    def pull(self, value, dbtype): 
     46        if value is None: 
     47            return None 
    4648        if isinstance(value, datetime.timedelta): 
    4749            return value 
     
    121123     
    122124    def pull(self, value, dbtype): 
     125        if value is None: 
     126            return None 
    123127        # Unescape octal sequences 
    124128        value = unescape_oct.sub(replace_unoct, value) 
    125         if isinstance(value, unicode): 
    126             return value.encode(dbtype.encoding) 
    127         else: 
    128             return str(value) 
     129         
     130        # Return values don't ever seem to be unicode 
     131##        if isinstance(value, unicode): 
     132##            return value.encode(dbtype.encoding) 
     133##        else: 
     134        return value 
    129135 
    130136 
     
    144150     
    145151    def pull(self, value, dbtype): 
     152        if value is None: 
     153            return None 
    146154        # Unescape octal sequences 
    147155        value = unescape_oct.sub(replace_unoct, value) 
    148         if isinstance(value, unicode): 
    149             return value 
    150         else: 
    151             return unicode(value, dbtype.encoding) 
     156        # Return values don't ever seem to be unicode 
     157##        if isinstance(value, unicode): 
     158##            return value 
     159##        else: 
     160        return unicode(value, dbtype.encoding) 
    152161 
    153162 
     
    169178     
    170179    def pull(self, value, dbtype): 
     180        if value is None: 
     181            return None 
    171182        # Unescape octal sequences 
    172183        value = unescape_oct.sub(replace_unoct, value) 
    173184         
    174         # Coerce to str for pickle.loads restriction. 
    175         if isinstance(value, unicode): 
    176             value = value.encode(dbtype.encoding) 
    177         else: 
    178             value = str(value) 
     185        # Return values don't ever seem to be unicode. 
     186##        # Coerce to str for pickle.loads restriction. 
     187##        if isinstance(value, unicode): 
     188##            value = value.encode(dbtype.encoding) 
     189##        else: 
     190##            value = str(value) 
    179191        return pickle.loads(value) 
    180192 
  • branches/ast/geniusql/select.py

    r118 r119  
    193193    input_list: a list of SQL expressions, one for each column in the 
    194194        SELECT clause. These will include any "expr AS name" alias. 
    195     output_list: a list of the SQL names (aliases), one per output column. 
     195    output_names: a list of the SQL names (aliases), one per output column. 
     196    output_qnames: a list of quoted SQL names (aliases), one per output column. 
    196197    output_keys: the keys for each column in the final table. 
     198    output_cols: a dict of source cols for the final table. 
    197199    """ 
    198200     
     
    209211        if isinstance(relation, Join): 
    210212            if isinstance(relation.table1, Join): 
    211                 self.result = relation.table2.schema.table('') 
    212             else: 
    213                 self.result = relation.table1.schema.table('') 
     213                self.schema = relation.table2.schema 
     214            else: 
     215                self.schema = relation.table1.schema 
    214216            self.tables = self.wrap(relation) 
    215217            self.fromclause = self.joinclause(self.tables) 
    216218        elif isinstance(relation, geniusql.Schema): 
    217219            # This is how we say we want to SELECT scalars (no FROM clause) 
    218             self.result = relation.table('') 
     220            self.schema = relation 
    219221            self.tables = [] 
    220222            self.fromclause = "" 
    221223        else: 
    222             self.result = relation.schema.table('') 
     224            self.schema = relation.schema 
    223225            self.tables = [self.db.joinwrapper(relation)] 
    224226            self.fromclause = relation.qname 
     
    227229         
    228230        self.input_list = [] 
    229         self.output_list = [] 
     231        self.output_names = [] 
     232        self.output_qnames = [] 
    230233        self.output_keys = [] 
     234        self.output_cols = {} 
    231235        self._groupby = [] 
    232236        if isinstance(query.attributes, logic.Expression): 
     
    257261                self.order = [relation[key].qname for key in query.order] 
    258262     
     263    def result_table(self, name): 
     264        """Return a new Table object for the result of this select. 
     265         
     266        This is too expensive to do when you don't need it, so it's 
     267        a separate function here. Try not to call it more than once 
     268        for a given SelectWriter instance. 
     269        """ 
     270        newtable = self.schema.table(name) 
     271        for colkey, name, qname in zip(self.output_keys, self.output_names, 
     272                                       self.output_qnames): 
     273            col = self.output_cols[colkey] 
     274            newcol = col.copy() 
     275            newcol.name = name 
     276            newcol.qname = qname 
     277            newcol.key = False 
     278            newcol.autoincrement = False 
     279            newcol.sequence_name = None 
     280            newcol.initial = 1 
     281            newtable[colkey] = newcol 
     282        return newtable 
     283     
    259284    def _copy_column(self, table, alias, colkey): 
    260285        """Copy the given column from the given table into our result table. 
     
    267292        """ 
    268293        col = table[colkey] 
    269         newcol = col.copy() 
    270         newcol.key = False 
    271         newcol.autoincrement = False 
    272         newcol.sequence_name = None 
    273         newcol.initial = 1 
    274          
     294         
     295        colname = col.name 
     296        colqname = col.qname 
    275297        if alias is None: 
    276298            # Single table. 
    277             selname = col.qname 
    278         else: 
    279             selname = '%s.%s' % (alias, col.qname) 
    280         if colkey in self.result: 
     299            selname = colqname 
     300        else: 
     301            selname = '%s.%s' % (alias, colqname) 
     302         
     303        if colkey in self.output_cols: 
    281304            # Get the key for the table. 
    282305            colkey = '%s_%s' % (table.schema.key_for(table), colkey) 
    283             newcol.name = '%s_%s' % (table.name, newcol.name) 
    284             newcol.qname = table.schema.db.quote(newcol.name) 
    285             selname = '%s AS %s' % (selname, newcol.qname) 
     306            colname = '%s_%s' % (table.name, colname) 
     307            colqname = table.schema.db.quote(colname) 
     308            selname = '%s AS %s' % (selname, colqname) 
    286309        self.input_list.append(selname) 
    287         self.output_list.append(newcol.qname) 
     310        self.output_names.append(colname) 
     311        self.output_qnames.append(colqname) 
    288312        self.output_keys.append(colkey) 
    289         self.result[colkey] = newcol 
     313        self.output_cols[colkey] = col 
    290314     
    291315    def sql(self, distinct=False, limit=None, into=""): 
     
    438462                                 self.query.attributes) 
    439463             
    440             if atom.name in self.result
     464            if atom.name in self.output_cols
    441465                bare_name = atom.name 
    442466                index = 1 
    443                 while atom.name in self.result
     467                while atom.name in self.output_cols
    444468                    atom.name = '%s%s' % (bare_name, index) 
    445469                    index += 1 
     
    448472            self.input_list.append('%s AS %s' % (atom.sql, qname)) 
    449473            self.output_keys.append(atom.name) 
    450             self.output_list.append(qname) 
     474            self.output_names.append(atom.name) 
     475            self.output_qnames.append(qname) 
    451476            if not atom.aggregate: 
    452477                self._groupby.append(atom.sql) 
    453478             
    454             col = objects.Column(atom.pytype, atom.dbtype, name=atom.name) 
    455             col.qname = self.db.quote(col.name) 
    456             col.adapter = atom.adapter 
    457             self.result[atom.name] = col 
     479            self.output_cols[atom.name] = atom 
    458480     
    459481    def deparse_order(self):