## This is the implementation of the Multicorn ForeignDataWrapper class that does all of the work in RethinkDB
## R.Otten - 2014

from multicorn import ForeignDataWrapper
from multicorn.utils import log_to_postgres, ERROR, WARNING, DEBUG

import rethinkdb as r

from operatorFunctions import unknownOperatorException, getOperatorFunction

## The Foreign Data Wrapper Class:
class RethinkDBFDW(ForeignDataWrapper):

    """
    RethinkDB FDW for PostgreSQL
    """

    def __init__(self, options, columns):

        super(RethinkDBFDW, self).__init__(options, columns)

        log_to_postgres('options:  %s' % options, DEBUG)
        log_to_postgres('columns:  %s' % columns, DEBUG)

        if options.has_key('host'):
            self.host = options['host']
        else:
            self.host = 'localhost'
            log_to_postgres('Using Default host:  localhost.', WARNING)

        if options.has_key('port'):
            self.port = options['port']
        else:
            self.port = '28015'
            log_to_postgres('Using Default port: 28015.', WARNING)

        if options.has_key('database'):
            self.database = options['database']
        else:
            log_to_postgres('database parameter is required.', ERROR)

        if options.has_key('table_name'):
            self.table = options['table_name']
        else:
            log_to_postgres('table_name parameter is required.', ERROR)

        if options.has_key('auth_key'):
            self.auth_key = options['auth_key']
        else:
            self.auth_key = ''

        self.columns = columns


    # actually do the work:
    def _run_rethinkdb_action(self, action):

        # try to connect
        try:

            conn = r.connect(host=self.host, port=self.port, db=self.database, auth_key=self.auth_key)

        except Exception, e:

            log_to_postgres('Connection Falure:  %s' % e, ERROR)


        # Now try to run the action:
        try:

            log_to_postgres('RethinkDB Action:  %s' % action, DEBUG)
            result = action.run(conn)

        except Exception, e:

            conn.close()
            log_to_postgres('RethinkDB error:  %s' %e, ERROR)
 

        return result


    # SQL SELECT:
    def execute(self, quals, columns):

        log_to_postgres('Query Columns:  %s' % columns, DEBUG)
        log_to_postgres('Query Filters:  %s' % quals, DEBUG)

        myQuery = r.table(self.table)\
                   .pluck(self.columns.keys())

        for qual in quals:

            try:
                operatorFunction = getOperatorFunction(qual.operator)
            except unknownOperatorException, e:
                log_to_postgres(e, ERROR)

            myQuery = myQuery.filter(operatorFunction(r.row[qual.field_name], qual.value))

        return self._run_rethinkdb_action(action=myQuery)


    # SQL INSERT:
    def insert(self, new_values):

        log_to_postgres('Insert Request - new values:  %s' % new_values, DEBUG)

        return self._run_rethinkdb_action(action=r.table(self.table)\
                                                  .insert(new_values))

    # SQL UPDATE:
    def update(self, old_values, new_values):

        log_to_postgres('Update Request - new values:  %s' % new_values, DEBUG)

        if not old_values.has_key('id'):

             log_to_postgres('Update request requires old_values ID (PK).  Missing From:  %s' % old_values, ERROR)

        return self._run_rethinkdb_action(action=r.table(self.table)\
                                                  .get(old_values.id)\
                                                  .update(new_values))

    # SQL DELETE
    def delete(self, old_values):

        log_to_postgres('Delete Request - old values:  %s' % old_values, DEBUG)

        if not old_values.has_key('id'):

            log_to_postgres('Update request requires old_values ID (PK).  Missing From:  %s' % old_values, ERROR)

        return self._run_rethinkdb_action(action=r.table(self.table)\
                                                  .get(old_values.id)\
                                                  .delete())




@@ -123,16 +123,65 @@
 - 2014%0A%0A
+from collections import OrderedDict%0Aimport json%0A%0A
 from mul
@@ -381,16 +381,17 @@
 nction%0A%0A
+%0A
 ## The F
@@ -2821,35 +2821,45 @@
 e))%0A%0A        ret
-urn
+hinkResults =
  self._run_rethi
@@ -2886,16 +2886,889 @@
 yQuery)%0A
+         %0A        # By default, Multicorn seralizes dictionary types into something for hstore column types.%0A        # That looks something like this:   %22key =%3E value%22%0A        # What we really want is this:  %22%7Bkey:value%7D%22%0A        # so we serialize it here.  (This is git issue #1 for this repo, and issue #86 in the Multicorn repo.)%0A%0A        for resultRow in rethinkResults:%0A%0A            # I don't think we can mutate the row in the rethinkResults cursor directly.%0A            # It needs to be copied out of the cursor to be reliably mutable.%0A            row = OrderedDict()%0A            for resultColumn in resultRow.keys():%0A%0A                if type(resultRow%5BresultColumn%5D) is dict:%0A%0A                    row%5BresultColumn%5D = json.dumps(resultRow%5BresultColumn%5D)%0A%0A                else:%0A%0A                    row%5BresultColumn%5D = resultRow%5BresultColumn%5D%0A%0A            yield row%0A 
 %0A%0A    # 

check columns on select and serialize (json.dumps) the dictionaries