# -*- coding: latin-1 -*-

from Products.PimenTechLibCommon.object import Object
from Products.PimenTechLibCommon.zlog import *

import psycopg
from time import time
from string import *

class DBP(Object):

	__super_init = Object.__init__
	__calls = 0
	__failures = 0

	def __init__(self, id, connection_id):
		self.__super_init(id)
		self.connection_id = connection_id
		self.conn = None

	def increment_calls(self, reset=0):
		DBP.__calls += 1
		if reset:
			DBP.__calls = 0

	def increment_failures(self, reset=0):
		DBP.__failures += 1
		if reset:
			DBP.__failures = 0

	def get_calls(self):
		return DBP.__calls

	def get_failures(self):
		return DBP.__failures
			
	def get_conn(self):
		if not self.conn:
			self.conn = getattr(self, self.connection_id)
		return self.conn()
	
	def get_cursor(self):
		"Retourne un curseur psycopg"
		return self.get_conn()._cursor()

	def rollback_connection(self):
		self.conn().db.rollback()

	def connect(self):
		self.conn = getattr(self, self.connection_id)
		conn = self.conn()
		if hasattr(conn, 'db'):
			del conn.db
		conn.db = conn.connect(conn.connection)

	def exec_sql(self, query, curs=None, vital=0):
		self.get_conn()._register()
		if curs is None:
			curs = self.get_cursor()
		self.message('exec_sql:%s' % query)
		try:
			curs.execute(str(query))
		except :
			self.error(self.get_traceback())
			if vital:
				raise "EXEC_SQL ERROR", str(query)
			return None
		return 1

	def exec_vital_sql(self, query, curs=None):
		return self.exec_sql(query, curs, 1)

	def dictfetch(self, query_string, curs=None):
		return self.fetch(query_string, curs, 'dict')

	def dictfetchone(self, query_string, curs=None, verif=1):
		rows = self.dictfetch(query_string, curs)
		if verif and len(rows) != 1:
			self.error("%s != 1 lignes pour %s" % (len(rows), query_string))
			raise 'FetchoneError'
		return rows and rows[0]

	def fetchone(self, query_string, curs=None, type_fetch='list', verif=1):
		rows = self.fetch(query_string, curs, type_fetch)
		if verif and len(rows) != 1:
			self.error("%s != 1 lignes pour %s" % (len(rows), query_string))
			raise 'FetchoneError'
		return rows and rows[0]

	def fetchoneof(self, query_string, curs=None, type_fetch='list'):
		return self.fetchone(query_string, curs, type_fetch, 0)
	
	def fetch(self, query_string, curs=None, type_fetch='list'):
		self.increment_calls()
		result = []
		self.get_conn()._register()
		if curs is None:
			curs = self.get_cursor()
		nselects = 0
		try:
			try:
				curs.execute(query_string)
			except psycopg.IntegrityError, perr:
				if perr.args[0].find("concurrent update") > -1:
					self.error("Concurrent update for query [%s]" % query_string)
				self.error("fetch(%s):\n%s" % (query_string,self.get_traceback()))
				curs.execute("abort")
				return result
			except psycopg.ProgrammingError, perr:
				self.error("fetch(%s):\n%s" % (query_string,self.get_traceback()))
				raise psycopg.ProgrammingError, self.get_traceback()
			if curs.description is not None:
				if type_fetch == 'list':
					result = curs.fetchall()
				elif type_fetch == 'dict':
					result = curs.dictfetchall()
				else:
					self.error("[dbp] Type fetch inconnu")
					raise "TYPE FETCH INCONNU"
			self.increment_failures(reset=1)
		except (psycopg.OperationalError, psycopg.InterfaceError):
			self.warning("[dbp] Trying to reconnect (failures : %s)" % (self.get_failures() +1))
			self.connect()
			return self.fetch(query_string, type_fetch=type_fetch)
		except psycopg.Error, err:
			self.increment_failures()
			if (self.get_failures() > 1000):
				self.increment_failures(reset=1)
				self.connect()
				return self.fetch(query_string, type_fetch=type_fetch)
			else:
				self.conn()._abort()
				self.error("Connexion aborted for query %s" % query_string)
				self.error("fetch(%s):\n%s" % (query_string,self.get_traceback()))
				raise psycopg.ProgrammingError, self.get_traceback()
		except:
			self.error(self.get_traceback())
			self.conn()._abort()
			self.error("Connexion aborted for query [%s]" % query_string)
		
		return result


