Project

General

Profile

« Previous | Next » 

Revision 2464

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.

View differences:

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