Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 19

Show
Ignore:
Timestamp:
02/19/07 19:33:18
Author:
fumanchu
Message:

First crack at an insert_into method. Resultset class renamed to 'Dataset'. All selects now generate a Table object with columns which match the results of the select. SelectWriter?.columns is now SelectWriter?.input_list, output_list, and 'result' (the new table).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/objects.py

    r18 r19  
    4242     
    4343    def alias(self, oldname, newname): 
    44         """Add a new key for the Index with the given, existing key. 
     44        """Move the Index at the given, existing key to the new key. 
    4545         
    4646        Consumer code should call this method when user-supplied index 
    47         names do not match the names in the database. This does not 
    48         remove the old key; both keys may be used to refer to the same 
    49         Index object. 
     47        names do not match the names in the database. 
    5048        """ 
    5149        if oldname == newname: 
     
    188186     
    189187    def alias(self, oldname, newname): 
    190         """Add a new key for the Column with the given, existing key. 
     188        """Move the Column at the given, existing key to the new key. 
    191189         
    192190        Consumer code should call this method when user-supplied column 
    193         names do not match the names in the database. This does not 
    194         remove the old key; both keys may be used to refer to the same 
    195         Column object. 
     191        names do not match the names in the database. 
    196192        """ 
    197193        if oldname == newname: 
     
    414410         
    415411        attrs = self.keys() 
    416         data = self.schema.db.select(self, attrs, restriction) 
    417         for row in data
     412        dataset = self.schema.db.select(self, attrs, restriction) 
     413        for row in dataset
    418414            row = dict(zip(attrs, row)) 
    419             if restriction and data.imperfect: 
     415            if restriction and dataset.imperfect: 
    420416                # Run a dummy object through our restriction before yielding. 
    421417                if not restriction(_ImperfectDummy(**row)): 
     
    575571     
    576572    def alias(self, oldname, newname): 
    577         """Add a new key for the Table with the given, existing key. 
     573        """Move the Table at the given, existing key to the new key. 
    578574         
    579575        Consumer code should call this method when user-supplied table 
    580         names do not match the names in the database. This does not 
    581         remove the old key; both keys may be used to refer to the same 
    582         Table object. 
     576        names do not match the names in the database. 
    583577        """ 
    584578        if oldname == newname: 
     
    773767    def log(self, msg): 
    774768        pass 
     769     
     770    def schema(self, name): 
     771        return self.schemaclass(self, name) 
    775772     
    776773    #                               Naming                               # 
     
    837834     
    838835    def fetch(self, query, conn=None): 
    839         """Return rowdata, columns (name, type) for the given query. 
    840          
    841         query should be a SQL query in string format 
     836        """Return rowdata, columns(name, type) for the given query. 
     837         
     838        query should be a SQL query in string format. 
     839         
    842840        rowdata will be an iterable of iterables containing the result values. 
    843841        columns will be an iterable of (column name, data type) pairs. 
     
    849847     
    850848    def select(self, relation, attributes, restriction=None, distinct=False): 
    851         """Yield matching data, coerced to Python types (where known).""" 
     849        """Return a Dataset of matching data, coerced to Python types.""" 
    852850        sel = self.selectwriter(self, relation, attributes, restriction) 
    853         data, _ = self.fetch(sel.sql(distinct)) 
    854         return ResultSet(data, sel.columns, sel.imperfect) 
    855      
    856     def schema(self, name): 
    857         return self.schemaclass(self, name) 
    858  
    859  
    860 class ResultSet: 
    861      
    862     def __init__(self, data, columns, imperfect): 
     851        data, coldefs = self.fetch(sel.sql(distinct)) 
     852        return Dataset(data, coldefs, sel.result, sel.imperfect) 
     853     
     854    def insert_into(self, name, relation, attributes, restriction=None, 
     855                    distinct=False): 
     856        """INSERT matching data INTO a new table and return the Table. 
     857         
     858        The 'name' argument will be used to name the new table in the 
     859        database. The new table will be returned, and will also be added 
     860        to the schema; the key will be the provided name. If you want to 
     861        use a different key for this table, call schema.alias(name, newkey) 
     862        after this method. 
     863        """ 
     864        sel = self.selectwriter(self, relation, attributes, restriction) 
     865        newtable = sel.result 
     866        newtable.name = newtable.schema.table_name(name) 
     867        newtable.qname = self.quote(newtable.name) 
     868         
     869        # CREATE TABLE 
     870        newtable.schema[name] = newtable 
     871         
     872        if sel.imperfect: 
     873            sql = sel.sql(distinct) 
     874            errors.warn("The requested INSERT INTO could not produce perfect " 
     875                        "SQL. The creation of the new table could take a " 
     876                        "long time, since it must fetch each row and INSERT " 
     877                        "it into the new table manually. %r" % sql) 
     878             
     879            # SELECT ALL 
     880            data, coldefs = self.fetch(sel.sql(distinct)) 
     881             
     882            names = [x[0] for x in coldefs] 
     883            dataset = Dataset(data, coldefs, newtable, sel.imperfect) 
     884            for row in dataset: 
     885                row = dict(zip(names, row)) 
     886                if restriction and dataset.imperfect: 
     887                    # Run a dummy object through our restriction before inserting. 
     888                    if not restriction(_ImperfectDummy(**row)): 
     889                        continue 
     890                # INSERT INTO 
     891                newtable.insert(**row) 
     892        else: 
     893            sql = ("INSERT INTO %s (%s) %s" % 
     894                   (newtable.qname, ", ".join(sel.output_list), 
     895                    sel.sql(distinct))) 
     896            self.execute(sql) 
     897         
     898        return newtable 
     899 
     900 
     901class Dataset: 
     902    """A populated relation; the result of a SELECT.""" 
     903     
     904    def __init__(self, data, coldefs, table, imperfect): 
    863905        self.data = data 
    864         self.columns = columns 
     906        self.coldefs = coldefs 
     907        self.table = table 
     908        self.coerce = self.table.schema.db.adapterfromdb.coerce 
    865909        self.imperfect = imperfect 
    866910        self.cursor = 0 
     
    877921         
    878922        coerced_row = [] 
    879         for i, (table, col, qname) in enumerate(self.columns): 
     923        for i, desc in enumerate(self.coldefs): 
     924            name, type = desc[:2] 
    880925            val = row[i] 
    881             if table and col: 
    882                 val = table.schema.db.adapterfromdb.coerce(val, col.dbtype, col.pytype) 
     926            col = self.table[name] 
     927            val = self.coerce(val, col.dbtype, col.pytype) 
    883928            coerced_row.append(val) 
    884929        return coerced_row 
  • trunk/geniusql/select.py

    r18 r19  
    176176        if isinstance(tos, tuple): 
    177177            # The name in question refers to a DB column (see visit_LOAD_FAST). 
    178             tablename, table = tos 
     178            alias, table = tos 
    179179            col = table[name] 
    180             atom = ColumnWrapper('%s.%s' % (tablename, col.qname), col) 
     180            atom = ColumnWrapper('%s.%s' % (alias, col.qname), col) 
    181181        else: 
    182182            # 'tos.name' will reference an attribute of the tos object. 
     
    513513        # Then we can tag the wrappers with "alias" metadata with impunity. 
    514514        if isinstance(relation, Join): 
     515            if isinstance(relation.table1, Join): 
     516                self.result = relation.table2.schema.table('') 
     517            else: 
     518                self.result = relation.table1.schema.table('') 
    515519            self.tables = self.wrap(relation) 
    516520            self.fromclause = self.joinclause(self.tables) 
    517521        else: 
     522            self.result = relation.schema.table('') 
    518523            self.tables = [self.db.joinwrapper(relation)] 
    519524            self.fromclause = relation.qname 
     
    521526        self.whereclause, self.imperfect = self.where() 
    522527         
    523         self.columns = [] 
     528        self.input_list = [] 
     529        self.output_list = [] 
    524530        if self.attributes is None: 
    525531            # Return all columns 
    526532            for t in self.tables: 
    527                 self.columns.extend(self._get_columns(t, None)
     533                self._get_columns(t, None
    528534        else: 
    529535            if isinstance(relation, Join): 
    530536                for t, attrs in zip(self.tables, self.attributes): 
    531                     self.columns.extend(self._get_columns(t, attrs)) 
    532             else: 
     537                    self._get_columns(t, attrs) 
     538            else: 
     539                # 'relation' is a single Table object. 
    533540                for a in self.attributes: 
    534                     col = relation[a] 
    535                     self.columns.append((relation, col, col.qname)) 
     541                    self._copy_column(relation, relation.qname, relation[a]) 
     542     
     543    def _copy_column(self, table, alias, col): 
     544        newcol = col.copy() 
     545        newcol.key = False 
     546        newcol.autoincrement = False 
     547        newcol.sequence_name = None 
     548        newcol.initial = 1 
     549         
     550        selname = '%s.%s' % (alias, col.qname) 
     551        if newcol.name in self.result: 
     552            newcol.name = '%s_%s' % (table.name, newcol.name) 
     553            newcol.qname = table.schema.db.quote(newcol.name) 
     554            selname = '%s AS %s' % (selname, newcol.qname) 
     555        self.input_list.append(selname) 
     556        self.output_list.append(newcol.qname) 
     557        self.result[newcol.name] = newcol 
    536558     
    537559    def _get_columns(self, tablewrapper, attrs=None): 
    538         """Return a list of (column, full quoted name) pairs.""" 
    539         tablename = tablewrapper.alias or tablewrapper.qname 
     560        """Add columns from the given table to our result table.""" 
     561        alias = tablewrapper.alias or tablewrapper.qname 
    540562        table = tablewrapper.table 
    541563        if attrs is None: 
     
    544566            cols = [(col.key, col) for col in table.itervalues()] 
    545567            cols.sort() 
    546             return [(table, col, '%s.%s' % (tablename, col.qname)) 
    547                     for iskey, col in cols] 
    548         else: 
    549             cols = [] 
     568            for iskey, col in cols: 
     569                self._copy_column(table, alias, col) 
     570        else: 
    550571            for a in attrs: 
    551                 col = table[a] 
    552                 cols.append((table, col, '%s.%s' % (tablename, col.qname))) 
    553             return cols 
    554      
    555     def sql(self, distinct=False): 
     572                self._copy_column(table, alias, table[a]) 
     573     
     574    def sql(self, distinct=False, into=""): 
    556575        """Return an SQL SELECT statement.""" 
    557576        if distinct: 
     
    560579            d = '' 
    561580         
    562         cols = ', '.join([qname for t, c, qname in self.columns]) 
     581        cols = ', '.join(self.input_list) 
     582         
     583        if into: 
     584            into = " INTO %s" % into 
    563585         
    564586        w = self.whereclause 
     
    568590            w = "" 
    569591         
    570         return "SELECT %s%s FROM %s%s;" % (d, cols, self.fromclause, w) 
     592        return "SELECT %s%s%s FROM %s%s;" % (d, cols, into, self.fromclause, w) 
    571593     
    572594    def where(self): 
  • trunk/geniusql/test/zoo_fixture.py

    r18 r19  
    404404        try: 
    405405            # views 
    406             legs = [x[0] for x in db.select(schema['Animal'], ['Legs'])] 
     406            view = db.select(schema['Animal'], ['Legs']) 
     407            legs = [x[0] for x in view] 
    407408            legs.sort() 
    408409            self.assertEqual(legs, [1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 100, 1000000]) 
     
    639640        ostrich = schema['Animal'].select(Species='Ostrich') 
    640641        self.assertEqual(ostrich, None) 
     642     
     643    def test_insert_into(self): 
     644        newtable = db.insert_into('fishers', 
     645                                  schema['Animal'] << schema['Food'], 
     646                                  [('ID', 'Species', 'ZooID'), 
     647                                   ('ID', 'Name')], 
     648                                  lambda a, f: f.Name == 'Live Fish') 
     649        self.assertEqual(set(newtable.keys()), 
     650                         set(['ID', 'Species', 'ZooID', 'Food_ID', 'Name'])) 
     651        self.assertEqual(newtable.select_all(), 
     652                         [{'Food_ID': 2, 'ZooID': 4, 'Name': u'Live Fish', 
     653                           'ID': 8, 'Species': u'Emperor Penguin'}, 
     654                          {'Food_ID': 2, 'ZooID': 4, 'Name': u'Live Fish', 
     655                           'ID': 9, 'Species': u'Adelie Penguin'}] 
     656                         ) 
    641657     
    642658##    def test_zzz_Schema_Upgrade(self):