########################################################################
#
# File Name:            Oracle.py
#
# Documentation:        http://docs.4suite.org/4RDF/Drivers/Oracle.py.html
#
"""
Interface to Oracle for 4RDF
WWW: http://4suite.org/RDF        e-mail: support@4suite.org

Copyright (c) 2000-2001 Fourthought, Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

import string, re
import DCOracle
from Ft.Rdf.Drivers import DataBaseExceptions
from Ft.Rdf.Statement import Statement


def EscapeQuotes(qstr):
    if qstr is None:
	return ""
    return string.replace(qstr, "'", "''")


def GetDb(dbName):
    return DbAdapter(dbName)    


def CreateDb(dbName):
    db = DCOracle.Connect(dbName)
    cursor = db.cursor()
    db.execute("""
               CREATE TABLE ftrdf_statement
               (
                subject VARCHAR(2048),
                predicate VARCHAR(2048),
                object VARCHAR(2048),
                uri VARCHAR(2048)
               )""")

    #db.execute("""CREATE INDEX statement_index on ftrdf_statement (subject,predicate,object)""")
    db.execute("""
            CREATE TABLE ftrdf_bound
            (
                name varchar(2048) primary key,
                object varchar(2048)
            )""")
    db.commit()
    return DbAdapter(dbName)


def ExistsDb(dbName):
    db = DCOracle.Connect(dbName)
    cursor = db.cursor()
    cursor.execute("SELECT table_name from user_tables where table_name = 'FTRDF_STATEMENT'")
    rt1 = cursor.fetchall()
    cursor.execute("SELECT table_name from user_tables where table_name = 'FTRDF_BOUND'")
    rt2 = cursor.fetchall()
    return len(rt1) + len(rt2) > 1


def DestroyDb(dbName):
    db = DCOracle.Connect(dbName)
    try:
    	db.execute("DROP TABLE ftrdf_statement")
    except:
        pass
    try:
       db.execute("DROP TABLE ftrdf_bound")
    except:
       pass
    db.commit()

CONNECTION_POOL_SIZE = 5

NAME_PATTERN = "[a-zA-Z_][a-zA-Z0-9\.\-_]*"
ORACLE_CONNECT_STRING = re.compile(r"(%s)/(%s)@(%s)"%(NAME_PATTERN, NAME_PATTERN, NAME_PATTERN))


class DbAdapter:
    connectionPool = {}

    def __init__(self, name):
        #Note: "name" is really the Oracle service name or SID
        match = ORACLE_CONNECT_STRING.match(name)
        if not match:
            raise DataBaseExceptions.InvalidName
        self._username = match.group(1)
        self._password = match.group(2)
        self._sid = match.group(3)
        self._connectString = name
        self._tableName = 'ftrdf_Statement'
        self._connection = None
        return

    ####
    ##Transaction Management
    ####

    def begin(self):
        if self._connection:
            raise DataBaseExceptions.TransactionInProgress
        self._connection = DCOracle.Connect(self._connectString)
        return

    def commit(self):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        self._connection.commit()
        return

    def rollback(self):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        self._connection.rollback()
        return

    ####
    ###Operations
    ####

    def addStatement(self, subject, predicate, object, uri):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        s = EscapeQuotes(subject)
        p = EscapeQuotes(predicate)
        o = EscapeQuotes(object)
        u = EscapeQuotes(uri)
        self._connection.execute("INSERT INTO ftrdf_statement (subject, predicate, object, uri) VALUES ('%s', '%s', '%s', '%s')" % (s, p, o, u))
        return

    def removeStatement(self, subject, predicate, object, uri):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        s = EscapeQuotes(subject)
        p = EscapeQuotes(predicate)
        o = EscapeQuotes(object)
        u = EscapeQuotes(uri)
        self._connection.execute("DELETE FROM ftrdf_statement WHERE subject = '%s' AND predicate = '%s' AND object = '%s' AND uri = '%s'" % (s, p, o, u))
        return

    def complete(self, subject, predicate, object):
        s = EscapeQuotes(subject)
        p = EscapeQuotes(predicate)
        o = EscapeQuotes(object)
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        cursor = self._connection.cursor()
        cmd = "SELECT subject, predicate, object, uri FROM ftrdf_statement"
        where = ''
        if s:
            where = " WHERE subject = '%s'" % s
        if p:
            if where:
                where = where + ' AND'
            else:
                where = where + ' WHERE'
            where = where + " predicate = '%s'" % p
        if o:
            if where:
                where = where + ' AND'
            else:
                where = where + ' WHERE'
            where = where + " object = '%s'" % o
        if where:
            cmd = cmd + where
        cursor.execute(cmd)
        rows = cursor.fetchall()
	return rows
        #res = []
        #for r in rows:
        #    res.append((r[0], r['predicate'], r['object'], r['uri']))
        #return res

    def size(self):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
	cursor = self._connection.cursor()
        cursor.execute('SELECT subject from ' + self._tableName)
        rt = cursor.fetchall()
        return len(rt)

    def contains(self, subject, predicate, object, uri):
        #FIXME We can do this faster with a optimized SQL query
        return len(self.complete(subject, predicate, object)) != 0

    def purge(self):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        self._connection.execute('DELETE from ' + self._tableName)

    def bind(self, object, name):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        safeObject = self._makeSafe(object)
        self._connection.execute(
            "INSERT INTO ftrdf_bound (name, object) VALUES ('%s', '%s')" % (
                name,
                safeObject
            )
        )
        self._bindings[name] = object

    def unbind(self, name):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        self._connection.execute("DELETE FROM ftrdf_bound WHERE name = '%s'" % name)
        if self._bindings.has_key(name):
            del self._bindings[name]

    def lookup(self, name):
        if self._bindings.has_key(name):
            return self._bindings[name]
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        cursor = self._connection.cursor()
        cursor.execute(
            "SELECT object FROM ftrdf_bound WHERE name = '%s'" % name
        )
        rows = cursor.fetchall()
        if not rows:
            return None
        return self._restoreSafe(rows[0][0])

    def keys(self):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        cursor = self._connection.cursor()
        cursor.execute("SELECT name from ftrdf_bound")
        rows = cursor.fetchall()
        return map(lambda x:x[0], rows)

    def has_key(self,name):
        if not self._connection:
            raise DataBaseExceptions.NoTransaction
        cursor = self._connection.cursor()
        cursor.execute("SELECT object from ftrdf_bound where name = '%s'" % name)
        rows = cursor.fetchall()
        return map(lambda x:x[0], rows)
        return rows and 1 or 0

    def __makeSafe(self, object):
        #return EscapeQuotes(object.expression)
        stream = cStringIO.StringIO()
        cPickle.Pickler(stream).dump(object)
        safeString = EscapeQuotes(stream.getvalue())
        return safeString

    def __restoreSafe(self, pickled):
        #from Ft.Rdf.Parsers.Ril import RilParserImp
        #p = RilParserImp.RilParserImp()
        #return p.parse(pickled)
        stream = cStringIO.StringIO(pickled)
        object = cPickle.Unpickler(stream).load()
        return object










