from ewah.operators.sql_base_operator import EWAHSQLBaseOperator
from ewah.constants import EWAHConstants as EC

from airflow.hooks.base_hook import BaseHook

import cx_Oracle

class EWAHOracleSQLOperator(EWAHSQLBaseOperator):

        _SQL_BASE = 'SELECT\n{columns}\nFROM "{schema}"."{table}"\nWHERE {where_clause}'
        _SQL_BASE_SELECT = \
            'SELECT * FROM ({select_sql}) t WHERE {{0}}'
        _SQL_COLUMN_QUOTE = '"'
        _SQL_MINMAX_CHUNKS = '''
            WITH base AS ({base})
            SELECT MIN({column}), MAX({column})
            FROM base;
        '''
        _SQL_CHUNKING_CLAUSE = '''
            AND {column} >= :from_value
            AND {column} <{equal_sign} :until_value
        '''
        _SQL_PARAMS = ':{0}'

        def __init__(self, *args, **kwargs):
            self.sql_engine = self._ORACLE
            super().__init__(*args, **kwargs)

        def _get_data_from_sql(self,
                sql,
                params={},
                return_dict=True,
            ):
            '''In Oracle, params are passed to the execute() function as kwargs
            https://cx-oracle.readthedocs.io/en/latest/user_guide/bind.html
            Params are then referenced like so:
                SELECT * FROM table WHERE field1 = :val1 AND field2 = :val2
            with a params dict structured like so: {'val1': 1, 'val2': 3}
            Also see here: https://stackoverflow.com/questions/35045879/cx-oracle-how-can-i-receive-each-row-as-a-dictionary
            to understand the workaround regarding return_dict
            '''
            def makeDictFactory(cursor):
                columnNames = [d[0] for d in cursor.description]
                def createRow(*args):
                    return dict(zip(columnNames, args))
                return createRow

            oracle_conn = cx_Oracle.connect(
                self.source_conn.login,
                self.source_conn.password,
                '{0}:{1}/{2}'.format(
                    self.source_conn.host,
                    self.source_conn.port,
                    self.source_conn.schema,
                ),
                encoding='UTF-8'
            )
            cursor = oracle_conn.cursor()
            if sql.strip()[-1:] == ';': # OracleSQL doesn't like semicolons
                sql = sql.strip()[:-1]
            self.log.info('Executing:\n{0}\n\nWith params:\n{1}'.format(
                sql,
                str(params),
            ))
            cursor.execute(sql, **params)
            if return_dict:
                cursor.rowfactory = makeDictFactory(cursor)
            data = cursor.fetchall()
            cursor.close()
            oracle_conn.close()
            return data
