Changeset 19
- Timestamp:
- 02/19/07 19:33:18
- Files:
-
- trunk/geniusql/objects.py (modified) (8 diffs)
- trunk/geniusql/select.py (modified) (6 diffs)
- trunk/geniusql/test/zoo_fixture.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/geniusql/objects.py
r18 r19 42 42 43 43 def alias(self, oldname, newname): 44 """ Add a new key for the Index with the given, existingkey.44 """Move the Index at the given, existing key to the new key. 45 45 46 46 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. 50 48 """ 51 49 if oldname == newname: … … 188 186 189 187 def alias(self, oldname, newname): 190 """ Add a new key for the Column with the given, existingkey.188 """Move the Column at the given, existing key to the new key. 191 189 192 190 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. 196 192 """ 197 193 if oldname == newname: … … 414 410 415 411 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: 418 414 row = dict(zip(attrs, row)) 419 if restriction and data .imperfect:415 if restriction and dataset.imperfect: 420 416 # Run a dummy object through our restriction before yielding. 421 417 if not restriction(_ImperfectDummy(**row)): … … 575 571 576 572 def alias(self, oldname, newname): 577 """ Add a new key for the Table with the given, existingkey.573 """Move the Table at the given, existing key to the new key. 578 574 579 575 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. 583 577 """ 584 578 if oldname == newname: … … 773 767 def log(self, msg): 774 768 pass 769 770 def schema(self, name): 771 return self.schemaclass(self, name) 775 772 776 773 # Naming # … … 837 834 838 835 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 842 840 rowdata will be an iterable of iterables containing the result values. 843 841 columns will be an iterable of (column name, data type) pairs. … … 849 847 850 848 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.""" 852 850 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 901 class Dataset: 902 """A populated relation; the result of a SELECT.""" 903 904 def __init__(self, data, coldefs, table, imperfect): 863 905 self.data = data 864 self.columns = columns 906 self.coldefs = coldefs 907 self.table = table 908 self.coerce = self.table.schema.db.adapterfromdb.coerce 865 909 self.imperfect = imperfect 866 910 self.cursor = 0 … … 877 921 878 922 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] 880 925 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) 883 928 coerced_row.append(val) 884 929 return coerced_row trunk/geniusql/select.py
r18 r19 176 176 if isinstance(tos, tuple): 177 177 # The name in question refers to a DB column (see visit_LOAD_FAST). 178 tablename, table = tos178 alias, table = tos 179 179 col = table[name] 180 atom = ColumnWrapper('%s.%s' % ( tablename, col.qname), col)180 atom = ColumnWrapper('%s.%s' % (alias, col.qname), col) 181 181 else: 182 182 # 'tos.name' will reference an attribute of the tos object. … … 513 513 # Then we can tag the wrappers with "alias" metadata with impunity. 514 514 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('') 515 519 self.tables = self.wrap(relation) 516 520 self.fromclause = self.joinclause(self.tables) 517 521 else: 522 self.result = relation.schema.table('') 518 523 self.tables = [self.db.joinwrapper(relation)] 519 524 self.fromclause = relation.qname … … 521 526 self.whereclause, self.imperfect = self.where() 522 527 523 self.columns = [] 528 self.input_list = [] 529 self.output_list = [] 524 530 if self.attributes is None: 525 531 # Return all columns 526 532 for t in self.tables: 527 self. columns.extend(self._get_columns(t, None))533 self._get_columns(t, None) 528 534 else: 529 535 if isinstance(relation, Join): 530 536 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. 533 540 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 536 558 537 559 def _get_columns(self, tablewrapper, attrs=None): 538 """ Return a list of (column, full quoted name) pairs."""539 tablename= tablewrapper.alias or tablewrapper.qname560 """Add columns from the given table to our result table.""" 561 alias = tablewrapper.alias or tablewrapper.qname 540 562 table = tablewrapper.table 541 563 if attrs is None: … … 544 566 cols = [(col.key, col) for col in table.itervalues()] 545 567 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: 550 571 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=""): 556 575 """Return an SQL SELECT statement.""" 557 576 if distinct: … … 560 579 d = '' 561 580 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 563 585 564 586 w = self.whereclause … … 568 590 w = "" 569 591 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) 571 593 572 594 def where(self): trunk/geniusql/test/zoo_fixture.py
r18 r19 404 404 try: 405 405 # 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] 407 408 legs.sort() 408 409 self.assertEqual(legs, [1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 100, 1000000]) … … 639 640 ostrich = schema['Animal'].select(Species='Ostrich') 640 641 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 ) 641 657 642 658 ## def test_zzz_Schema_Upgrade(self):
