Changeset 23
- Timestamp:
- 03/08/07 18:52:55
- Files:
-
- trunk/geniusql/objects.py (modified) (8 diffs)
- trunk/geniusql/providers/ado.py (modified) (1 diff)
- trunk/geniusql/providers/firebird.py (modified) (1 diff)
- trunk/geniusql/providers/mysql.py (modified) (1 diff)
- trunk/geniusql/providers/postgres.py (modified) (1 diff)
- trunk/geniusql/providers/sqlite.py (modified) (10 diffs)
- trunk/geniusql/test/zoo_fixture.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/geniusql/objects.py
r22 r23 19 19 return key 20 20 raise ValueError("The given value could not be found: %r" % value) 21 22 def alias(self, oldname, newname): 23 """Move the object at the given, existing key to the new key. 24 25 Consumer code should call this method when user-supplied object 26 names do not match the names in the database. 27 """ 28 if oldname == newname: 29 return 30 31 obj = self[oldname] 32 if newname in self: 33 dict.__delitem__(self, newname) 34 dict.__delitem__(self, oldname) 35 dict.__setitem__(self, newname, obj) 21 36 22 37 … … 57 72 def __setstate__(self, state): 58 73 self.update(state) 59 60 def alias(self, oldname, newname):61 """Move the Index at the given, existing key to the new key.62 63 Consumer code should call this method when user-supplied index64 names do not match the names in the database.65 """66 if oldname == newname:67 return68 obj = self[oldname]69 if newname in self:70 dict.__delitem__(self, newname)71 dict.__delitem__(self, oldname)72 dict.__setitem__(self, newname, obj)73 74 74 75 def __setitem__(self, key, index): … … 90 91 91 92 92 class Column :93 class Column(object): 93 94 """A column in a table in a database. 94 95 … … 169 170 Table, or calls methods like 'rename'. 170 171 indices: a dict-like IndexSet of Index objects. 171 references: a dict of the form: {name: (nearColKey, farTableKey, farColKey)}. 172 references: a dict of the form: 173 {name: (near Column key, far Table key, far Column key)}. 172 174 """ 173 175 … … 210 212 self.indices.table = self 211 213 212 def alias(self, oldname, newname):213 """Move the Column at the given, existing key to the new key.214 215 Consumer code should call this method when user-supplied column216 names do not match the names in the database.217 """218 if oldname == newname:219 return220 221 obj = self[oldname]222 if newname in self:223 dict.__delitem__(self, newname)224 dict.__delitem__(self, oldname)225 dict.__setitem__(self, newname, obj)226 227 214 def _add_column(self, column): 228 215 """Internal function to add the column to the database.""" … … 309 296 return i 310 297 298 def add_primary(self): 299 """Set the PRIMARY KEY for this Table, using its Column.key values.""" 300 pk = [column.qname for column in self.itervalues() if column.key] 301 if pk: 302 self.schema.db.execute("ALTER TABLE %s ADD PRIMARY KEY (%s);" % 303 (self.qname, ", ".join(pk))) 304 305 def drop_primary(self): 306 """Remove any PRIMARY KEY for this Table.""" 307 raise NotImplementedError 311 308 312 309 # ---------------------------- OLTP/CRUD ---------------------------- # … … 332 329 if self[key].key: 333 330 ids[key] = value 334 return self.whereclause(**ids) 331 try: 332 return self.whereclause(**ids) 333 except Exception, x: 334 x.args += (inputs,) 335 raise 335 336 336 337 def insert(self, **inputs): … … 597 598 finally: 598 599 self._discover_lock.release() 599 600 def alias(self, oldname, newname):601 """Move the Table at the given, existing key to the new key.602 603 Consumer code should call this method when user-supplied table604 names do not match the names in the database.605 """606 if oldname == newname:607 return608 609 obj = self[oldname]610 if newname in self:611 dict.__delitem__(self, newname)612 dict.__delitem__(self, oldname)613 dict.__setitem__(self, newname, obj)614 600 615 601 def _column_name(self, tablename, columnkey): trunk/geniusql/providers/ado.py
r22 r23 378 378 conn = None 379 379 cat = None 380 381 def drop_primary(self): 382 """Remove any PRIMARY KEY for this Table.""" 383 db = self.schema.db 384 385 data, _ = db.fetch(adSchemaIndexes, schema=True) 386 pknames = [row[5] for row in data 387 if (self.name == row[2]) and row[6]] 388 for name in pknames: 389 db.execute('ALTER TABLE %s DROP CONSTRAINT %s;' 390 % (self.qname, name)) 380 391 381 392 trunk/geniusql/providers/firebird.py
r22 r23 301 301 302 302 return newids 303 304 def drop_primary(self): 305 """Remove any PRIMARY KEY for this Table.""" 306 db = self.schema.db 307 308 data, _ = db.fetch( 309 "SELECT RDB$CONSTRAINT_NAME FROM RDB$RELATION_CONSTRAINTS " 310 "WHERE (RDB$CONSTRAINT_TYPE = 'PRIMARY KEY') " 311 "AND (RDB$RELATION_NAME = '%s')" % self.name) 312 for row in data: 313 db.execute('ALTER TABLE %s DROP CONSTRAINT %s;' 314 % (self.qname, row[0].rstrip())) 303 315 304 316 trunk/geniusql/providers/mysql.py
r22 r23 181 181 def _grab_new_ids(self, idkeys, conn): 182 182 return {idkeys[0]: conn.insert_id()} 183 184 def drop_primary(self): 185 """Remove any PRIMARY KEY for this Table.""" 186 self.schema.db.execute('ALTER TABLE %s DROP PRIMARY KEY;' % self.qname) 183 187 184 188 trunk/geniusql/providers/postgres.py
r22 r23 131 131 newids[idkey] = data[0][0] 132 132 return newids 133 134 def drop_primary(self): 135 """Remove any PRIMARY KEY for this Table.""" 136 db = self.schema.db 137 138 # Get the OID of the table 139 data, _ = db.fetch("SELECT oid FROM pg_class WHERE " 140 "relname = '%s'" % self.name) 141 table_OID = data[0][0] 142 143 data, _ = db.fetch("SELECT conname, * FROM pg_constraint WHERE conrelid " 144 "= %s AND contype = 'p'" % table_OID) 145 for row in data: 146 constraint_name = row[0] 147 db.execute('ALTER TABLE %s DROP CONSTRAINT "%s";' 148 % (self.qname, constraint_name)) 133 149 134 150 trunk/geniusql/providers/sqlite.py
r22 r23 285 285 286 286 class SQLiteTable(geniusql.Table): 287 288 def _parent_key(self): 289 """Return the key of this Table in its parent Database.""" 290 names = [x for x in self.schema if self.schema[x].name == self.name] 291 return names[0] 287 """A table in a database; a dict of Column objects. 288 289 Values in this dict must be instances of Column (or a subclass of it). 290 Keys should be consumer-friendly names for each Column value. 291 292 name: the SQL name for this table (unquoted). 293 qname: the SQL name for this table (quoted). 294 schema: the schema for this table. 295 created: whether or not this Table has a concrete implementation in the 296 database. If False (the default), then changes to Table items can be 297 made with impunity. If True, then appropriate ALTER TABLE commands 298 are executed whenever a consumer adds or deletes items from the 299 Table, or calls methods like 'rename'. 300 indices: a dict-like IndexSet of Index objects. 301 references: a dict of the form: 302 {name: (near Column key, far Table key, far Column key)}. 303 304 Various versions of SQLite have limited support for ALTER TABLE. 305 When necessary, this class will compensate with the following process: 306 307 1. Create a temp table which has the desired new schema. 308 2. Copy the entire dataset from the original table to the temp table. 309 3. Drop the original table. 310 4. Re-create the original table with the desired new schema. 311 5. Copy the entire dataset from the temp table to the re-created table. 312 6. Drop the temporary table. 313 314 Needless to say, this can take a LOT longer than most other stores. 315 """ 292 316 293 317 def _temp_copy(self): 294 # Create the temporary table with the new fields (no indices). 318 # Make a temporary table. Callers then modify it before binding. 319 schema = self.schema 295 320 temptable = self.copy() 296 tempkey = "temp_" + self._parent_key() 297 temptable.name = self.schema.table_name(tempkey) 298 temptable.qname = self.schema.db.quote(temptable.name) 299 temptable.indices.clear() 321 tempkey = "temp_" + schema.key_for(self) 322 temptable.name = schema.table_name(tempkey) 323 temptable.qname = schema.db.quote(temptable.name) 324 # Update index names. 325 for key, i in temptable.indices.iteritems(): 326 i.name = schema.table_name("i" + temptable.name + i.colname) 327 i.qname = schema.db.quote(i.name) 328 i.tablename = temptable.name 300 329 return tempkey, temptable 301 330 302 def _copy_from_temp(self, temptable, t hiskey, tempkey):331 def _copy_from_temp(self, temptable, tempkey): 303 332 """Copy data from a temp table to a new table for self.""" 333 schema = self.schema 304 334 305 335 # Drop the old table and create the new, final table. … … 307 337 newtable.name = self.name 308 338 newtable.qname = self.qname 309 self.schema[thiskey] = newtable 339 # Update index names. 340 for key, i in newtable.indices.iteritems(): 341 i.name = schema.table_name("i" + newtable.name + i.colname) 342 i.qname = schema.db.quote(i.name) 343 i.tablename = newtable.name 344 schema[schema.key_for(self)] = newtable 310 345 311 346 # Copy data from the temp table to the final table. … … 313 348 # mixes up the fields (during rename, at least). 314 349 selfields = ", ".join([c.qname for c in temptable.values()]) 315 s elf.schema.db.execute("INSERT INTO %s (%s) SELECT %s FROM %s;" %316 (newtable.qname, selfields, selfields,317 temptable.qname))350 schema.db.execute("INSERT INTO %s (%s) SELECT %s FROM %s;" % 351 (newtable.qname, selfields, selfields, 352 temptable.qname)) 318 353 319 354 # Drop the intermediate table. 320 del s elf.schema[tempkey]355 del schema[tempkey] 321 356 322 357 if not _add_column_support: … … 337 372 # Add the new column to the copy. 338 373 dict.__setitem__(temptable, key, column) 339 # Bind the temp table to the DB .374 # Bind the temp table to the DB (this will call CREATE TABLE). 340 375 self.schema[tempkey] = temptable 341 376 … … 353 388 354 389 # Copy data from the temp table to a new table for self. 355 self._copy_from_temp(temptable, self._parent_key(),tempkey)390 self._copy_from_temp(temptable, tempkey) 356 391 357 392 def __delitem__(self, key): … … 369 404 # Drop the column from the copy. 370 405 dict.__delitem__(temptable, key) 371 # Bind the temp table to the DB .406 # Bind the temp table to the DB (this will call CREATE TABLE). 372 407 self.schema[tempkey] = temptable 373 408 374 409 # Copy data from the old table to the temp table. 375 selfields = [] 376 for k, c in temptable.iteritems(): 377 qname = c.qname 378 selfields.append(qname) 410 selfields = [c.qname for c in c in temptable.itervalues()] 379 411 self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 380 412 (temptable.qname, ", ".join(selfields), 381 413 self.qname)) 382 414 383 self._copy_from_temp(temptable, self._parent_key(),tempkey)415 self._copy_from_temp(temptable, tempkey) 384 416 385 417 if column.autoincrement: … … 401 433 # Make a temporary copy. 402 434 tempkey, temptable = self._temp_copy() 403 # Bind the temp table to the DB .435 # Bind the temp table to the DB (this will call CREATE TABLE). 404 436 self.schema[tempkey] = temptable 405 437 … … 415 447 self.qname)) 416 448 417 self._copy_from_temp(temptable, self._parent_key(),tempkey)449 self._copy_from_temp(temptable, tempkey) 418 450 419 451 def _grab_new_ids(self, idkeys, conn): … … 423 455 new_id = conn.sqlite_last_insert_rowid() 424 456 return {idkeys[0]: new_id} 457 458 def add_primary(self): 459 """Set the PRIMARY KEY for this Table, using its Column.key values.""" 460 pk = [column.qname for column in self.itervalues() if column.key] 461 if pk: 462 # Make a temporary copy. 463 tempkey, temptable = self._temp_copy() 464 # Bind the temp table to the DB. 465 self.schema[tempkey] = temptable 466 # Copy data from the old table to the temp table. 467 selfields = [c.qname for c in temptable.itervalues()] 468 self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 469 (temptable.qname, ", ".join(selfields), 470 self.qname)) 471 # Create the new table and copy data from the temp table to it. 472 self._copy_from_temp(temptable, tempkey) 473 474 def drop_primary(self): 475 """Remove any PRIMARY KEY for this Table.""" 476 # Make a temporary copy. 477 tempkey, temptable = self._temp_copy() 478 # Drop all .key's from the copy. 479 for col in temptable.itervalues(): 480 col.key = False 481 # Bind the temp table to the DB. 482 self.schema[tempkey] = temptable 483 484 # Copy data from the old table to the temp table. 485 selfields = [c.qname for c in temptable.itervalues()] 486 self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" % 487 (temptable.qname, ", ".join(selfields), 488 self.qname)) 489 # Create the new table and copy data from the temp table to it. 490 self._copy_from_temp(temptable, tempkey) 491 425 492 426 493 … … 605 672 cols = [] 606 673 for row in data: 607 dbtype = row[2].upper() 608 c = geniusql.Column(self.db.typeadapter.python_type(dbtype), dbtype, 609 row[4], {}, bool(row[5]), 610 row[1], self.db.quote(row[1])) 674 cid, name, dbtype, notnull, default, pk = row 675 dbtype = dbtype.upper() 676 c = geniusql.Column(self.db.typeadapter.python_type(dbtype), 677 dbtype, default, {}, bool(pk), 678 name, self.db.quote(name)) 611 679 612 680 # "A single row can hold up to 2 ** 30 bytes of data trunk/geniusql/test/zoo_fixture.py
r22 r23 603 603 ostrich = schema['Animal'].select(Species='Ostrich') 604 604 self.assertEqual(ostrich, None) 605 606 def test_primary_key_support(self): 607 # Drop and re-add the PK on, oh, how about the Animal table? 608 schema['Animal'].drop_primary() 609 schema['Animal'].add_primary() 605 610 606 611 def test_insert_into(self):
