Revision 2148
Added by Aaron Marcuse-Kubitza over 12 years ago
lib/sql.py | ||
---|---|---|
193 | 193 |
def execute(self, query, params=None): |
194 | 194 |
self._is_insert = query.upper().find('INSERT') >= 0 |
195 | 195 |
self.query_lookup = _query_lookup(query, params) |
196 |
try: return_value = self.inner.execute(query, params) |
|
196 |
try: |
|
197 |
try: return_value = self.inner.execute(query, params) |
|
198 |
finally: self.query = get_cur_query(self.inner) |
|
197 | 199 |
except Exception, e: |
200 |
_add_cursor_info(e, self) |
|
198 | 201 |
self.result = e # cache the exception as the result |
199 | 202 |
self._cache_result() |
200 | 203 |
raise |
201 |
finally: self.query = get_cur_query(self.inner) |
|
202 | 204 |
# Fetch all rows so result will be cached |
203 | 205 |
if self.rowcount == 0 and not self._is_insert: consume_rows(self) |
204 | 206 |
return return_value |
... | ... | |
234 | 236 |
except StopIteration: return None |
235 | 237 |
|
236 | 238 |
def run_query(self, query, params=None, cacheable=False): |
239 |
'''Translates known DB errors to typed exceptions: |
|
240 |
See self.DbCursor.execute().''' |
|
237 | 241 |
if not self.caching: cacheable = False |
238 | 242 |
used_cache = False |
239 | 243 |
try: |
... | ... | |
247 | 251 |
else: cur = self.db.cursor() |
248 | 252 |
|
249 | 253 |
# Run query |
250 |
try: cur.execute(query, params) |
|
251 |
except Exception, e: |
|
252 |
_add_cursor_info(e, cur) |
|
253 |
raise |
|
254 |
cur.execute(query, params) |
|
254 | 255 |
finally: |
255 | 256 |
if self.log_debug != log_debug_none: # only compute msg if needed |
256 | 257 |
if used_cache: cache_status = 'Cache hit' |
... | ... | |
301 | 302 |
def run_query(db, query, params=None, recover=None, cacheable=False): |
302 | 303 |
if recover == None: recover = False |
303 | 304 |
|
304 |
def run(): return run_raw_query(db, query, params, cacheable) |
|
305 |
if recover and not db.is_cached(query, params): |
|
306 |
return with_savepoint(db, run) |
|
307 |
else: return run() # don't need savepoint if cached |
|
305 |
try: |
|
306 |
def run(): return run_raw_query(db, query, params, cacheable) |
|
307 |
if recover and not db.is_cached(query, params): |
|
308 |
return with_savepoint(db, run) |
|
309 |
else: return run() # don't need savepoint if cached |
|
310 |
except Exception, e: |
|
311 |
if not recover: raise # need savepoint to run index_cols() |
|
312 |
msg = str(e) |
|
313 |
match = re.search(r'duplicate key value violates unique constraint ' |
|
314 |
r'"((_?[^\W_]+)_[^"]+)"', msg) |
|
315 |
if match: |
|
316 |
constraint, table = match.groups() |
|
317 |
try: cols = index_cols(db, table, constraint) |
|
318 |
except NotImplementedError: raise e |
|
319 |
else: raise DuplicateKeyException(cols, e) |
|
320 |
match = re.search(r'null value in column "(\w+)" violates not-null ' |
|
321 |
'constraint', msg) |
|
322 |
if match: raise NullValueException([match.group(1)], e) |
|
323 |
match = re.search(r'relation "(\w+)" already exists', msg) |
|
324 |
if match: raise DuplicateTableException(match.group(1), e) |
|
325 |
raise # no specific exception raised |
|
308 | 326 |
|
309 | 327 |
##### Basic queries |
310 | 328 |
|
... | ... | |
605 | 623 |
|
606 | 624 |
##### Heuristic queries |
607 | 625 |
|
608 |
def with_parsed_errors(db, func): |
|
609 |
'''Translates known DB errors to typed exceptions''' |
|
610 |
try: return func() |
|
611 |
except Exception, e: |
|
612 |
msg = str(e) |
|
613 |
match = re.search(r'duplicate key value violates unique constraint ' |
|
614 |
r'"((_?[^\W_]+)_[^"]+)"', msg) |
|
615 |
if match: |
|
616 |
constraint, table = match.groups() |
|
617 |
try: cols = index_cols(db, table, constraint) |
|
618 |
except NotImplementedError: raise e |
|
619 |
else: raise DuplicateKeyException(cols, e) |
|
620 |
match = re.search(r'null value in column "(\w+)" violates not-null ' |
|
621 |
'constraint', msg) |
|
622 |
if match: raise NullValueException([match.group(1)], e) |
|
623 |
match = re.search(r'relation "(\w+)" already exists', msg) |
|
624 |
if match: raise DuplicateTableException(match.group(1), e) |
|
625 |
raise # no specific exception raised |
|
626 |
|
|
627 | 626 |
def try_insert(db, table, row, returning=None): |
628 | 627 |
'''Recovers from errors''' |
629 |
return with_parsed_errors(db, lambda: insert(db, table, row, returning, |
|
630 |
recover=True)) |
|
628 |
return insert(db, table, row, returning, recover=True) |
|
631 | 629 |
|
632 | 630 |
def put(db, table, row, pkey_=None, row_ct_ref=None): |
633 | 631 |
'''Recovers from errors. |
... | ... | |
679 | 677 |
|
680 | 678 |
out_pkeys = temp_prefix+'_out_pkeys' |
681 | 679 |
def insert_(): |
680 |
'''Inserts and capture output pkeys.''' |
|
682 | 681 |
cur = insert_select(db, out_table, mapping.keys(), |
683 | 682 |
*mk_select_(mapping.values()), returning=out_pkey, |
684 | 683 |
into=out_pkeys, recover=True, table_is_esc=table_is_esc) |
... | ... | |
697 | 696 |
pkeys_cols, start=0), into=pkeys) |
698 | 697 |
|
699 | 698 |
# Do inserts and selects |
700 |
try: |
|
701 |
# Insert and capture output pkeys |
|
702 |
with_parsed_errors(db, insert_) |
|
699 |
try: insert_() |
|
703 | 700 |
except DuplicateKeyException, e: |
704 | 701 |
join_cols = util.dict_subset_right_join(mapping, e.cols) |
705 | 702 |
joins = in_joins + [(out_table, join_cols)] |
Also available in: Unified diff
sql.py: Merged with_parsed_errors() into run_query() so all recoverable queries would automatically benefit from DB error message parsing. DbConn: Moved _add_cursor_info() to DbCursor.execute().