Project

General

Profile

« Previous | Next » 

Revision 2148

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().

View differences:

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