storage_oracle.c   [plain text]


/*
 * jabberd - Jabber Open Source Server
 * Copyright (c) 2002-2003 Jeremie Miller, Thomas Muldowney,
 *                         Ryan Eatmon, Robert Norris
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
 */

/** @file sm/storage_oracle.c
  * @brief oracle storage module
  * @author Nathan Christiansen
  * $Date: 2006/03/14 23:27:28 $
  * $Revision: 1.1 $
  */

#include "sm.h"

#ifdef STORAGE_ORACLE

#include <string.h>
#include <oci.h>

/** internal structure, holds our data */
typedef struct OracleDriver
{
  OCIEnv *ociEnvironment;
  OCIError *ociError;
  OCISvcCtx *ociService;
  OCIStmt *ociStatement;
  OCIDefine *ociDefine;
  OCIBind *ociBind;
  xht filters;
  char *prefix;
  char *svUser;
  char *svPass;
} *OracleDriverPointer;

#define BLOCKSIZE (1024)

/** internal: do and return the math and ensure it gets realloc'd */
static int _st_oracle_realloc(void **oblocks, int len)
{
  void *nblocks;
  int nlen;

  /* round up to standard block sizes */
  nlen = (((len-1)/BLOCKSIZE)+1)*BLOCKSIZE;

  /* keep trying till we get it */
  while((nblocks = realloc(*oblocks, nlen)) == NULL) sleep(1);
  *oblocks = nblocks;
  return nlen;
}

/** this is the safety check used to make sure there's always enough mem */
#define ORACLE_SAFE(blocks, size, len) if((size) > len) len = _st_oracle_realloc((void**)&(blocks),(size));

int checkOCIError(st_driver_t drv, char *szDoing, OCIError *m_ociError, sword nStatus)
{
  text txtErrorBuffer[512];
  ub4 nErrorCode;

  switch (nStatus)
  {
    case OCI_SUCCESS:
      break;
    case OCI_SUCCESS_WITH_INFO:
      log_write(drv->st->sm->log, LOG_ERR, "(%s) Error - OCI_SUCCESS_WITH_INFO\n", szDoing);
      break;
    case OCI_NEED_DATA:
      log_write(drv->st->sm->log, LOG_ERR, "(%s) Error - OCI_NEED_DATA\n", szDoing);
      break;
    case OCI_NO_DATA:
      log_write(drv->st->sm->log, LOG_ERR, "(%s) Error - OCI_NODATA\n", szDoing);
      break;
    case OCI_ERROR:
      OCIErrorGet(m_ociError, (ub4) 1, (text *) NULL, &nErrorCode, txtErrorBuffer, (ub4) sizeof(txtErrorBuffer), OCI_HTYPE_ERROR);
      log_write(drv->st->sm->log, LOG_ERR, "(%s) Error - %s\n", szDoing, txtErrorBuffer);
      break;
    case OCI_INVALID_HANDLE:
      log_write(drv->st->sm->log, LOG_ERR, "(%s) Error - OCI_INVALID_HANDLE\n", szDoing);
      break;
    case OCI_STILL_EXECUTING:
      log_write(drv->st->sm->log, LOG_ERR, "(%s) Error - OCI_STILL_EXECUTE\n", szDoing);
      break;
    default:
      break;
  }

  return nStatus;
}

int oracle_escape_string(char *dest, int dest_length, char *src, int src_length)
{
  int  result = 0;
  /* src is not null ended */
  char *src_end  = src + src_length;
  char *dest_end = dest + dest_length - 1;
   
  while (src < src_end)
  {
    if (dest < dest_end)
    {
      static const char ESCAPED_STR[] = "'&";
      
      if (strchr(ESCAPED_STR, *src) != NULL)
      {
        if (dest + 2 < dest_end)
        {
          *dest++ = '\'';
	  *dest++ = *src;
	  src++;
        }
        else
        {
	  result = -1;
	  break;
        }
      }
      else
      {
        *dest++ = *src++;
      }
    }
    else
    {
      result = -1;
      break;
    }
  }
  *dest = '\0';

  return result;
}
      


static int oracle_ping(st_driver_t drv)
{
  OracleDriverPointer odpOracleDriver = (OracleDriverPointer)drv->private;

  // Prepare the check statement
  int nResultCode = OCIStmtPrepare(odpOracleDriver->ociStatement, odpOracleDriver->ociError,
                                   "select sysdate from dual", (ub4) 24, OCI_NTV_SYNTAX,
                                   OCI_DEFAULT);

  // This is the real check
  nResultCode = OCIStmtExecute(odpOracleDriver->ociService, odpOracleDriver->ociStatement, odpOracleDriver->ociError,
                                   (ub4) 0, (ub4) 0,
                                   (CONST OCISnapshot *) NULL, (OCISnapshot *) NULL,
                                   OCI_DESCRIBE_ONLY );


  // If there was an error...
  if (nResultCode != 0)
  {
    char szErrorBuffer[250];
    char *svHost, *svUser, *svPass;

    OCIErrorGet((dvoid *)odpOracleDriver->ociError, (ub4) 1, (text *) NULL, &nResultCode, szErrorBuffer, (ub4) sizeof(szErrorBuffer), OCI_HTYPE_ERROR);
    log_write(drv->st->sm->log, LOG_ERR, "storage_oracle.c (oracle_ping): %s", szErrorBuffer);

    // Obtain user configuration
    svHost = config_get_one(drv->st->sm->config, "storage.oracle.host", 0);
    svUser = config_get_one(drv->st->sm->config, "storage.oracle.user", 0);
    svPass = config_get_one(drv->st->sm->config, "storage.oracle.pass", 0);

    // Logon to the database
    nResultCode = OCILogon((dvoid *)odpOracleDriver->ociEnvironment, (dvoid *)odpOracleDriver->ociError, &(odpOracleDriver->ociService), svUser, strlen(svUser), svPass, strlen(svPass), svHost, strlen(svHost));

    
    if (nResultCode != 0)
    {
      OCIErrorGet((dvoid *)odpOracleDriver->ociError, (ub4) 1, (text *) NULL, &nResultCode, szErrorBuffer, (ub4) sizeof(szErrorBuffer), OCI_HTYPE_ERROR);
      log_write(drv->st->sm->log, LOG_ERR, "storage_oracle.c (oracle_ping): %s", szErrorBuffer);
    }
  }

  return nResultCode;
}



static void _st_oracle_convert_filter_recursive(st_filter_t f, char **buf, int *buflen, int *nbuf)
{
  st_filter_t scan;

  switch(f->type)
  {
    case st_filter_type_PAIR:
      ORACLE_SAFE((*buf), *buflen + 12, *buflen);
      *nbuf += sprintf(&((*buf)[*nbuf]), "( \"%s\" = \'%s\' ) ", f->key, f->val);

      break;

      case st_filter_type_AND:
        ORACLE_SAFE((*buf), *buflen + 2, *buflen);
        *nbuf += sprintf(&((*buf)[*nbuf]), "( ");

        for(scan = f->sub; scan != NULL; scan = scan->next)
        {
          _st_oracle_convert_filter_recursive(scan, buf, buflen, nbuf);

          if(scan->next != NULL)
          {
            ORACLE_SAFE((*buf), *buflen + 4, *buflen);
            *nbuf += sprintf(&((*buf)[*nbuf]), "AND ");
          }
        }

        ORACLE_SAFE((*buf), *buflen + 2, *buflen);
        *nbuf += sprintf(&((*buf)[*nbuf]), ") ");

        return;

      case st_filter_type_OR:
        ORACLE_SAFE((*buf), *buflen + 2, *buflen);
        *nbuf += sprintf(&((*buf)[*nbuf]), "( ");

        for(scan = f->sub; scan != NULL; scan = scan->next)
        {
          _st_oracle_convert_filter_recursive(scan, buf, buflen, nbuf);

          if(scan->next != NULL)
          {
            ORACLE_SAFE((*buf), *buflen + 3, *buflen);
            *nbuf += sprintf(&((*buf)[*nbuf]), "OR ");
          }
        }

        ORACLE_SAFE((*buf), *buflen + 2, *buflen);
        *nbuf += sprintf(&((*buf)[*nbuf]), ") ");

        return;

      case st_filter_type_NOT:
        ORACLE_SAFE((*buf), *buflen + 6, *buflen);
        *nbuf += sprintf(&((*buf)[*nbuf]), "( NOT ");

        _st_oracle_convert_filter_recursive(f->sub, buf, buflen, nbuf);

        ORACLE_SAFE((*buf), *buflen + 2, *buflen);
        *nbuf += sprintf(&((*buf)[*nbuf]), ") ");

        return;
  }
}

static char *_st_oracle_convert_filter(st_driver_t drv, char *owner, char *filter)
{
  OracleDriverPointer data = (OracleDriverPointer) drv->private;
  char *buf = NULL, *sbuf = NULL, *cfilter;
  int buflen = 0, nbuf = 0, fbuf;
  st_filter_t f;

  ORACLE_SAFE(buf, 24 + strlen(owner), buflen);

  nbuf = sprintf(buf, "\"collection-owner\" = '%s'", owner);

  sbuf = xhash_get(data->filters, filter);
  if(sbuf != NULL)
  {
    ORACLE_SAFE(buf, buflen + strlen(sbuf) + 7, buflen);
    nbuf += sprintf(&buf[nbuf], " AND %s", sbuf);
    return buf;
  }

  cfilter = pstrdup(xhash_pool(data->filters), filter);

  f = storage_filter(filter);
  if(f == NULL)
  {
    return buf;
  }

  ORACLE_SAFE(buf, buflen + 5, buflen);
  nbuf += sprintf(&buf[nbuf], " AND ");

  fbuf = nbuf;

  _st_oracle_convert_filter_recursive(f, &buf, &buflen, &nbuf);

  xhash_put(data->filters, cfilter, pstrdup(xhash_pool(data->filters), &buf[fbuf]));

  pool_free(f->p);

  return buf;
}

static st_ret_t _st_oracle_add_type(st_driver_t drv, char *type)
{
  return st_SUCCESS;
}


static st_ret_t _st_oracle_put_guts(st_driver_t drv, char *type, char *owner, os_t os)
{
  static const char STR_PREFIX[] = "STR";
  static const char NAD_PREFIX[] = "NAD";
  
  OracleDriverPointer data = (OracleDriverPointer) drv->private;
  char *left = NULL, *right = NULL;
  int lleft = 0, lright = 0, nleft, nright;
  os_object_t o;
  char *key = NULL, *cval = NULL;
  int cval_len;
  dvoid *val = NULL;
  os_type_t ot;
  char *xml = NULL;
  int xlen;
  char tbuf[128];
  int nResultCode = 0;

  if(os_count(os) == 0)
  {
    return st_SUCCESS;
  }

  if(data->prefix != NULL)
  {
    snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, type);
    type = tbuf;
  }

  if(os_iter_first(os))
  {
    do
    {
      ORACLE_SAFE(left, strlen(type) + 36, lleft);
      nleft = sprintf(left, "INSERT INTO \"%s\" ( \"collection-owner\"", type);
  
      ORACLE_SAFE(right, strlen(owner) + 15, lright);
      nright = sprintf(right, " ) VALUES ( '%s'", owner);
  
      o = os_iter_object(os);

      if(os_object_iter_first(o))
      {
        do 
        {
          os_object_iter_get(o, &key, &val, &ot);
      
          switch(ot) 
          {
            case os_type_BOOLEAN:
              cval = val ? strdup("1") : strdup("0");
              break;
      
            case os_type_INTEGER:
              cval = (char *) malloc(sizeof(char) * 20);
              sprintf(cval, "%d", (int) val);
              strlen(cval);
              break;
      
            case os_type_STRING:
	      /* Ensure that we have enough space for an escaped string. */
              cval_len = (strlen((char *) val) * 2) + 1;
	      cval     = (char *) malloc(cval_len);
              oracle_escape_string(cval , cval_len, (char *) val, strlen(val));
              break;
      
            /* !!! might not be a good idea to mark nads this way */
            case os_type_NAD:
              nad_print((nad_t) val, 0, &xml, &xlen);
	      /* Ensure that we have enough space for an escaped string. */
	      cval_len = (xlen * 2) + sizeof (NAD_PREFIX);
              cval = (char *) malloc(cval_len);
	      memcpy(cval, NAD_PREFIX, sizeof (NAD_PREFIX) - 1);
              oracle_escape_string(cval + sizeof (NAD_PREFIX) - 1, 
	                           cval_len - sizeof (NAD_PREFIX) + 1, (char *) xml, xlen);
              break;
          }
      
          log_debug(ZONE, "key %s val %s", key, cval);
      
          ORACLE_SAFE(left, lleft + strlen(key) + 4, lleft);
          nleft += sprintf(&left[nleft], ", \"%s\"", key);
    
          ORACLE_SAFE(right, lright + strlen(cval) + 4, lright);
          nright += sprintf(&right[nright], ", '%s'", cval);
    
          free(cval);
        } while(os_object_iter_next(o));
  
        ORACLE_SAFE(left, lleft + strlen(right) + 2, lleft);
        sprintf(&left[nleft], "%s )", right);
    
        log_debug(ZONE, "_st_oracle_put_guts: Generated SQL: %s", left);

        nResultCode = checkOCIError(drv, "oracle_put_guts: Prepare", data->ociError, OCIStmtPrepare(data->ociStatement, data->ociError, left, 
                                                                                     (ub4) strlen(left), OCI_NTV_SYNTAX, OCI_DEFAULT));
  
        if (nResultCode != 0)
        {
          free(left);
          free(right);
          return st_FAILED;
        }

        nResultCode = checkOCIError(drv, "oracle_put_guts: Execute", data->ociError, OCIStmtExecute(data->ociService, data->ociStatement, 
                                                                                     data->ociError, (ub4) 1, (ub4) 0, 
                                                                                     (CONST OCISnapshot *) NULL, (OCISnapshot *) NULL, 
                                                                                     OCI_DEFAULT | OCI_COMMIT_ON_SUCCESS));

        if (nResultCode != 0)
        {
          free(left);
          free(right);
          return st_FAILED;
        }
      }
    } while(os_iter_next(os));
  }

  free(left);
  free(right);

  return st_SUCCESS;
}

static st_ret_t _st_oracle_put(st_driver_t drv, char *type, char *owner, os_t os)
{

  if( !owner ) {
    log_debug(ZONE,"_st_oracle_put: owner is null");
    return st_FAILED;
  }
  
  if(os_count(os) == 0)
  {
    return st_SUCCESS;
  }

  if(oracle_ping(drv) != 0)
  {
    log_write(drv->st->sm->log, LOG_ERR, "oracle: connection to database lost");
    return st_FAILED;
  }

  if(_st_oracle_put_guts(drv, type, owner, os) != st_SUCCESS)
  {
    return st_FAILED;
  }

  return st_SUCCESS;
}

static st_ret_t _st_oracle_get(st_driver_t drv, char *a_szType, char *owner, char *filter, os_t *os)
{
  OracleDriverPointer data = (OracleDriverPointer) drv->private;
  os_object_t o;
  os_type_t ot;
  char szBuffer[128];
  char *szWhereClause = NULL;
  char *szQuery = NULL;
  int nQueryLength = 0;
  int nResultCode = 0;
  int nNumberOfFields = 0;
  int nIndex = 0;

  if( !owner ) {
    log_debug(ZONE,"_st_oracle_get: owner is null");
    return st_FAILED;
  }
  
  if(oracle_ping(drv) != 0)
  {
    log_write(drv->st->sm->log, LOG_ERR, "_st_oracle_get: Connection to database lost!");
    return st_FAILED;
  }

  if(data->prefix != NULL)
  {
    snprintf(szBuffer, sizeof(szBuffer), "%s%s", data->prefix, a_szType);
    a_szType = szBuffer;
  }

  szWhereClause = _st_oracle_convert_filter(drv, owner, filter);
  log_debug(ZONE, "_st_oracle_get: Generated Filter: %s", szWhereClause);

  ORACLE_SAFE(szQuery, strlen(a_szType) + strlen(szWhereClause) + 50, nQueryLength);
  sprintf(szQuery, "SELECT * FROM \"%s\" WHERE %s ORDER BY \"object-sequence\"", a_szType, szWhereClause);
  free(szWhereClause);

  log_debug(ZONE, "_st_oracle_get: Prepared SQL: %s", szQuery);

  nResultCode = checkOCIError(drv, "_st_oracle_get: Prepare Statement", data->ociError, OCIStmtPrepare(data->ociStatement, data->ociError,
                                                                                        szQuery, (ub4)strlen(szQuery), OCI_NTV_SYNTAX,
                                                                                        OCI_DEFAULT));

  if (nResultCode != 0)
  {
    free(szQuery);
    return st_FAILED;
  }

  nResultCode = checkOCIError(drv, "_st_oracle_get: Statement Describe", data->ociError, OCIStmtExecute(data->ociService,
                                                                                         data->ociStatement, data->ociError, (ub4)0,
                                                                                         (ub4)0, (CONST OCISnapshot *)NULL,
                                                                                         (OCISnapshot *)NULL, OCI_DESCRIBE_ONLY));

  if (nResultCode != 0)
  {
    free(szQuery);
    return st_FAILED;
  }

  free(szQuery);

  nResultCode = OCI_SUCCESS;

  checkOCIError(drv, "_st_oracle_get: Get Field Count", data->ociError, OCIAttrGet(data->ociStatement, OCI_HTYPE_STMT,
                                                                        (dvoid *)&nNumberOfFields, (ub4 *)NULL, OCI_ATTR_PARAM_COUNT,
                                                                        data->ociError));
  if (nNumberOfFields == 0)
  {
    return st_NOTFOUND;
  }
  else
  {
    /*
     * TODO: Handle memory better. 
     * The DDL for the "vcard" table has 21 fields. The following implementation allocates 82K for 21 fields.
     */
    OCIDefine *arrFields[nNumberOfFields];
    char arrszFieldData[nNumberOfFields][4001]; /* Size each field for the maximum VARCHAR2 size + terminating null */
    char arrszFieldName[nNumberOfFields][255];
    ub2 arrnFieldType[nNumberOfFields];
    sb2 arrnFieldIndicator[nNumberOfFields];
    int arrnFieldSize[nNumberOfFields];
    char *svFieldName;
    int nNameSize;
    int nIntValue;
    nad_t nad;


    for (nIndex = 0; nIndex < nNumberOfFields; nIndex++)
    {
      arrFields[nIndex] = NULL;

      checkOCIError(drv, "_st_oracle_get: Get Parameter", data->ociError, OCIParamGet(data->ociStatement, OCI_HTYPE_STMT, data->ociError,
                                                                          (dvoid **) &arrFields[nIndex], (ub4) (nIndex + 1)));

      checkOCIError(drv, "_st_oracle_get: Get Field Name", data->ociError, OCIAttrGet(arrFields[nIndex], OCI_DTYPE_PARAM,
                                                                           (dvoid *) &svFieldName, &nNameSize,
                                                                           OCI_ATTR_NAME, data->ociError));
      strncpy(arrszFieldName[nIndex], svFieldName, nNameSize);
      arrszFieldName[nIndex][nNameSize] = '\0';
      
      arrnFieldType[nIndex] = 0;
      checkOCIError(drv, "_st_oracle_get: Get Field Type", data->ociError, OCIAttrGet(arrFields[nIndex], OCI_DTYPE_PARAM,
                                                                           (dvoid *) &arrnFieldType[nIndex], (ub4 *) NULL,
                                                                           (ub4) OCI_ATTR_DATA_TYPE, data->ociError));

      checkOCIError(drv, "_st_oracle_get: Get Field Size", data->ociError, OCIAttrGet(arrFields[nIndex], OCI_DTYPE_PARAM,
                                                                           (dvoid *) &arrnFieldSize[nIndex], (ub4 *) NULL,
                                                                           (ub4) OCI_ATTR_DATA_SIZE, data->ociError));
      if (arrnFieldSize[nIndex] > 4000 || arrnFieldSize[nIndex] < 1)
      {
      	arrnFieldSize[nIndex] = 4000;
      }

      checkOCIError(drv, "_st_oracle_get: Define String", data->ociError, OCIDefineByPos(data->ociStatement, &arrFields[nIndex],
                                                                          data->ociError, (nIndex + 1), (dvoid *)&arrszFieldData[nIndex],
                                                                          4000, SQLT_STR, &arrnFieldIndicator[nIndex], 
                                                                          (ub2 *) 0, (ub2 *) 0, OCI_DEFAULT));
    }


    nResultCode = OCIStmtExecute(data->ociService, data->ociStatement, data->ociError, (ub4) 1, (ub4) 0, (CONST OCISnapshot *) NULL,
                  (OCISnapshot *) NULL, OCI_DEFAULT);

    if (nResultCode == OCI_SUCCESS || nResultCode == OCI_SUCCESS_WITH_INFO)
    {
      for (nIndex = 0; nIndex < nNumberOfFields; nIndex++)
      {
        if (arrnFieldIndicator[nIndex] == -1)
        {
          arrszFieldData[nIndex][0] = '\0';
        }
      }
    }
    else if (nResultCode != OCI_NO_DATA)
    {
      checkOCIError(drv, "_st_oracle_get: Execute Statement", data->ociError, nResultCode);
      return st_FAILED;
    }

    if (nResultCode == OCI_NO_DATA)
    {
      return st_NOTFOUND;
    }

    *os = os_new();

    while (nResultCode != OCI_NO_DATA)
    {
      o = os_object_new(*os);

      for (nIndex = 0; nIndex < nNumberOfFields; nIndex++)
      {
        if(strcmp(arrszFieldName[nIndex], "collection-owner") == 0 || strcmp(arrszFieldName[nIndex], "object-sequence") == 0)
        {
          continue;
        }
        
        if (arrszFieldData[nIndex][0] == '\0')
        {
          continue;
        }
        
        switch(arrnFieldType[nIndex])
        {
          case SQLT_CHR:   /* VARCHAR2, VARCHAR, CHAR_VARYING, CHARACTER_VARYING, NVARCHAR2, 
                            * NCHAR_VARYING, NATIONAL_CHAR_VARYING, or NATIONAL_CHARACTER_VARYING field. */
            if (arrnFieldSize[nIndex] > 2)
            {
              log_debug(ZONE, "Field %s is Field Type SQLT_CHR of Size %d, setting os_type_STRING", arrszFieldName[nIndex], arrnFieldSize[nIndex]);
              ot = os_type_STRING;
            }
            else
            {
              log_debug(ZONE, "Field %s is Field Type SQLT_CHR of Size %d, setting os_type_BOOLEAN", arrszFieldName[nIndex], arrnFieldSize[nIndex]);
              ot = os_type_BOOLEAN;
            }
            break;

          case SQLT_AFC:   /* CHAR, CHARACTER, NATIONAL_CHAR, NATIONAL_CHARACTER, or NCHAR field. */
            if (arrnFieldSize[nIndex] > 2)
            {
              log_debug(ZONE, "Field %s is Field Type SQLT_AFC of Size %d, setting os_type_STRING", arrszFieldName[nIndex], arrnFieldSize[nIndex]);
              ot = os_type_STRING;
            }
            else
            {
              log_debug(ZONE, "Field %s is Field Type SQLT_AFC of Size %d, setting os_type_BOOLEAN", arrszFieldName[nIndex], arrnFieldSize[nIndex]);
              ot = os_type_BOOLEAN;
            }
            break;

          case SQLT_NUM:   /* INT, REAL, NUMERIC, DOUBLE_PRECISION, SMALLINT, FLOAT, DECIMAL, NUMBER, or INTEGER field. */
            log_debug(ZONE, "Field %s is Field Type SQLT_NUM of Size %d", arrszFieldName[nIndex], arrnFieldSize[nIndex]);
            ot = os_type_INTEGER;
            break;

          default:
            log_debug(ZONE, "Unknown field type %d, for column %s ignoring it", arrnFieldType[nIndex], arrszFieldName[nIndex]);
            continue;
        }
        
        switch(ot)
        {
          case os_type_BOOLEAN:
            nIntValue = (arrszFieldData[nIndex][0] == '0') ? 0 : 1;
            os_object_put(o, arrszFieldName[nIndex], &nIntValue, ot);
            break;

          case os_type_INTEGER:
            nIntValue = atoi(arrszFieldData[nIndex]);
            os_object_put(o, arrszFieldName[nIndex], &nIntValue, ot);
            break;

          case os_type_STRING:
            if(strlen(arrszFieldData[nIndex]) >= 3 && strncmp(arrszFieldData[nIndex], "NAD", 3) == 0)
            {
              if(strlen(arrszFieldData[nIndex]) == 3)
              {
                log_write(drv->st->sm->log, LOG_ERR, "Found XML cell with no XML content; table=%s, owner=%s", a_szType, owner);
                os_free(*os);
                return st_FAILED;
              }

              nad = nad_parse(drv->st->sm->router->nad_cache, &arrszFieldData[nIndex][3], strlen(arrszFieldData[nIndex]) - 3);
              if(nad == NULL)
              {
                log_write(drv->st->sm->log, LOG_ERR, "Found XML cell with unparseable XML content; table=%s, owner=%s", a_szType, owner);
		log_write(drv->st->sm->log, LOG_ERR, "Unparseable Content: %s", &arrszFieldData[nIndex][3]);
                os_free(*os);
                return st_FAILED;
              }

              os_object_put(o, arrszFieldName[nIndex], nad, os_type_NAD);
            }
            else 
	    {
              int offset;

              if(strlen(arrszFieldData[nIndex]) >= 3 && strncmp(arrszFieldData[nIndex], "STR", 3) == 0) 
	      {
                offset = 3;
              } 
              else 
              {
                offset = 0;
              }

              os_object_put(o, arrszFieldName[nIndex], arrszFieldData[nIndex + offset], os_type_STRING);
            } 
            break;

            case os_type_NAD:
              break;
          }
        }

      log_debug(ZONE, "Get Next Row.");
      nResultCode = OCIStmtFetch2(data->ociStatement, data->ociError, 1, OCI_DEFAULT, 0, OCI_DEFAULT);

      if (nResultCode == OCI_SUCCESS || nResultCode == OCI_SUCCESS_WITH_INFO)
      {
        for (nIndex = 0; nIndex < nNumberOfFields; nIndex++)
        {
          if (arrnFieldIndicator[nIndex] == -1)
          {
            arrszFieldData[nIndex][0] = '\0';
          }
        }
      }
      else if (nResultCode != OCI_NO_DATA)
      {
        checkOCIError(drv, "_st_oracle_get: Fetch Next Row", data->ociError, nResultCode);
        // If we get a database error exit the while loop. This probably should return st_FAILED here.
        break;
      }
    }
  }

  return st_SUCCESS;
}

static st_ret_t _st_oracle_delete(st_driver_t drv, char *type, char *owner, char *filter)
{
  OracleDriverPointer data = (OracleDriverPointer) drv->private;
  char *cond, *buf = NULL;
  int buflen = 0;
  int nResultCode = 0;
  char tbuf[128];

  if(oracle_ping(drv) != 0) 
  {
    log_write(drv->st->sm->log, LOG_ERR, "oracle: Connection to database lost");
    return st_FAILED;
  }

  if(data->prefix != NULL)
  {
    snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, type);
    type = tbuf;
  }

  cond = _st_oracle_convert_filter(drv, owner, filter);
  log_debug(ZONE, "oracle: Generated filter: %s", cond);

  ORACLE_SAFE(buf, strlen(type) + strlen(cond) + 19, buflen);
  sprintf(buf, "DELETE FROM \"%s\" WHERE %s", type, cond);
  free(cond);

  log_debug(ZONE, "_st_oracle_delete: Prepared SQL: %s", buf);

  nResultCode = checkOCIError(drv, "_st_oracle_delete: Prepare", data->ociError, OCIStmtPrepare(data->ociStatement, data->ociError, buf, 
                                                                                 (ub4) strlen(buf), OCI_NTV_SYNTAX, OCI_DEFAULT));
  
  if (nResultCode != 0)
  {
  	free(buf);
    return st_FAILED;
  }
  
  log_debug(ZONE, "_st_oracle_delete: Executing Delete.");

  nResultCode = checkOCIError(drv, "_st_oracle_delete: Execute", data->ociError, OCIStmtExecute(data->ociService, data->ociStatement, 
                                                                                 data->ociError, (ub4) 1, (ub4) 0, (CONST OCISnapshot *) NULL, 
                                                                                 (OCISnapshot *) NULL, OCI_DEFAULT | OCI_COMMIT_ON_SUCCESS));
  free(buf);

  log_debug(ZONE, "Result query: %d",nResultCode);
  
  if(nResultCode != 0) 
  {
    return st_FAILED;
  }
  
  return st_SUCCESS;
}

static st_ret_t _st_oracle_replace(st_driver_t drv, char *type, char *owner, char *filter, os_t os)
{
  if(oracle_ping(drv) != 0)
  {
    log_write(drv->st->sm->log, LOG_ERR, "oracle: connection to database lost");
    return st_FAILED;
  }

  if(_st_oracle_delete(drv, type, owner, filter) == st_FAILED)
  {
    return st_FAILED;
  }

  if(_st_oracle_put_guts(drv, type, owner, os) == st_FAILED)
  {
    return st_FAILED;
  }

  return st_SUCCESS;
}

static void _st_oracle_free(st_driver_t drv) {
    OracleDriverPointer data = (OracleDriverPointer) drv->private;

    OCILogoff(data->ociService, data->ociError);
    OCIHandleFree((dvoid *) data->ociStatement, OCI_HTYPE_STMT);
    OCIHandleFree((dvoid *) data->ociService, OCI_HTYPE_SVCCTX);
    OCIHandleFree((dvoid *) data->ociError, OCI_HTYPE_ERROR);
    OCIHandleFree((dvoid *) data->ociEnvironment, OCI_HTYPE_ENV);

    xhash_free(data->filters);

    free(data->prefix);

    free(data);
}

st_ret_t st_oracle_init(st_driver_t drv)
  {
    int nResultCode;
    char *svHost, *svUser, *svPass;
    OCIEnv     *ociEnvironment;
    OCIError   *ociError;
    OCISvcCtx  *ociService;
    OCIStmt    *ociStatement;

    OracleDriverPointer data;

    svHost = config_get_one(drv->st->sm->config, "storage.oracle.host", 0);
    svUser = config_get_one(drv->st->sm->config, "storage.oracle.user", 0);
    svPass = config_get_one(drv->st->sm->config, "storage.oracle.pass", 0);

    if(svHost == NULL || svUser == NULL || svPass == NULL)
    {
      log_write(drv->st->sm->log, LOG_ERR, "(st_oracle_init: ) Invalid driver config from XML file.");
      return st_FAILED;
    }

    /* Initialize OCI */
    nResultCode = OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0, (dvoid * (*)(dvoid *, size_t))0, (dvoid * (*)(dvoid *, dvoid *, size_t))0, (void (*)(dvoid *, dvoid *)) 0);

    if (nResultCode != 0)
    {
      log_write(drv->st->sm->log, LOG_ERR, "(st_oracle_init: ) Could not Initialize OCI (%d)", nResultCode);
      return st_FAILED;
    }

    /* Initialize evironment */
    nResultCode = OCIEnvInit((OCIEnv **) &ociEnvironment, OCI_DEFAULT, (size_t) 0, (dvoid **) 0);

    if (nResultCode != 0)
    {
      log_write(drv->st->sm->log, LOG_ERR, "(st_oracle_init: ) Could not Initialize OCI Environment (%d)", nResultCode);
      return st_FAILED;
    }

    /* Initialize handles */
    nResultCode = OCIHandleAlloc((dvoid *) ociEnvironment, (dvoid **) &ociError, OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0);

    if (nResultCode != 0)
    {
      log_write(drv->st->sm->log, LOG_ERR, "(st_oracle_init: ) Could not create OCI Error object (%d)" , nResultCode);
      nResultCode = OCIHandleFree((dvoid *) ociEnvironment, OCI_HTYPE_ENV);
      return st_FAILED;
    }

    nResultCode = checkOCIError(drv, "st_oracle_init: Allocate Service", ociError, OCIHandleAlloc((dvoid *) ociEnvironment,
                                                                                   (dvoid **)&ociService, OCI_HTYPE_SVCCTX,
                                                                                   (size_t)NULL, (dvoid **)NULL));
    if (nResultCode != 0)
    {
      nResultCode = OCIHandleFree((dvoid *) ociError, OCI_HTYPE_ERROR);
      nResultCode = OCIHandleFree((dvoid *) ociEnvironment, OCI_HTYPE_ENV);
      return st_FAILED;
    }

    /* Connect to database server */
    nResultCode = checkOCIError(drv, "st_oracle_init: Connect to Server", ociError, OCILogon(ociEnvironment, ociError, &ociService,
                                                                                    svUser, strlen(svUser), svPass, strlen(svPass),
                                                                                    svHost, strlen(svHost)));

    if (nResultCode != 0)
    {
      nResultCode = OCIHandleFree((dvoid *) ociService, OCI_HTYPE_SVCCTX);
      nResultCode = OCIHandleFree((dvoid *) ociError, OCI_HTYPE_ERROR);
      nResultCode = OCIHandleFree((dvoid *) ociEnvironment, OCI_HTYPE_ENV);
      return st_FAILED;
    }

    /* Allocate and prepare SQL statement */
    nResultCode = checkOCIError(drv, "st_oracle_init: Allocate Statement", ociError, OCIHandleAlloc((dvoid *)ociEnvironment,
                                                                                     (dvoid **)&ociStatement, OCI_HTYPE_STMT,
                                                                                     (size_t)NULL, (dvoid **)NULL));

    if (nResultCode != 0)
    {
      nResultCode = OCILogoff(ociService, ociError);
      nResultCode = OCIHandleFree((dvoid *) ociService, OCI_HTYPE_SVCCTX);
      nResultCode = OCIHandleFree((dvoid *) ociError, OCI_HTYPE_ERROR);
      nResultCode = OCIHandleFree((dvoid *) ociEnvironment, OCI_HTYPE_ENV);
      return st_FAILED;
    }

    data = (OracleDriverPointer) malloc(sizeof(struct OracleDriver));
    memset(data, 0, sizeof(struct OracleDriver));

    data->ociEnvironment = ociEnvironment;
    data->ociError = ociError;
    data->ociService = ociService;
    data->ociStatement = ociStatement;
    data->ociDefine = NULL;
    data->ociBind = NULL;
    data->svUser = svUser;
    data->svPass = svPass;

    data->filters = xhash_new(17);

    data->prefix = config_get_one(drv->st->sm->config, "storage.oracle.prefix", 0);

    drv->private = (void *) data;

    drv->add_type = _st_oracle_add_type;
    drv->put = _st_oracle_put;
    drv->get = _st_oracle_get;
    drv->delete = _st_oracle_delete;
    drv->replace = _st_oracle_replace;
    drv->free = _st_oracle_free;

    return st_SUCCESS;
  }

#endif