'A generic way to insert a set of tuples into a table,
the whole set of inserts is treated as one transaction
Changes from standard DbApiHook implementation:
- Oracle SQL queries in cx_Oracle can not be terminated with a semicolon (\';\')
- Replace NaN values with NULL using numpy.nan_to_num (not using is_nan() because of input types error for strings)
- Coerce datetime cells to Oracle DATETIME format during insert'

def insert_rows(self, table, rows, target_fields=None, commit_every=1000):

    if target_fields:
        target_fields = ',    '.join(target_fields)
        target_fields = '({})'.format(target_fields)
    else:
        target_fields = ''
    conn = self.get_conn()
    cur = conn.cursor()
    if self.supports_autocommit:
        cur.execute('SET    autocommit    =    0')
    conn.commit()
    i = 0
    for row in rows:
        i += 1
        l = []
        for cell in row:
            if isinstance(cell, basestring):
                l.append((("'" + str(cell).replace("'", "''")) + "'"))
            elif (cell is None):
                l.append('NULL')
            elif ((type(cell) == float) and numpy.isnan(cell)):
                l.append('NULL')
            elif isinstance(cell, numpy.datetime64):
                l.append((("'" + str(cell)) + "'"))
            elif isinstance(cell, datetime):
                l.append((("to_date('" + cell.strftime('%Y-%m-%d    %H:%M:%S')) + "','YYYY-MM-DD    HH24:MI:SS')"))
            else:
                l.append(str(cell))
        values = tuple(l)
        sql = 'INSERT    /*+    APPEND    */    INTO    {0}    {1}    VALUES    ({2})'.format(table, target_fields, ','.join(values))
        cur.execute(sql)
        if ((i % commit_every) == 0):
            conn.commit()
            logging.info('Loaded    {i}    into    {table}    rows    so    far'.format(**locals()))
    conn.commit()
    cur.close()
    conn.close()
    logging.info('Done    loading.    Loaded    a    total    of    {i}    rows'.format(**locals()))
