# -*- coding: utf8 -*-
'''
DB MODULE - Oracle 9 & 10
*******************************
***  DB MODULE - Oracle 9 & 10
*******************************
$RCSfile: dbmod_oracle.py,v $
$Revision: 952 $
$Author: markus $
$Date: 2009-10-01 16:23:15 +0200 (Do, 01. Okt 2009) $

The biocase.wrapper database module to access Oracle 9 & 10 databases.
You will need the cx_Oracle module to use it. 
There are different versions available of cx_Oracle for Oracle 9 & 10.
'''

from biocase.wrapper.errorclasses		import *
from biocase.tools.various_functions 	import flatten, scalarpNoList
from biocase.wrapper.typeconverter		import DBtransformerClass, DBAttributeClass
from biocase.wrapper.sql.operators		import logicalOperator
from biocase.wrapper.sql.base			import sqlBase
from string							    import join
from biocase.wrapper.dbmod.base_dbmod   import DBBasemodule
from biocase.wrapper.sql.operators 	    import getWhereObjTables, compressOperator

import logging
log = logging.getLogger("pywrapper.dbmod")



#############################################
#
# CUSTOMIZATION for Oracle
#
# OVERLOAD BASE CLASSES AND OTHER METHODS
#
#############################################

# do a case insensitive LIKE search by applying the function LOWER.
# Please modify this for a different function like UPPER if you want to 
# or outcomment it to make the search case sensitive.
sqlBase.caseAlteringFunctionForLike = 'LOWER'

# escaped characters in SQL
sqlBase.escape = {}
sqlBase.escape["?"] = "\\?"
sqlBase.escape["'"] = "''"

# syntax to create an alias of a table. #1=real table name, #2=alias name
sqlBase.templates['alias'] = '#1 #2'

# a select statement. #1=select part, #2=from(+where+orderby) part, #3=limit/top number
sqlBase.templates['select-limit'] = 'SELECT * FROM ( SELECT DISTINCT #1 FROM #2) WHERE ROWNUM <= #3'

#
# enable (+) operator instead of LEFT JOIN for Oracle<9
sqlBase.binaryCOP['ora_left_join'] = "(+) ="

# override standard type-transformation from module dbtypes:
def makeDate(self, x):
	# convert from date-string to Oracle TO_DATE function
	x = unicode(x)
	# replace escape characters
	for old,new in sqlBase.escape.items():
		x = x.replace(old, new)
	dateformat = 'YYYY-MM-DD HH:MM:SS'
	# replace the T for time in the ANSI time format with space so oracle understands it
	x = x.replace('T', ' ')
	dateformat = dateformat[:len(x)]
	value = sqlBase.templates['string'].replace('#1', x)
	return "TO_DATE(%s, '%s')"%(value, dateformat)
DBtransformerClass.makeDate = makeDate



# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
class DBmodule(DBBasemodule):
	__version__ = '0.90 using cx_Oracle ?'
	__dbmod__	= u'Oracle DB module'
	
	# _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
	def __init__(self, psfObj):
		# setting the psfObj reference to self.psfObj
		# call createDBconnection method
		DBBasemodule.__init__(self, psfObj)
		# read DB 8bit encoding
		log.debug("8bit DB Encoding: %s"%(psfObj.encoding))
		#
		#
		# FOR TESTING ONLY !!!
		#
		#
		# dont use parameters but integrate all parameters into SQL strings:
		self.trans.integrateParametersInSQLString()
		# replace method again with this special Oracle method!
		DBtransformerClass.makeDate = makeDate


	# _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
	def createDBconnection(self):		
		try:
			import cx_Oracle
		except:
			raise DbModuleMissingError('cx_Oracle')
		self.__class__.__version__ = self.__class__.__version__[:-1]+cx_Oracle.version
		try:
			loginParas = "user:%s  password=%s  TNS=%s" %(self.psfObj.user, self.psfObj.password, self.psfObj.database)
			return cx_Oracle.connect(str(self.psfObj.user), str(self.psfObj.password), str(self.psfObj.database))
		except:
			raise ConnectionError()

	# _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
	def fromClause(self, tableList):
		'''Returns the SQL string of the FROM clause for a given table(alias) list.
		Makes use of the tablegraph to produce an ordered list of tables
		and their relation - the relObj (table-alias names, whereObj).'''
		if len(tableList) > 1:
			TreeJoinLists = self.CMFObject.tableTreeObj.getJoinTableListOfLists(tableList)
			#TreeJoinLists = self.psfObj.tablegraph.getJoinTableListOfLists(tableList)
			AdditionalWhereObjs = []
			fromClause = None
			for t, relObj in flatten(TreeJoinLists, scalarpNoList):
				if relObj is not None:
					relObj.type = 'ora_left_join'
					AdditionalWhereObjs.append(relObj)
				if fromClause is None:
					fromClause = self.getTableSyntax(t)
				else:
					fromClause += ', '+ self.getTableSyntax(t)
			addWhereObj = logicalOperator('and', AdditionalWhereObjs)
			return (fromClause, addWhereObj)
		else:
			return (self.getTableSyntax(tableList[0]), None)
		
	

