########################################################################
#
# File Name:            Postgres.py
#
# Documentation:        http://docs.4suite.org/4RDF/Drivers/Postgres.py.html
#
"""
Interface to Postgres 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 sys, string, cPickle, cStringIO
from Ft.Lib.PgDatabase import EscapeQuotes
from Ft.Rdf.Drivers import DataBaseExceptions
from Ft.Rdf import Statement, Model

class Commands:
    
    ADD = 10
    REMOVE = 11
    COMPLETE = 12
    CONTAINS = 13
    SIZE = 14
    SIZE_ALL = 15

    BIND = 20
    UNBIND = 21
    LOOKUP = 22
    KEYS = 23
    HAS_KEY = 24


class SqlAdapter:
    def __init__(self, sqlCommands, comparisons):
        self._sql = sqlCommands
        self._cmp = comparisons
        self._db = None
        return

    def begin(self):
        raise NotImplementedError('%s should override this' %
                                  self.__class__.__name__)

    def commit(self):
        raise NotImplementedError('%s should override this' %
                                  self.__class__.__name__)

    def rollback(self):
        raise NotImplementedError('%s should override this' %
                                  self.__class__.__name__)

    def add(self, statements):
        # Takes a list of tuples [(s, p, o, stmtUri, srcUri), ...]
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        for s in statements:
            self._sql[Commands.ADD].execute(self._db,
                                            subject=EscapeQuotes(s[0]),
                                            predicate=EscapeQuotes(s[1]),
                                            object=EscapeQuotes(s[2]),
                                            statementUri=EscapeQuotes(s[3]),
                                            sourceUri=EscapeQuotes(s[4]),
                                            )

        return
        
    def remove(self, statements):
        for s in statements:
            self.removePattern(s[0], s[1], s[2], s[3], s[4], {})

    def removePattern(self, subject, predicate, object, statementUri,
                      sourceUri, flags):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        subject = EscapeQuotes(subject)
        predicate = EscapeQuotes(predicate)
        object = EscapeQuotes(object)
        statementUri = EscapeQuotes(statementUri)
        sourceUri = EscapeQuotes(sourceUri)

        # We can use an emtpy string check because of EscapeQuotes()
        command = self._sql[(Commands.REMOVE,
                             subject != '',
                             predicate != '',
                             object != '',
                             statementUri != '',
                             sourceUri != '',
                             )]

        command.execute(self._db,
                        subject=subject,
                        predicate=predicate,
                        object=object,
                        statementUri=statementUri,
                        sourceUri=sourceUri,
                        subjectOp=self._cmp[flags.get('subjectFlags')],
                        predicateOp=self._cmp[flags.get('predicateFlags')],
                        objectOp=self._cmp[flags.get('objectFlags')],
                        statementUriOp=self._cmp[flags.get('statementUriFlags')],
                        sourceUriOp=self._cmp[flags.get('sourceUriFlags')],
                        )
        return        

    def complete(self, subject, predicate, object, statementUri, sourceUri,
                 flags):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        subject = EscapeQuotes(subject)
        predicate = EscapeQuotes(predicate)
        object = EscapeQuotes(object)
        statementUri = EscapeQuotes(statementUri)
        sourceUri = EscapeQuotes(sourceUri)
        
        # We can use an emtpy string check because of EscapeQuotes()
        command = self._sql[(Commands.COMPLETE,
                             subject != '',
                             predicate != '',
                             object != '',
                             statementUri != '',
                             sourceUri != '',
                             )]
                               
        result = command.query(self._db,
                               subject=subject,
                               predicate=predicate,
                               object=object,
                               statementUri=statementUri,
                               sourceUri=sourceUri,
                               subjectOp=self._cmp[flags.get('subjectFlags')],
                               predicateOp=self._cmp[flags.get('predicateFlags')],
                               objectOp=self._cmp[flags.get('objectFlags')],
                               statementUriOp=self._cmp[flags.get('statementUriFlags')],
                               sourceUriOp=self._cmp[flags.get('sourceUriFlags')],
                               )
        return map(lambda x: (x[0], x[1], x[2], x[3], x[4]), result or [])


    def size(self, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        if sourceUri:
            uri = EscapeQuotes(sourceUri)
            result = self._sql[Commands.SIZE].query(self._db, sourceUri=uri)
        else:
            result = self._sql[Commands.SIZE_ALL].query(self._db)
        return result and result[0][0] or 0

    def contains(self, subject, predicate, object, statementUri, sourceUri, flags):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        subject = EscapeQuotes(subject)
        predicate = EscapeQuotes(predicate)
        object = EscapeQuotes(object)
        statementUri = EscapeQuotes(statementUri)
        sourceUri = EscapeQuotes(sourceUri)
        
        # We can use an emtpy string check because of EscapeQuotes()
        command = self._sql[(Commands.CONTAINS,
                             subject != '',
                             predicate != '',
                             object != '',
                             statementUri != '',
                             sourceUri != '',
                             )]
                               
        result = command.query(self._db,
                               subject=subject,
                               predicate=predicate,
                               object=object,
                               statementUri=statementUri,
                               sourceUri=sourceUri,
                               subjectOp=self._cmp[flags.get('subjectFlags')],
                               predicateOp=self._cmp[flags.get('predicateFlags')],
                               objectOp=self._cmp[flags.get('objectFlags')],
                               statementUriOp=self._cmp[flags.get('statementUriFlags')],
                               sourceUriOp=self._cmp[flags.get('sourceUriFlags')],
                               )

        return result and result[0][0] > 0 or 0

    def bind(self, object, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        
        self._sql[Commands.BIND].execute(self._db,
                                         object=EscapeQuotes(cPickle.dumps(object)),
                                         name=EscapeQuotes(name),
                                         sourceUri=EscapeQuotes(sourceUri),
                                         )
        return

    def unbind(self, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        self._sql[Commands.UNBIND].execute(self._db,
                                           name=EscapeQuotes(name),
                                           sourceUri=EscapeQuotes(sourceUri),
                                           )
        return

    def lookup(self, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        result = self._sql[Commands.LOOKUP].query(self._db,
                                                  name=EscapeQuotes(name),
                                                  sourceUri=EscapeQuotes(sourceUri),
                                                  )
        return result and cPickle.loads(result[0][0]) or None

    def keys(self, sourceUri):
        result = self._sql[Commands.KEYS].query(self._db,
                                                sourceUri=EscapeQuotes(sourceUri),
                                                )
        return map(lambda x: x[0], result or [])

    def has_key(self, name, sourceUri):
        result = self._sql[Commands.HAS_KEY].query(self._db,
                                                   name=EscapeQuotes(name),
                                                   sourceUri=EscapeQuotes(sourceUri),
                                                   )
        return result and int(result[0][0]) or 0
