# Buggy code
def housekeeping(self, expired_threshold, info_threshold):
    # delete 'closed' or 'expired' alerts older than "expired_threshold" hours
    # and 'informational' alerts older than "info_threshold" hours
    delete = """
            DELETE FROM alerts
             WHERE (status IN ('closed', 'expired')
                    AND last_receive_time < (NOW() at time zone 'utc' - INTERVAL '%(expired_threshold)s hours'))
                OR (severity='informational'
                    AND last_receive_time < (NOW() at time zone 'utc' - INTERVAL '%(info_threshold)s hours'))
        """
    self._delete(
        delete,
        {"expired_threshold": expired_threshold, "info_threshold": info_threshold},
    )

    # get list of alerts to be newly expired
    update = """
            SELECT id, event, last_receive_id
              FROM alerts
             WHERE status NOT IN ('expired','shelved') AND timeout!=0
               AND (last_receive_time + INTERVAL '1 second' * timeout) < (NOW() at time zone 'utc')
        """
    expired = self._fetchall(update, {})

    # get list of alerts to be unshelved
    update = """
            SELECT id, event, last_receive_id
              FROM alerts
             WHERE status='shelved'
               AND (last_receive_time + INTERVAL '1 second' * timeout) < (NOW() at time zone 'utc')
        """
    unshelved = self._fetchall(update, {})

    return (expired, unshelved)


# Traceback (ApiError)
2018-04-28 00:06:43,862 - alerta[18702]: ERROR - HOUSEKEEPING FAILED: Type names and field names can only contain alphanumeric characters and underscores: '?column?' [in /usr/lib/python2.7/site-packages/alerta_server-5.2.0_-py2.7.egg/alerta/exceptions.py:67]
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/usr/lib/python2.7/site-packages/flask_cors/decorator.py", line 128, in wrapped_function
resp = make_response(f(*args, **kwargs))
File "/usr/lib/python2.7/site-packages/alerta_server-5.2.0_-py2.7.egg/alerta/auth/utils.py", line 95, in wrapped
return f(*args, **kwargs)
File "/usr/lib/python2.7/site-packages/alerta_server-5.2.0_-py2.7.egg/alerta/management/views.py", line 148, in housekeeping
raise ApiError('HOUSEKEEPING FAILED: %s' % e, 503)
ApiError: HOUSEKEEPING FAILED: Type names and field names can only contain alphanumeric characters and underscores: '?column?'

# Fixed code
def housekeeping(self, expired_threshold, info_threshold):
    # delete 'closed' or 'expired' alerts older than "expired_threshold" hours
    # and 'informational' alerts older than "info_threshold" hours
    delete = """
            DELETE FROM alerts
             WHERE (status IN ('closed', 'expired')
                    AND last_receive_time < (NOW() at time zone 'utc' - INTERVAL '%(expired_threshold)s hours'))
                OR (severity='informational'
                    AND last_receive_time < (NOW() at time zone 'utc' - INTERVAL '%(info_threshold)s hours'))
        """
    self._delete(
        delete,
        {"expired_threshold": expired_threshold, "info_threshold": info_threshold},
    )

    # get list of alerts to be newly expired
    select = """
            SELECT id, event, last_receive_id
              FROM alerts
             WHERE status NOT IN ('expired','shelved') AND timeout!=0
               AND (last_receive_time + INTERVAL '1 second' * timeout) < (NOW() at time zone 'utc')
        """
    expired = self._fetchall(select, {})

    # get list of alerts to be unshelved
    select = """
        WITH shelved AS (
            SELECT DISTINCT ON (a.id) a.id, a.event, a.last_receive_id, h.update_time, a.timeout
              FROM alerts a, UNNEST(history) h
             WHERE a.status='shelved'
               AND h.type='action'
               AND h.status='shelved'
          ORDER BY a.id, h.update_time DESC
        )
        SELECT id, event, last_receive_id
          FROM shelved
         WHERE (update_time + INTERVAL '1 second' * timeout) < (NOW() at time zone 'utc')
        """
    unshelved = self._fetchall(select, {})

    return (expired, unshelved)