Changeset 119
- Timestamp:
- 08/06/07 15:47:54
- Files:
-
- branches/ast/geniusql/adapters.py (modified) (17 diffs)
- branches/ast/geniusql/dbtypes.py (modified) (1 diff)
- branches/ast/geniusql/deparse.py (modified) (9 diffs)
- branches/ast/geniusql/logic.py (modified) (3 diffs)
- branches/ast/geniusql/objects.py (modified) (6 diffs)
- branches/ast/geniusql/providers/postgres.py (modified) (4 diffs)
- branches/ast/geniusql/select.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/ast/geniusql/adapters.py
r118 r119 159 159 # sqlite 3 will return an int. 160 160 # This construction should handle both. 161 if value is None: 162 return None 161 163 return bool(int(value)) 162 164 … … 172 174 173 175 def pull(self, value, dbtype): 176 if value is None: 177 return None 174 178 return bool(value) 175 179 … … 188 192 189 193 def pull(self, value, dbtype): 194 if value is None: 195 return None 190 196 if isinstance(value, datetime.datetime): 191 197 return value … … 204 210 205 211 def pull(self, value, dbtype): 212 if value is None: 213 return None 206 214 # These are in order for a reason: datetime is a subclass of date! 207 215 if isinstance(value, datetime.datetime): … … 223 231 224 232 def pull(self, value, dbtype): 233 if value is None: 234 return None 225 235 if isinstance(value, datetime.time): 226 236 return value … … 251 261 252 262 def pull(self, value, dbtype): 263 if value is None: 264 return None 253 265 days, seconds = divmod(long(value), 86400) 254 266 return datetime.timedelta(int(days), int(seconds)) … … 265 277 266 278 def pull(self, value, dbtype): 279 if value is None: 280 return None 267 281 return float(value) 268 282 … … 287 301 288 302 def pull(self, value, dbtype): 303 if value is None: 304 return None 289 305 return self.pytype(value) 290 306 … … 315 331 316 332 def pull(self, value, dbtype): 333 if value is None: 334 return None 317 335 if isinstance(value, unicode): 318 336 return value.encode(dbtype.encoding) … … 324 342 325 343 def pull(self, value, dbtype): 344 if value is None: 345 return None 326 346 if isinstance(value, unicode): 327 347 return value … … 349 369 350 370 def pull(self, value, dbtype): 371 if value is None: 372 return None 351 373 # Coerce to str for pickle.loads restriction. 352 374 if isinstance(value, unicode): … … 371 393 372 394 def pull(self, value, dbtype): 395 if value is None: 396 return None 373 397 return self.pytype(value) 374 398 … … 410 434 411 435 def pull(self, value, dbtype): 436 if value is None: 437 return None 412 438 return self.pytype(value) 413 439 … … 426 452 427 453 def pull(self, value, dbtype): 454 if value is None: 455 return None 428 456 # pywin32 build 205 began support for returning 429 457 # COM Currency objects as decimal objects. … … 462 490 463 491 def pull(self, value, dbtype): 492 if value is None: 493 return None 464 494 if isinstance(value, float): 465 495 value = repr(value) … … 478 508 return str(value) 479 509 def pull(self, value, dbtype): 510 if value is None: 511 return None 480 512 if (isinstance(value, basestring) or 481 513 (typerefs.decimal and … … 518 550 519 551 def pull(self, value, dbtype): 552 if value is None: 553 return None 520 554 if isinstance(value, float): 521 555 value = repr(value) branches/ast/geniusql/dbtypes.py
r118 r119 551 551 return self.known_types['bool'][0]() 552 552 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 553 592 def dbtype_for_datetime_datetime(self, hints): 554 593 return self.known_types['datetime'][0]() branches/ast/geniusql/deparse.py
r118 r119 127 127 sql_bin_op = dict([(v, v) for v in astwalk.codewalk.binary_repr.itervalues()]) 128 128 129 # These are not adapter.push(bool) (which are used on one side of130 # a comparison). Instead, these are used when the whole (sub)expression131 # is True or False, e.g. "WHERE TRUE", or "WHERE TRUE and 'a'.'b' = 3".132 bool_true = "TRUE"133 bool_false = "FALSE"134 135 129 def __init__(self, tables, expr, typeset): 136 130 self.tables = tables … … 141 135 142 136 # 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 149 142 self.none_expr = SQLExpression("NULL", "expr0", None, type(None)) 150 143 … … 196 189 if result is cannot_represent: 197 190 # The entire expression could not be evaluated. 198 result = self. true_expr191 result = self.expr_true 199 192 self.imperfect = True 200 elif result == self. T:201 result = self. true_expr202 elif result == self. F:203 result = self. false_expr193 elif result == self.comp_true: 194 result = self.expr_true 195 elif result == self.comp_false: 196 result = self.expr_false 204 197 205 198 # Cache the result … … 240 233 self.imperfect = True 241 234 # Use TRUE for the term, so all records are returned. 242 term = self. true_expr243 # 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 248 241 newterms.append("(%s)" % term.sql) 249 242 clause = self.get_expr(" AND ".join(newterms), bool) … … 261 254 self.imperfect = True 262 255 # Use TRUE for the term, so all records are returned. 263 term = self. true_expr264 # 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 269 262 newterms.append("(%s)" % term.sql) 270 263 clause = self.get_expr(" OR ".join(newterms), bool) … … 327 320 def visit_CallFunc(self, func, *args): 328 321 # 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] 332 324 333 325 posargs = [] 334 326 kwargs = {} 335 for arg in args :327 for arg in args[:-2]: 336 328 if isinstance(arg, astwalk.ast.Keyword): 337 329 key, val = arg.name, self.walk(arg.value) … … 380 372 return cannot_represent 381 373 382 ops = list(ops)383 384 374 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) 388 380 if op2 is cannot_represent: 389 381 return cannot_represent … … 408 400 else: 409 401 raise ValueError("Non-equality Null comparisons not allowed.") 410 elif op in ('<', '<=', '==', '!=', '>', '>='):402 elif op in reverseop: 411 403 try: 412 404 sql = op1.adapter.compare_op(op1, op, self.sql_cmp_op[op], op2) … … 491 483 else: 492 484 # Nothing will match the empty list, so return none. 493 return self. false_expr485 return self.expr_false 494 486 495 487 def builtins_icontainedby(self, op1, op2): branches/ast/geniusql/logic.py
r118 r119 359 359 e = logic.Expression(lambda x: x.a == 3 and x.b == 1) 360 360 """ 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): 367 366 co += [124, 0, 0, 368 367 105, i, 0, 369 368 100, i, 0, 370 369 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, 372 372 1, 373 373 ] … … 377 377 co.append(83) 378 378 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) 383 381 384 382 # Form code object and function. … … 387 385 # filename, name, firstlineno, lnotab[, freevars[, cellvars]]) 388 386 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, '') 391 388 func = FunctionType(co, {}) 392 389 return Expression(func, earlybind=False) branches/ast/geniusql/objects.py
r118 r119 909 909 (as positional arguments). 910 910 """ 911 conn = self.connections.get()912 913 911 if not isinstance(query, select.Query): 914 912 query = select.Query(*query) … … 916 914 sel = self.selectwriter(self, query) 917 915 sql = sel.sql(distinct, limit) 918 data, _ = self.fetch(sql , conn)916 data, _ = self.fetch(sql) 919 917 920 918 d = Dataset(sel, data) … … 937 935 938 936 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) 945 938 # CREATE TABLE 946 939 newtable.schema[name] = newtable … … 965 958 else: 966 959 sql = ("INSERT INTO %s (%s) %s" % 967 (newtable.qname, ", ".join(sel.output_ list), selsql))960 (newtable.qname, ", ".join(sel.output_qnames), selsql)) 968 961 self.execute(sql) 969 962 … … 999 992 self.dataset = dataset 1000 993 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] 1001 998 1002 999 def __iter__(self): … … 1012 1009 under the assumption that the caller will do so itself. 1013 1010 """ 1014 d = self.dataset1015 1016 1011 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 44 44 45 45 def pull(self, value, dbtype): 46 if value is None: 47 return None 46 48 if isinstance(value, datetime.timedelta): 47 49 return value … … 121 123 122 124 def pull(self, value, dbtype): 125 if value is None: 126 return None 123 127 # Unescape octal sequences 124 128 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 129 135 130 136 … … 144 150 145 151 def pull(self, value, dbtype): 152 if value is None: 153 return None 146 154 # Unescape octal sequences 147 155 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) 152 161 153 162 … … 169 178 170 179 def pull(self, value, dbtype): 180 if value is None: 181 return None 171 182 # Unescape octal sequences 172 183 value = unescape_oct.sub(replace_unoct, value) 173 184 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) 179 191 return pickle.loads(value) 180 192 branches/ast/geniusql/select.py
r118 r119 193 193 input_list: a list of SQL expressions, one for each column in the 194 194 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. 196 197 output_keys: the keys for each column in the final table. 198 output_cols: a dict of source cols for the final table. 197 199 """ 198 200 … … 209 211 if isinstance(relation, Join): 210 212 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 214 216 self.tables = self.wrap(relation) 215 217 self.fromclause = self.joinclause(self.tables) 216 218 elif isinstance(relation, geniusql.Schema): 217 219 # This is how we say we want to SELECT scalars (no FROM clause) 218 self. result = relation.table('')220 self.schema = relation 219 221 self.tables = [] 220 222 self.fromclause = "" 221 223 else: 222 self. result = relation.schema.table('')224 self.schema = relation.schema 223 225 self.tables = [self.db.joinwrapper(relation)] 224 226 self.fromclause = relation.qname … … 227 229 228 230 self.input_list = [] 229 self.output_list = [] 231 self.output_names = [] 232 self.output_qnames = [] 230 233 self.output_keys = [] 234 self.output_cols = {} 231 235 self._groupby = [] 232 236 if isinstance(query.attributes, logic.Expression): … … 257 261 self.order = [relation[key].qname for key in query.order] 258 262 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 259 284 def _copy_column(self, table, alias, colkey): 260 285 """Copy the given column from the given table into our result table. … … 267 292 """ 268 293 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 275 297 if alias is None: 276 298 # 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: 281 304 # Get the key for the table. 282 305 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) 286 309 self.input_list.append(selname) 287 self.output_list.append(newcol.qname) 310 self.output_names.append(colname) 311 self.output_qnames.append(colqname) 288 312 self.output_keys.append(colkey) 289 self. result[colkey] = newcol313 self.output_cols[colkey] = col 290 314 291 315 def sql(self, distinct=False, limit=None, into=""): … … 438 462 self.query.attributes) 439 463 440 if atom.name in self. result:464 if atom.name in self.output_cols: 441 465 bare_name = atom.name 442 466 index = 1 443 while atom.name in self. result:467 while atom.name in self.output_cols: 444 468 atom.name = '%s%s' % (bare_name, index) 445 469 index += 1 … … 448 472 self.input_list.append('%s AS %s' % (atom.sql, qname)) 449 473 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) 451 476 if not atom.aggregate: 452 477 self._groupby.append(atom.sql) 453 478 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 458 480 459 481 def deparse_order(self):
