"""
dsq_postgres.backend.decorators
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:copyright: (c) 2010-2012 DISQUS.
:license: Apache License 2.0, see LICENSE for more details.
"""

import sys

from dsq_postgres.exceptions import TransactionAborted
from dsq_postgres.backend.helpers import can_reconnect
from functools import wraps


def auto_reconnect_cursor(func):
    """
    Attempt to safely reconnect when an error is hit that resembles the
    bouncer disconnecting the client due to a timeout/etc during a cursor
    execution.
    """
    @wraps(func)
    def inner(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except Exception, e:
            if not can_reconnect(e):
                raise

            self.db.close(reconnect=True)
            self.cursor = self.db.make_cursor()

            return func(self, *args, **kwargs)

    return inner


def auto_reconnect_connection(func):
    """
    Attempt to safely reconnect when an error is hit that resembles the
    bouncer disconnecting the client due to a timeout/etc.
    """
    @wraps(func)
    def inner(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except Exception, e:
            if not can_reconnect(e):
                raise

            self.close(reconnect=True)

            return func(self, *args, **kwargs)

    return inner


def capture_transaction_exceptions(func):
    """
    Catches database errors and reraises them on subsequent errors that throw
    some cruft about transaction aborted.
    """
    def raise_the_exception(conn, exc):
        if 'current transaction is aborted, commands ignored until end of transaction block' in str(exc):
            exc_info = getattr(conn, '_last_exception', None)
            if exc_info is None:
                raise
            new_exc = TransactionAborted(sys.exc_info(), exc_info)
            raise new_exc.__class__, new_exc, exc_info[2]

        conn._last_exception = sys.exc_info()
        raise

    @wraps(func)
    def inner(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except Exception, e:
            raise_the_exception(self.db, e)

    return inner


def send_set_time_zone(func):
    """
    Lazily sends a SET TIME ZONE command before a query is executed if it
    is pending (e.g. from an isolation level change).
    """
    def set_tz(wrapper):
        if not wrapper.db._needs_tz:
            return

        tz_info = wrapper.db.settings_dict.get('TIME_ZONE')
        if not tz_info:
            return

        wrapper.cursor.execute("SET TIME ZONE %s", [tz_info])
        wrapper.db._needs_tz = False

    @wraps(func)
    def inner(self, *args, **kwargs):
        set_tz(self)

        return func(self, *args, **kwargs)
    return inner

"""
dsq_postgres.backend.decorators
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:copyright: (c) 2010-2012 DISQUS.
:license: Apache License 2.0, see LICENSE for more details.
"""

import sys

from dsq_postgres.exceptions import TransactionAborted
from dsq_postgres.backend.helpers import can_reconnect
from functools import wraps


def auto_reconnect_cursor(func):
    """
    Attempt to safely reconnect when an error is hit that resembles the
    bouncer disconnecting the client due to a timeout/etc during a cursor
    execution.
    """
    @wraps(func)
    def inner(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except Exception, e:
            if not can_reconnect(e):
                raise

            self.db.close(reconnect=True)
            self.cursor = self.db.make_cursor()

            return func(self, *args, **kwargs)

    return inner


def auto_reconnect_connection(func):
    """
    Attempt to safely reconnect when an error is hit that resembles the
    bouncer disconnecting the client due to a timeout/etc.
    """
    @wraps(func)
    def inner(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except Exception, e:
            if not can_reconnect(e):
                raise

            self.close(reconnect=True)

            return func(self, *args, **kwargs)

    return inner


def capture_transaction_exceptions(func):
    """
    Catches database errors and reraises them on subsequent errors that throw
    some cruft about transaction aborted.
    """
    def raise_the_exception(conn, exc):
        if 'current transaction is aborted, commands ignored until end of transaction block' in str(exc):
            exc_info = getattr(conn, '_last_exception', None)
            if exc_info is None:
                raise
            new_exc = TransactionAborted(sys.exc_info(), exc_info)
            raise new_exc.__class__, new_exc, exc_info[2]

        conn._last_exception = sys.exc_info()
        raise

    @wraps(func)
    def inner(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except Exception, e:
            raise_the_exception(self.db, e)

    return inner


def send_set_time_zone(func):
    """
    Lazily sends a SET TIME ZONE command before a query is executed if it
    is pending (e.g. from an isolation level change).
    """
    def set_tz(wrapper):
        if not wrapper.db._needs_tz:
            return

        tz_info = wrapper.db.settings_dict.get('TIME_ZONE')
        if not tz_info:
            return

        wrapper.cursor.execute("SET TIME ZONE %s", [tz_info])
        wrapper.db._needs_tz = False

    @wraps(func)
    def inner(self, *args, **kwargs):
        set_tz(self)

        return func(self, *args, **kwargs)
    return inner

dsq_postgres.backend.decorators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  :copyright: (c) 2010-2012 DISQUS. :license: Apache License 2.0, see LICENSE for more details. Attempt to safely reconnect when an error is hit that resembles the     bouncer disconnecting the client due to a timeout/etc during a cursor     execution. Attempt to safely reconnect when an error is hit that resembles the     bouncer disconnecting the client due to a timeout/etc. Catches database errors and reraises them on subsequent errors that throw     some cruft about transaction aborted. Lazily sends a SET TIME ZONE command before a query is executed if it     is pending (e.g. from an isolation level change).