Revision 2464
Added by Aaron Marcuse-Kubitza over 12 years ago
lib/sql.py | ||
---|---|---|
274 | 274 |
def esc_name(self, name): return esc_name(self, name) # calls global func |
275 | 275 |
|
276 | 276 |
def run_query(self, query, params=None, cacheable=False, log_level=2, |
277 |
exc_log_level=None):
|
|
277 |
debug_msg_ref=None):
|
|
278 | 278 |
''' |
279 |
@param exc_log_level The log_level if the query throws an exception.
|
|
280 |
Defaults to the value of log_level.
|
|
279 |
@param log_ignore_excs The log_level will be increased by 2 if the query
|
|
280 |
throws one of these exceptions.
|
|
281 | 281 |
''' |
282 | 282 |
assert query != None |
283 |
if exc_log_level == None: exc_log_level = log_level |
|
284 | 283 |
|
285 | 284 |
if not self.caching: cacheable = False |
286 | 285 |
used_cache = False |
287 |
success = False |
|
288 | 286 |
try: |
289 | 287 |
# Get cursor |
290 | 288 |
if cacheable: |
... | ... | |
297 | 295 |
|
298 | 296 |
# Run query |
299 | 297 |
cur.execute(query, params) |
300 |
|
|
301 |
success = True |
|
302 | 298 |
finally: |
303 |
if self.debug: # only compute msg if needed |
|
304 |
if not success: log_level = exc_log_level |
|
299 |
if self.debug and debug_msg_ref != None:# only compute msg if needed |
|
305 | 300 |
if used_cache: cache_status = 'Cache hit' |
306 | 301 |
elif cacheable: cache_status = 'Cache miss' |
307 | 302 |
else: cache_status = 'Non-cacheable' |
308 |
self.log_debug(cache_status+': '+strings.one_line(
|
|
309 |
str(get_cur_query(cur, query, params))), log_level)
|
|
303 |
debug_msg_ref[0] = cache_status+': '+strings.one_line(
|
|
304 |
str(get_cur_query(cur, query, params))) |
|
310 | 305 |
|
311 | 306 |
return cur |
312 | 307 |
|
... | ... | |
355 | 350 |
|
356 | 351 |
def with_savepoint(db, func): return db.with_savepoint(func) |
357 | 352 |
|
358 |
def run_query(db, query, params=None, recover=None, cacheable=False, **kw_args): |
|
353 |
def run_query(db, query, params=None, recover=None, cacheable=False, |
|
354 |
log_level=2, log_ignore_excs=None, **kw_args): |
|
359 | 355 |
'''For params, see run_raw_query()''' |
360 | 356 |
if recover == None: recover = False |
357 |
if log_ignore_excs == None: log_ignore_excs = () |
|
358 |
log_ignore_excs = tuple(log_ignore_excs) |
|
361 | 359 |
|
360 |
debug_msg_ref = [None] |
|
362 | 361 |
try: |
363 |
def run(): return run_raw_query(db, query, params, cacheable, **kw_args) |
|
364 |
if recover and not db.is_cached(query, params): |
|
365 |
return with_savepoint(db, run) |
|
366 |
else: return run() # don't need savepoint if cached |
|
367 |
except Exception, e: |
|
368 |
if not recover: raise # need savepoint to run index_cols() |
|
369 |
msg = exc.str_(e) |
|
370 |
|
|
371 |
match = re.search(r'duplicate key value violates unique constraint ' |
|
372 |
r'"((_?[^\W_]+)_[^"]+?)"', msg) |
|
373 |
if match: |
|
374 |
constraint, table = match.groups() |
|
375 |
try: cols = index_cols(db, table, constraint) |
|
376 |
except NotImplementedError: raise e |
|
377 |
else: raise DuplicateKeyException(constraint, cols, e) |
|
378 |
|
|
379 |
match = re.search(r'null value in column "(\w+?)" violates not-null ' |
|
380 |
r'constraint', msg) |
|
381 |
if match: raise NullValueException('NOT NULL', [match.group(1)], e) |
|
382 |
|
|
383 |
match = re.search(r'\b(?:invalid input (?:syntax|value)\b.*?' |
|
384 |
r'|date/time field value out of range): "(.+?)"\n' |
|
385 |
r'(?:(?s).*?)\bfunction "(\w+?)".*?\bat assignment', msg) |
|
386 |
if match: |
|
387 |
value, name = match.groups() |
|
388 |
raise FunctionValueException(name, strings.to_unicode(value), e) |
|
389 |
|
|
390 |
match = re.search(r'relation "(\w+?)" already exists', msg) |
|
391 |
if match: raise DuplicateTableException(match.group(1), e) |
|
392 |
|
|
393 |
match = re.search(r'function "(\w+?)" already exists', msg) |
|
394 |
if match: raise DuplicateFunctionException(match.group(1), e) |
|
395 |
|
|
396 |
raise # no specific exception raised |
|
362 |
try: |
|
363 |
def run(): return run_raw_query(db, query, params, cacheable, |
|
364 |
log_level, debug_msg_ref, **kw_args) |
|
365 |
if recover and not db.is_cached(query, params): |
|
366 |
return with_savepoint(db, run) |
|
367 |
else: return run() # don't need savepoint if cached |
|
368 |
except Exception, e: |
|
369 |
if not recover: raise # need savepoint to run index_cols() |
|
370 |
msg = exc.str_(e) |
|
371 |
|
|
372 |
match = re.search(r'duplicate key value violates unique constraint ' |
|
373 |
r'"((_?[^\W_]+)_[^"]+?)"', msg) |
|
374 |
if match: |
|
375 |
constraint, table = match.groups() |
|
376 |
try: cols = index_cols(db, table, constraint) |
|
377 |
except NotImplementedError: raise e |
|
378 |
else: raise DuplicateKeyException(constraint, cols, e) |
|
379 |
|
|
380 |
match = re.search(r'null value in column "(\w+?)" violates not-null' |
|
381 |
r' constraint', msg) |
|
382 |
if match: raise NullValueException('NOT NULL', [match.group(1)], e) |
|
383 |
|
|
384 |
match = re.search(r'\b(?:invalid input (?:syntax|value)\b.*?' |
|
385 |
r'|date/time field value out of range): "(.+?)"\n' |
|
386 |
r'(?:(?s).*?)\bfunction "(\w+?)".*?\bat assignment', msg) |
|
387 |
if match: |
|
388 |
value, name = match.groups() |
|
389 |
raise FunctionValueException(name, strings.to_unicode(value), e) |
|
390 |
|
|
391 |
match = re.search(r'relation "(\w+?)" already exists', msg) |
|
392 |
if match: raise DuplicateTableException(match.group(1), e) |
|
393 |
|
|
394 |
match = re.search(r'function "(\w+?)" already exists', msg) |
|
395 |
if match: raise DuplicateFunctionException(match.group(1), e) |
|
396 |
|
|
397 |
raise # no specific exception raised |
|
398 |
except log_ignore_excs: |
|
399 |
log_level += 2 |
|
400 |
raise |
|
401 |
finally: |
|
402 |
if debug_msg_ref[0] != None: db.log_debug(debug_msg_ref[0], log_level) |
|
397 | 403 |
|
398 | 404 |
##### Basic queries |
399 | 405 |
|
... | ... | |
415 | 421 |
assert isinstance(into, sql_gen.Table) |
416 | 422 |
|
417 | 423 |
kw_args['recover'] = True |
418 |
kw_args.setdefault('exc_log_level', kw_args.get('log_level', 2) + 2) |
|
419 |
# by default, will have exc_log_level=4 |
|
424 |
kw_args.setdefault('log_ignore_excs', (DuplicateTableException,)) |
|
420 | 425 |
|
421 | 426 |
temp = not db.debug # tables are created as permanent in debug mode |
422 | 427 |
# "temporary tables cannot specify a schema name", so remove schema |
... | ... | |
567 | 572 |
AS $$'''+mogrify(db, query, params)+''';$$; |
568 | 573 |
''' |
569 | 574 |
run_query(db, function_query, recover=True, cacheable=True, |
570 |
exc_log_level=4)
|
|
575 |
log_ignore_excs=(DuplicateFunctionException,))
|
|
571 | 576 |
break # this version was successful |
572 | 577 |
except DuplicateFunctionException, e: |
573 | 578 |
function_name = next_version(function_name) |
Also available in: Unified diff
sql.py: Fixed bug where queries with versioned identifiers which threw an exception (not related to name collisions) were being output with a too-high log_level, because all exceptions were output with the higher exc_log_level, by making the following changes: DbConn.run_query(): Changed exc_log_level param to log_ignore_excs param so that only certain exceptions would cause the query to be output with a higher log_level. Moved the code that actual emits the query debug message from DbConn.run_query() to module-level run_query() so it would apply the log_ignore_excs filter after the exception had already been parsed into specific types.