########################################################################
#
# File Name: 	        Adapter.py
#
# Documentation:	http://docs.ftsuite.com/4ODS/Drivers/Oracle/Adapter.py.html
#
"""
The Oracle back-end.
WWW: http://4suite.org/4ODS         e-mail: support@4suite.org

Copyright (c) 1999 Fourthought, Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""
import sys,string,types, os

import string, cPickle, tempfile
from Ft.Ods import FtodsBindingException
from Ft.Ods import FtodsObjectNotFound

from Ft.Ods.StorageManager.Adapters import Constants
from Ft.Ods.StorageManager.Adapters import SQL
from Ft.Ods.StorageManager.Adapters.SQL import CompiliedStatement
import OracleStatements
import Manager

def _4ods_anyToOdmg(value):
    return cPickle.loads(value)
def _4ods_anyToSql(value):
    return "'"+escapeQuotes(cPickle.dumps(value))+"'"

def escapeQuotes(qstr):
    return string.replace(qstr,"'","''")

LARGE_FILE_DIR = None
if os.name == 'posix':
    LARGE_FILE_DIR = '/var/local/data/blobs'
else:
    LARGE_FILE_DIR = 'c:\TEMP'
    
LARGE_FILE_DIR = os.environ.get('FT_ORACLE_BLOB_DIR', LARGE_FILE_DIR)

if LARGE_FILE_DIR is None:    
    raise DbmError('BOOTSTRAP', 'Please define FT_ORACLE_BLOB_DIR in the environment')


class DbAdapter(SQL.DbAdapter):
    statements = {}
    uptoDateStatements = {}
    nameMap = {}
    manager = Manager.DbManager()
    
    ################
    #The Transactional Interface
    ################
    def begin(self,db):
        pass

    def commit(self,db):
        db[0].commit()

    def abort(self,db):
        db[0].rollback()

    def checkpoint(self,db):
        db[0].commit()
        newDb = self.manager.connect(db[2])
        db[0] = newDb[0]
        db[1] = newDb[1]
        

    def lock(self,db,repoId,mode):
        raise "NOT DONE"
        table_name = self._4ods_getTableNameFromRepoId(txId,repoId)
        if mode == Constants.READ_LOCK:
            #Do a read lock
            qstr = 'LOCK %s IN ACCESS SHARE MODE'
        else:
            #Do a write lock
            qstr = 'LOCK %s in ACCESS EXCLUSIVE MODE'

        self._db.execute(qstr)
        return

    def try_lock(self,repoId,mode):
        raise "NOT DONE"
        #FIXME how do we do this in Postgres???
        self.lock(repoId,mode)



    #####################################
    #MISC
    #####################################


    def _execute(self,db,st):
        db[0].execute(st)

    statements[CompiliedStatement.NEW_PYTHON_CLASS_ID] = OracleStatements.NewPythonClassIdStatement()
    statements[CompiliedStatement.NEW_PYTHON_CLASS] = OracleStatements.NewPythonClassStatement()
    statements[CompiliedStatement.GET_PYTHON_CLASS] = OracleStatements.GetPythonClassStatement()
    statements[CompiliedStatement.DELETE_PYTHON_CLASS] = OracleStatements.DeletePythonClassStatement()
    statements[CompiliedStatement.GET_PYTHON_CLASS_ID] = OracleStatements.GetPythonClassIdStatement()
    

    #####################
    #Repository Management
    #####################
    statements[CompiliedStatement.NEW_REPO_ID] = OracleStatements.NewRepoIdStatement()
    statements[CompiliedStatement.NEW_REPO] = OracleStatements.NewRepoStatement()
    statements[CompiliedStatement.NEW_REPO_MAP] = OracleStatements.NewRepoMapStatement()
    statements[CompiliedStatement.GET_REPO] = OracleStatements.GetRepoStatement()
    statements[CompiliedStatement.GET_REPO_TUPLE] = OracleStatements.GetRepoTupleStatement()
    statements[CompiliedStatement.DELETE_REPO] = OracleStatements.DeleteRepoStatement()
    statements[CompiliedStatement.DELETE_REPO_MAP] = OracleStatements.DeleteRepoMapStatement()
    statements[CompiliedStatement.UPDATE_REPO_TIME] = OracleStatements.UpdateRepoTimeStatement()
    uptoDateStatements[Constants.ObjectTypes.REPO] = OracleStatements.CheckRepoTimeStatement()
    nameMap['_repoId'] = 'repoId'
    nameMap['name'] = 'rname'
    nameMap['comment'] = 'rcomment'
    nameMap['type'] = 'rtype'


    #################################
    #Objects
    #################################
    statements[CompiliedStatement.NEW_OBJECT_ID] = OracleStatements.NewObjectIdStatement()
    statements[CompiliedStatement.NEW_OBJECT_MAP] = OracleStatements.NewObjectMapStatement()
    statements[CompiliedStatement.NEW_OBJECT] = OracleStatements.NewObjectStatement()
    statements[CompiliedStatement.UPDATE_OBJECT_TIME] = OracleStatements.UpdateObjectTimeStatement()
    statements[CompiliedStatement.GET_OBJECT] = OracleStatements.GetObjectStatement()
    statements[CompiliedStatement.GET_OBJECT_TUPLE] = OracleStatements.GetObjectTupleStatement()
    statements[CompiliedStatement.DELETE_OBJECT] = OracleStatements.DeleteObjectStatement()
    statements[CompiliedStatement.DELETE_OBJECT_MAP] = OracleStatements.DeleteObjectMapStatement()
    statements[CompiliedStatement.GET_OBJECT_IDS] = OracleStatements.GetObjectIdsStatement()
    uptoDateStatements[Constants.ObjectTypes.OBJECT] = OracleStatements.CheckObjectTimeStatement()


    #############################
    #Extents
    #############################
    statements[CompiliedStatement.NEW_EXTENT_ID] = OracleStatements.NewExtentIdStatement()
    statements[CompiliedStatement.NEW_EXTENT] = OracleStatements.NewExtentStatement()
    statements[CompiliedStatement.GET_EXTENT_ID] = OracleStatements.GetExtentIdStatement()
    statements[CompiliedStatement.ADD_EXTENT_MAPPING] = OracleStatements.AddExtentMappingStatement()
    statements[CompiliedStatement.DROP_EXTENT_MAPPING] = OracleStatements.DropExtentMappingStatement()
    statements[CompiliedStatement.DROP_ALL_EXTENT_MAPPING] = OracleStatements.DropAllExtentMappingStatement()
    statements[CompiliedStatement.DROP_EXTENT] = OracleStatements.DropExtentStatement()
    statements[CompiliedStatement.GET_EXTENT_NAMES] = OracleStatements.GetExtentNamesStatement()
    statements[CompiliedStatement.GET_EXTENT_ID_AND_TYPE] = OracleStatements.GetExtentIdAndTypeStatement()
    statements[CompiliedStatement.GET_EXTENT] = OracleStatements.GetExtentStatement()


    ##############################
    #Bindings
    #############################
    statements[CompiliedStatement.GET_BOUND_OBJECT] = OracleStatements.GetBoundObjectStatement()
    statements[CompiliedStatement.ADD_BOUND_NAME] = OracleStatements.AddBoundNameStatement()
    statements[CompiliedStatement.DROP_BOUND_NAME] = OracleStatements.DropBoundNameStatement()
    statements[CompiliedStatement.GET_BOUND_NAMES] = OracleStatements.GetBoundNamesStatement()
    statements[CompiliedStatement.GET_OBJECT_BOUND_NAMES] = OracleStatements.GetObjectBoundNamesStatement()


    ##############################
    #Collections
    ##############################
    statements[CompiliedStatement.NEW_COLLECTION_ID] = OracleStatements.NewCollectionIdStatement()
    statements[CompiliedStatement.NEW_COLLECTION] = OracleStatements.NewCollectionStatement()
    statements[CompiliedStatement.UPDATE_COLLECTION_INDEXES_ADD] = OracleStatements.UpdateCollectionIndexesAddStatement()
    statements[CompiliedStatement.NEW_COLLECTION_ENTRY] = OracleStatements.NewCollectionEntryStatement()
    statements[CompiliedStatement.DELETE_COLLECTION_ENTRY] = OracleStatements.DeleteCollectionEntryStatement()
    statements[CompiliedStatement.UPDATE_COLLECTION_INDEXES_REMOVE] = OracleStatements.UpdateCollectionIndexesRemoveStatement()
    statements[CompiliedStatement.DELETE_COLLECTION] = OracleStatements.DeleteCollectionStatement()
    statements[CompiliedStatement.DELETE_COLLECTION_TUPLE] = OracleStatements.DeleteCollectionTupleStatement()
    statements[CompiliedStatement.GET_COLLECTION] = OracleStatements.GetCollectionStatement()
    statements[CompiliedStatement.GET_COLLECTION_TUPLE] = OracleStatements.GetCollectionTupleStatement()
    statements[CompiliedStatement.UPDATE_COLLECTION_TIME] = OracleStatements.UpdateCollectionTimeStatement()

    uptoDateStatements[Constants.ObjectTypes.COLLECTION] = OracleStatements.CheckCollectionTimeStatement()


    #######################
    #Blobs
    #######################

    #FIXME replace with oracle blobs???

    def newBlob(self,db):
        tempfile.tempdir = LARGE_FILE_DIR
        fileName = tempfile.mktemp()
        db[1].execute("SELECT ftods_blobid.NEXTVAL from DUAL")
        bid = int(db[1].fetchall()[0][0])
        db[0].execute("INSERT into ftods_blobs (bid,filename) VALUES (%d,'%s')" % (bid,fileName))
        open(fileName,'w').write('')
        return bid

    def readBlob(self,db,bid):
        db[1].execute("SELECT fileName from ftods_blobs WHERE bid = %d" % (bid))
        rt = db[1].fetchall()
        if not len(rt):
            return None
        fileName = rt[0][0]
        try:
            data = open(fileName,'r').read()
        except IOError:
            return None
        return data

    def writeBlob(self,db,bid,data):
        db[1].execute("SELECT fileName from ftods_blobs WHERE bid = %d" % (bid))
        rt = db[1].fetchall()
        if not len(rt):
            return None

        fileName = rt[0][0]
        open(fileName,'w').write(data)

    def deleteBlob(self,db,bid):
        db[1].execute("SELECT fileName from ftods_blobs WHERE bid = %d" % (bid))
        rt = db[1].fetchall()
        if not len(rt):
            return None

        fileName = rt[0][0]
        os.unlink(fileName)




    #Overdides
    def _4ods_createUpdateRepoString(self,tableName,rid,types,names,values):
        qstr = self._4ods_tupleToUpdateString(types,names,values)
        if not qstr: return
        return 'UPDATE %s SET %s WHERE repoid = %i' % (tableName,
                                                       qstr,
                                                       rid
                                                       )


    def _4ods_createUpdateObjectString(self,oid,typeId,types,names,nameIds,values):
        qstr = 'UPDATE interface__%s SET ' % typeId
        for ctr in range(len(nameIds)):
            if values[ctr] != None:
                if names[ctr] == '_oid':
                    continue
                else:
                    newName = 'def__%s' % nameIds[ctr]
                qstr = qstr + '%s = %s,' % (newName,self.toSqlValues[types[ctr]](values[ctr]))
                

        qstr = "%s WHERE oid = %i"%(qstr[:-1],oid)
        return qstr



    toSqlValues = {Constants.Types.STRING:lambda x:"'%s'"%escapeQuotes(x),
                   Constants.Types.BLOB:str,
                   Constants.Types.UNSIGNED_SHORT:str,
                   Constants.Types.OBJECT:str,
                   Constants.Types.ENUMERATION:str,
                   Constants.Types.SIGNED_SHORT:str,
                   Constants.Types.DOUBLE:str,
                   Constants.Types.FLOAT:str,
                   Constants.Types.UNSIGNED_LONG:lambda x:"%g"%x,
                   Constants.Types.SIGNED_LONG_LONG:lambda x:repr(long(x))[:-1],
                   Constants.Types.SIGNED_LONG:lambda x:"%g"%x,
                   Constants.Types.BOOLEAN:str,
                   Constants.Types.OBJECT_LIST:str,
                   Constants.Types.OBJECT_SET:str,
                   Constants.Types.OBJECT_BAG:str,
                   Constants.Types.STRING_LIST:str,
                   Constants.Types.STRING_BAG:str,
                   Constants.Types.STRING_SET:str,
                   Constants.Types.BOOLEAN_LIST:str,
                   Constants.Types.BOOLEAN_BAG:str,
                   Constants.Types.BOOLEAN_SET:str,
                   Constants.Types.SIGNED_SHORT_LIST:str,
                   Constants.Types.SIGNED_SHORT_BAG:str,
                   Constants.Types.SIGNED_SHORT_SET:str,
                   Constants.Types.UNSIGNED_SHORT_LIST:str,
                   Constants.Types.UNSIGNED_SHORT_BAG:str,
                   Constants.Types.UNSIGNED_SHORT_SET:str,
                   Constants.Types.SIGNED_LONG_LIST:str,
                   Constants.Types.SIGNED_LONG_BAG:str,
                   Constants.Types.SIGNED_LONG_SET:str,
                   Constants.Types.UNSIGNED_LONG_LIST:str,
                   Constants.Types.UNSIGNED_LONG_BAG:str,
                   Constants.Types.UNSIGNED_LONG_SET:str,
                   Constants.Types.SIGNED_LONG_LONG_LIST:str,
                   Constants.Types.SIGNED_LONG_LONG_BAG:str,
                   Constants.Types.SIGNED_LONG_LONG_SET:str,
                   Constants.Types.FLOAT_LIST:str,
                   Constants.Types.FLOAT_BAG:str,
                   Constants.Types.FLOAT_SET:str,
                   Constants.Types.DOUBLE_LIST:str,
                   Constants.Types.DOUBLE_BAG:str,
                   Constants.Types.DOUBLE_SET:str,
                   Constants.Types.ANY:_4ods_anyToSql,
                   }
                   

    toOdmgValues = {Constants.Types.UNSIGNED_SHORT:int,
                    Constants.Types.OBJECT:int,
                    Constants.Types.ENUMERATION:int,
                    Constants.Types.SIGNED_SHORT:int,
                    Constants.Types.UNSIGNED_LONG:int,
                    Constants.Types.SIGNED_LONG_LONG:long,
                    Constants.Types.SIGNED_LONG:long,
                    Constants.Types.DOUBLE:float,
                    Constants.Types.FLOAT:float,
                    Constants.Types.STRING:lambda x:x and str(x) or "",
                    Constants.Types.BLOB:int,
                    Constants.Types.BOOLEAN:int,
                    Constants.Types.OBJECT_LIST:int,
                    Constants.Types.OBJECT_SET:int,
                    Constants.Types.OBJECT_BAG:int,
                    Constants.Types.STRING_LIST:int,
                    Constants.Types.STRING_BAG:int,
                    Constants.Types.STRING_SET:int,
                    Constants.Types.BOOLEAN_LIST:int,
                    Constants.Types.BOOLEAN_BAG:int,
                    Constants.Types.BOOLEAN_SET:int,
                    Constants.Types.SIGNED_SHORT_LIST:int,
                    Constants.Types.SIGNED_SHORT_BAG:int,
                    Constants.Types.SIGNED_SHORT_SET:int,
                    Constants.Types.UNSIGNED_SHORT_LIST:int,
                    Constants.Types.UNSIGNED_SHORT_BAG:int,
                    Constants.Types.UNSIGNED_SHORT_SET:int,
                    Constants.Types.SIGNED_LONG_LIST:int,
                    Constants.Types.SIGNED_LONG_BAG:int,
                    Constants.Types.SIGNED_LONG_SET:int,
                    Constants.Types.UNSIGNED_LONG_LIST:int,
                    Constants.Types.UNSIGNED_LONG_BAG:int,
                    Constants.Types.UNSIGNED_LONG_SET:int,
                    Constants.Types.SIGNED_LONG_LONG_LIST:int,
                    Constants.Types.SIGNED_LONG_LONG_BAG:int,
                    Constants.Types.SIGNED_LONG_LONG_SET:int,
                    Constants.Types.FLOAT_LIST:int,
                    Constants.Types.FLOAT_BAG:int,
                    Constants.Types.FLOAT_SET:int,
                    Constants.Types.DOUBLE_LIST:int,
                    Constants.Types.DOUBLE_BAG:int,
                    Constants.Types.DOUBLE_SET:int,
                    Constants.Types.ANY:_4ods_anyToOdmg,
                    }




