diff -urpN drupal/includes/database.oracle.inc drupal-6.x-dev-with-oracle-5.4/includes/database.oracle.inc
--- drupal/includes/database.oracle.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-with-oracle-5.4/includes/database.oracle.inc	2007-06-04 01:43:42.000000000 +0800
@@ -0,0 +1,1699 @@
+<?php
+// $ID$
+
+/**
+ * @file
+ * Database interface code for Oracle database servers.
+ */
+
+/**
+ * @ingroup database
+ * @{
+ */
+
+/**
+ * Report database status.
+ */
+function db_status_report($phase) {
+  $t = get_t();
+
+  $version = db_version();
+
+  $form['oracle'] = array(
+    'title' => $t('Oracle database'),
+    'value' => $version,
+  );
+
+  return $form;
+}
+
+/**
+ * Returns the version of the database server currently in use.
+ *
+ * @return Database server version
+ */
+function db_version() {
+  return db_result(db_query('SELECT banner FROM sys.V_$VERSION'));
+}
+
+/**
+ * Initialize a database connection.
+ */
+function db_connect($url) {
+
+  // Check if Oracle support is present in PHP 4 or 5
+  if (!(function_exists('oci_connect'))) {
+    drupal_maintenance_theme();
+    drupal_set_title('PHP Oracle support not enabled');
+    print theme('maintenance_page', '<p>We were unable to use the Oracle database because the Oracle extension for PHP is not installed. Check your <code>PHP.ini</code> to see how you can enable it.</p>');
+    //create a book page about Oracle support.
+    exit;
+  }
+
+  $url = parse_url($url);
+
+  // Decode url-encoded information in the db connection string.
+  $url['user'] = urldecode($url['user']);
+  $url['pass'] = urldecode($url['pass']);
+  $url['host'] = urldecode($url['host']);
+  $url['path'] = urldecode($url['path']);
+
+  // Build oracle connection string and allow for non-standard Oracle port.
+  list($url['path'], $url['charset']) = explode('/', substr($url['path'], 1));
+  $conn_string = '//' . $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '') . '/' . $url['path'];
+
+  $connection = isset($url['charset']) && $url['charset'] ?
+    @oci_connect($url['user'], $url['pass'], $conn_string, $url['charset']) :
+    @oci_connect($url['user'], $url['pass'], $conn_string);
+
+  // Test connecting to the database.
+  if (!$connection && ($error = oci_error())) {
+    drupal_maintenance_theme();
+    drupal_set_header('HTTP/1.1 503 Service Unavailable');
+    drupal_set_title('Unable to connect to database server');
+    print theme('maintenance_page', '<p>If you still have to install Drupal, proceed to the <a href="'. base_path() .'install.php">installation page</a>.</p>
+<p>If you have already finished installed Drupal, this either means that the username and password information in your <code>settings.php</code> file is incorrect or that we can\'t connect to the Oracle database server. This could mean your hosting provider\'s database server is down.</p>
+<p>The Oracle error was: '. theme('placeholder', $error['message']) .'.</p>
+<p>Currently, the username is '. theme('placeholder', $url['user']) .' and the database server is '. theme('placeholder', $url['host']) .'.</p>
+<ul>
+  <li>Are you sure you have the correct username and password?</li>
+  <li>Are you sure that you have typed the correct hostname?</li>
+  <li>Are you sure that the database server is running?</li>
+</ul>
+<p>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.</p>');
+    exit;
+  }
+
+  return $connection;
+}
+
+/**
+ * Sanitize query adding "" around Oracle SQL reserved column names.
+ */
+function _db_rewrite_reserved($part) {
+  // Hack to get around the fact that the column name might be at the front or at the end of the string
+  $part = " " . $part . " ";
+  $db_reserved_words = array ('access', 'comment', 'mode', 'session', 'uid', 'file');
+  foreach ($db_reserved_words as $word) {
+    $part = eregi_replace('([[:space:],\.\(<>\+-=!*])'.$word.'([[:space:],\.\)<>\+-=!*])','\\1"'.$word.'"\\2', $part);
+  }
+  return substr($part,1,-1);
+}
+
+/**
+ * Map all "!= ''" into "IS NOT NULL" as oracle standard
+ */
+function _db_rewrite_not_null($query) {
+  if (ereg('^(SELECT)', $query)) {
+    $replacementQuery = preg_replace("/\s*(!=\s*'').*/Dsi", " IS NOT NULL ", $query);
+    $query = $replacementQuery;
+  }
+  return $query;
+}
+
+/**
+ * Rewrite SELECT query quote reserved word and handle CLOB comparison.
+ *
+ * Isolated function to rewrite select query. planned for INSERT INTO ... SELECT
+ *
+ *  @param query
+ *    SELECT query string
+ *  @param debug
+ *    debug mode ON/OFF to print message(OFF)
+ *
+ *  @return
+ *    query string with reserved words quoted
+ */
+function _db_rewrite_select($query, $debug = 0) {
+
+  global $active_db, $last_result, $_query_rc_result, $queries;
+
+  // Parsing SELECT query, because we need to manage CLOB and BLOBs in WHERE clause
+  // This is only for simple queries
+  // TODO: think of better $re to handle complex queries (JOINS)
+  if (strpos($query, 'JOIN') || strpos($query, 'ORDER') || strpos($query, 'GROUP') || strpos($query, 'LIKE') || !strpos($query, 'WHERE')) {
+    $newSQL = _db_rewrite_reserved($query);
+    return $newSQL;
+  } else {
+    $re = "/\sfrom\s*(\w+)/si";
+    preg_match($re, $query, $tmpArr);
+    $tablename = trim($tmpArr[1]);
+    if (!$tablename) {
+      $query .= " FROM DUAL";
+    }
+
+    /*
+     * Determine whether or not each column is a LOB (CLOB or BLOB) and
+     * substitute a bind variable. If the column list is not provided,
+     * we'll need to obtain it from the DB.
+     *
+     * TODO: Augment this query with some sort of caching system, so it knows
+     *       which columns are LOBs without having to ask the DB every time.
+     */
+    $col_query = "SELECT column_name, data_type, column_id
+                FROM USER_TAB_COLUMNS
+                WHERE table_name = '" . strtoupper($tablename) . "'
+                ORDER BY column_id";
+
+    $col_result = oci_parse($active_db, $col_query);
+    oci_execute($col_result , OCI_DEFAULT);
+    oci_fetch_all($col_result, $ora_cols, 0, -1, OCI_ASSOC);
+
+    for ($i=0; $i<sizeof($ora_cols["DATA_TYPE"]); $i++) {
+      if ($ora_cols["DATA_TYPE"][$i] == 'CLOB') {
+    	$CLOB_vars[] = $ora_cols["COLUMN_NAME"][$i];
+      }
+    }
+
+    $newSQL = _db_rewrite_reserved($query);
+    if (sizeof($CLOB_vars)) {
+      $CLOB_str = implode("|", $CLOB_vars);
+      $sql = str_replace("\'", chr(0), $query);
+      $sql = str_replace("''", chr(1), $sql);
+      $pattern = "/(" . $CLOB_str . ")\s*(=|!=)\s*('.*?')/si";
+      $containerArr = array();
+      preg_match_all($pattern, $sql, $containerArr);
+
+      for ($j=0; $j<sizeof($containerArr[0]); $j++) {
+        $sql = str_replace($containerArr[0][$j], " dbms_lob.instr(" . $containerArr[1][$j] . ", " . $containerArr[3][$j] . ") > 0 ", $sql);
+      }
+
+      $newSQL = str_replace(chr(1), "''", $sql);
+      $newSQL = str_replace(chr(0), "\'", $newSQL);
+      $newSQL = _db_rewrite_reserved($newSQL);
+    }
+    return $newSQL;
+
+  }
+}
+
+/**
+ * Parsing implementation.
+ *
+ * Try to handle queries without bucket '()'
+ *   JOIN:   is in FROM part,
+ *           seems can be handled in it.
+ *   GROUP:  not tested
+ *   SELECT A FROM B WHERE C ORDER BY D
+ */
+function _regexp_select_easy($query) {
+  $hasWhere = 0;
+  $hasOrder = 0;
+  $hasIn = 0;
+  $argOrder = array();
+  $regexp_select = "/^select (.*) from (.*)";
+  $i = 3;
+  if (strpos($query, " WHERE ")) {
+    $hasWhere = 1;
+    $argOrder['where'] = $i;
+    $i++;
+    $regexp_select .= " where (.*)";
+    if (strpos($query, " IN ")) {
+      $hasIn = 1;
+      $argOrder['in'] = $i;
+      $i++;
+      $regexp_select .= " in (\(.*\))";
+    }
+  }
+  if (strpos($query, " ORDER ")) {
+    $hasOrder = 1;
+    $argOrder['order'] = $i;
+    $i++;
+    $regexp_select .= " order by (.*)";
+  }
+  $regexp_select .= "/Dsi";
+  preg_match($regexp_select, $query, $matches2);
+
+  if (isset($matches2[1]) && $matches2[1]) {
+    $ret  = "SELECT "._db_rewrite_reserved($matches2[1]);
+    $ret .= " FROM "._db_rewrite_reserved($matches2[2]);
+    if ($hasWhere) {
+      $ret .= " WHERE "._db_rewrite_reserved($matches2[($argOrder['where'])]);
+    }
+    if ($hasIn) {
+      $ret .= " IN ".$matches2[($argOrder['in'])];
+    }
+    if ($hasOrder) {
+      $ret .= " ORDER BY "._db_rewrite_reserved($matches2[($argOrder['order'])]);
+    }
+  } else {
+    // not a query, just field names
+    $ret = $query;
+  }
+  return $ret;
+}
+
+/**
+ * Parsing query with recursive function.
+ * Break bucket until no bucket.
+ * At the same time do complete parsing to remaining SQL segment.
+ */
+function _regexp_select_recur($query) {
+  $regexp_bucket = "/\((.*)\)/Dsi";
+  preg_match($regexp_bucket, $query, $matches);
+  if (isset($matches[1]) && $matches[1]) {
+    $return_str = _regexp_select_recur($matches[1]);
+
+    // Replace query in bucket with some string
+    // Prevent re-parsing
+    $new_query = str_replace($matches[1],"a1", $query);
+
+    $new_query = _regexp_select_easy($new_query);
+
+    // Restore the query
+    $return_query = str_replace("a1", $return_str, $new_query);
+    return $return_query;
+  } else {
+    // End of recursive: no bucket found
+    $return_query = _regexp_select_easy($query);
+
+    return $return_query;
+  }
+}
+
+/**
+ * Helper function for db_num_rows().
+ *
+ * Store number of result row in global array, let db_num_rows() to query.
+ *
+ * NOTE: As the previous version store the entire resource into array,
+ *       in some heavy query page(?q=admin/build/menu)
+ *       it exceeds the limit of maximum cursor in Oracle.
+ *       Instead of adjusting the limit,
+ *       here just store the num_rows of each query,
+ *       instead of the resource itself
+ *
+ * NOTE: ocirowcount doesn't return the number of lines selected in a SELECT query. It
+ *       cannot be used here. Further, there is no way to implement this function cleanly
+ *       with an Oracle DB. Oracle stores its query results on the server side, therefore
+ *       the client does not have to download the entire result set when the query is
+ *       executed. However, since the client can not see the entire result set, it cant
+ *       (from within the client only) determine how many rows were selected.
+ *
+ *       Unfortunately, the cleanest possible way I could figure out was to have two
+ *       result resources for SELECT statements, one for retrieving data and the other
+ *       for counting rows. The alternate resource is set above in _db_query.
+ *
+ * @param $result
+ *   A database query result resource, as returned from OCIParse().
+ *   Just for array indexing.
+ * @param $result_rc
+ *   A database query result resource, returned from OCIParse().
+ *   The result resource to be counted.
+ * @param $mode
+ *   mode 0 : For SELECT statement.
+ *   mode x : For INSERT/UPDATE/DELETE (not used, just placed).
+ *
+ * @return
+ *   The number of saved result row.
+ */
+function _db_set_num_rows($result, $result_rc, $mode = 0) {
+  global $_query_rc_result;
+  if (!$mode) {
+    // Reset result cursor and count
+    ociexecute($result_rc, OCI_COMMIT_ON_SUCCESS);
+    // Simply use OCIFetchStatement() to get num_rows
+    $num_rows = OCIFetchStatement($result_rc, $rows);
+    $_query_rc_result[$result] = $num_rows;
+    return $num_rows;
+  } else {
+    //Just test $result with OCIROWCOUNT (this is appropriate for INSERT/UPDATE/DELETE statements)
+    $num_rows = ocirowcount($result_rc);
+    $_query_rc_result[$result] = $num_rows;
+    return $num_rows;
+  }
+}
+
+/**
+ * Helper function for db_query().
+ * A wrapper for all optimze functions.
+ */
+function _db_optimize_sql($query) {
+  $query = _db_rewrite_not_null($query);
+
+  return $query;
+}
+
+/**
+ * Runs a basic query in the active database.
+ *
+ * User-supplied arguments to the query should be passed in as separate
+ * parameters so that they can be properly escaped to avoid SQL injection
+ * attacks.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. Instead of a variable number of query arguments,
+ *   you may also pass a single array containing the query arguments.
+ *
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @return
+ *   A database query result resource, or FALSE if the query was not
+ *   executed correctly.
+ */
+function db_query($query) {
+  $args = func_get_args();
+  array_shift($args);
+  $query = db_prefix_tables($query);
+
+  // Optimize query according to Oracle limitation
+  $query = _db_optimize_sql($query);
+
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_query($query);
+}
+
+/**
+ * Helper function for db_query().
+ */
+function _db_query($query, $debug = 0) {
+  global $active_db, $last_result, $_query_rc_result, $queries, $counterS;
+
+  $counterS++;
+  $dbug = 0;
+  $CLOB_vars = array();
+
+  if (variable_get('dev_query', 0)) {
+    list($usec, $sec) = explode(' ', microtime());
+    $timer = (float)$usec + (float)$sec;
+  }
+
+  if (!isset($_query_rc_result)) {
+    $_query_rc_result = array();
+  }
+
+  // Rewrite all INSERT/UPDATE statements to check and handle LOBs since Oracle
+  // cannot handle them as just strings of text delimited by single quote
+  // marks (') once they go over 4000 charecters (common in Drupal).
+  if (eregi('^(INSERT INTO )', $query)) {
+    // Split statement into three groups - the table name, the field list
+    // (if provided) and "VALUES" and the value list. Then, further split
+    // the column list and value/ list into arrays.
+    //
+    // TODO: do data type validation between INSERT fields and SELECT fields
+    // TODO: Handle "INSERT INTO .. SELECT .. " query,
+    //       e.g. CCK: content_admin.inc
+
+    $re = "/[\s)]values\s*(\(.*\))/si";
+    preg_match($re, $query, $tmpArr);
+    if (!isset($tmpArr[0]) || !trim($tmpArr[0])) {
+      //drupal_set_message("The INSERT query does not contain VALUES, function not yet DONE, checking is skipped");
+      $re = "/^insert into ([a-z0-9_\"]+)(?:\s*\((.*)\))? (select .*)$/Dsi";
+      preg_match($re, $query, $parts);
+
+      $tablename = _db_rewrite_reserved($parts[1]);
+      $col_list = _db_rewrite_reserved($parts[2]);
+      $val_list = _db_rewrite_select($parts[3]);
+
+      $query = "INSERT INTO " . $tablename . " (" . $col_list . ") " . $val_list;
+      $result = OCIParse($active_db, $query);
+      OCIExecute($result, OCI_DEFAULT) or die($query);
+      OCICommit($active_db);
+      return true;
+    }
+
+    // Break INSERT statement into parts
+    $re = "/^insert into ([a-zA-Z0-9_\"]+)\s+\((.*)\)\s+values\s*\((.*)\)/Dsi";
+
+    preg_match($re, $query, $parts);
+    if (isset($parts[3]) && count($parts[3])) {
+      $tablename = _db_rewrite_reserved($parts[1]);
+      $col_list = _db_rewrite_reserved($parts[2]);
+      $val_list = $parts[3];
+      $col_arr = explode(",",$col_list); //the column list is strictly delimited by ","
+      $hasColName = 1;
+    } else {
+      $re = "/^insert into ([a-zA-Z0-9_\"]+)\s+values\s*\((.*)\)/Dsi";
+      preg_match($re, $query, $parts);
+      $tablename = _db_rewrite_reserved($parts[1]);
+      $val_list = $parts[2];
+      $hasColName  = 0;
+    }
+
+    //However, the value list is not strictly delimited by ",", use a regex to break it up
+    $re="/,(?=(?:[^']*'[^']*')*(?![^']*'))/";
+    $results=preg_split($re, trim($val_list));
+    $val_arr = preg_replace("/^\"(.*)\"$/","$1", $results);
+
+    /*
+     * Determine whether or not each column is a LOB (CLOB or BLOB) and
+     * substitute a bind variable. If the column list is not provided,
+     * we'll need to obtain it from the DB.
+     *
+     * TODO: Augment this query with some sort of caching system, so it knows
+     *       which columns are LOBs without having to ask the DB every time.
+     */
+    $col_query = "select column_name, data_type, column_id
+                from USER_TAB_COLUMNS
+                where table_name = '" . strtoupper($tablename) . "'
+                order by column_id";
+    $col_result = ociparse($active_db, $col_query);
+    OCIExecute($col_result , OCI_DEFAULT);
+    ocifetchstatement($col_result, $ora_cols, 0, -1, OCI_ASSOC);
+
+    // Check + Build $col_arr
+    if (! is_array($col_arr)) {
+      //populate $col_list
+      $col_arr = $ora_cols['COLUMN_NAME'];
+      for($i=0; $i < count($col_arr);$i++) {
+        // If column name is in lower case, it needs to have "" around it since its a reserved word
+        if ($col_arr[$i] == strtolower($col_arr[$i])){
+          $col_arr[$i] = "\"" . $col_arr[$i] . "\"";
+        }
+      }
+      array_change_key_case($col_arr, CASE_LOWER);
+    }
+
+    //Clean $col_arr and $val_arr
+    foreach ($col_arr as $key => $value) {
+      $col_arr[$key] = trim($value);
+    }
+    foreach ($val_arr as $key => $value) {
+      $val_arr[$key] = trim($value);
+    }
+
+    // $lob_cols[n] => [0] Column Name, [1] Value to be stored
+    $lob_cols = array();
+
+    // Check for CLOB/BLOB
+    for($i=0; $i < count($col_arr);$i++) {
+      $query_col = trim(str_replace("\"", "", strtolower($col_arr[$i])));
+      for($j=0; $j < count($ora_cols['COLUMN_NAME']);$j++) {
+        $ora_col = trim(strtolower($ora_cols['COLUMN_NAME'][$j]));
+        if ($query_col == $ora_col) {
+          if ($ora_cols['DATA_TYPE'][$j] == "CLOB" || $ora_cols['DATA_TYPE'][$j] == "BLOB") {
+            // Replace that value with a bind variable if the data is not ''
+            if ($val_arr[$i] != "''") {
+              $lob_cols[] = array($col_arr[$i], $val_arr[$i]);
+            }
+            $val_arr[$i] = "EMPTY_" . $ora_cols['DATA_TYPE'][$j] . "()";
+          }
+          break;
+        }
+      }
+    }
+    //Rebuild Query
+    $newquery  = "INSERT INTO " . $tablename;
+    if ($hasColName) {
+      $newquery .= " (" . implode(",", $col_arr) . ")";
+    }
+    $newquery .= " VALUES (" .  implode(",", $val_arr) . ")";
+
+    //Build RETURNING string
+    if (count($lob_cols) > 0) {
+      $lobcollist = null;
+      $bindvarlist = null;
+      for($i=0;$i < count($lob_cols);$i++) {
+        $lobcollist .=  "," . $lob_cols[$i][0];
+        $bindvarlist .= "," . ":" . $lob_cols[$i][0];
+      }
+      $lobcollist = substr($lobcollist,1);
+      $bindvarlist = substr($bindvarlist,1);
+      $newquery .= " RETURNING " . $lobcollist . " INTO " . $bindvarlist;
+    }
+
+    $result = OCIParse($active_db, $newquery);
+    $clobs = array();
+
+    //Create bind variables
+    if (count($lob_cols) > 0) {
+      for($i=0;$i < count($lob_cols);$i++) {
+        $clob = ocinewdescriptor($active_db, OCI_D_LOB);
+        ocibindbyname($result, ':' . $lob_cols[$i][0], $clob, -1, OCI_B_CLOB);
+        $clobs[] = $clob;
+      }
+    }
+
+    OCIExecute($result, OCI_DEFAULT) or die($newquery);
+
+    //Bind CLOB data
+    if (count($lob_cols) > 0) {
+      for($i=0;$i < count($lob_cols);$i++) {
+        $clob = $clobs[$i];
+        $data = $lob_cols[$i][1];
+        //Strip ' from the beginning and end of the string (since they'll be
+        //  interpreted literally and not as string encapsulators)
+        if (substr($data,0,1) == "'" && substr($data,-1,1) == "'") {
+          $data = substr($data,1,-1);
+
+          // Remove duplicated quote for further steps
+          $tmp_str = str_replace("''", "'", $data);
+          $data = $tmp_str;
+        }
+        // In order to update the table, a row must have been found in the where clause!
+        if (ocirowcount($result) > 0) {
+          $clob->save($data);
+        }
+      }
+    }
+
+    OCICommit($active_db);
+
+    //Free CLOBs
+    if (count($lob_cols) > 0) {
+      for($i=0;$i < count($lob_cols);$i++) {
+        $clob = $clobs[$i];
+        $clob->free();
+      }
+    }
+
+  } // end (eregi('^(INSERT INTO )', $query))
+  else if (eregi('^(UPDATE )', $query)) {
+    //Rewrite UPDATE statement
+
+    /*
+     * Split statement into three groups - the table name, the field and
+     * value list, and the WHERE clause (if provided). Then, further split
+     * the column list and value list into arrays.
+     */
+    $re = "/update\\s*([a-z0-9_\"]+)\\s*set\\s*(.*)\\s*where\\s*(.*)/si";
+    preg_match($re, $query, $parts);
+    $tablename = _db_rewrite_reserved($parts[1]);
+    $item_list = $parts[2];
+    $whereclause = _db_rewrite_reserved($parts[3]);
+
+    $re="/,(?=(?:[^']*'[^']*')*(?![^']*'))/";
+    $results=preg_split($re, trim($item_list));
+    $item_arr = preg_replace("/^\"(.*)\"$/","$1", $results);
+
+    //Split item array into column and value arrays
+    $col_arr = array();
+    $val_arr = array();
+
+    for($i=0; $i < count($item_arr);$i++) {
+      $pos = strpos($item_arr[$i], "=");
+      $col_arr[] = trim(substr($item_arr[$i],0,$pos-1));
+      $val_arr[] = trim(substr($item_arr[$i],$pos+1));
+    }
+
+    $col_arr = explode(",",_db_rewrite_reserved(implode(",", $col_arr)));
+
+    /*
+     * Determine whether or not each column is a LOB (CLOB or BLOB) and
+     * substitute a bind variable. If the column list is not provided,
+     * we'll need to obtain it from the DB.
+     *
+     * TODO: Augment this query with some sort of caching system, so it knows
+     *       which columns are LOBs without having to ask the DB every time.
+     */
+    $col_query = "select column_name, data_type, column_id
+                from USER_TAB_COLUMNS
+                where table_name = '" . strtoupper($tablename) . "'
+                order by column_id";
+    $col_result = OCIParse($active_db, $col_query);
+    OCIExecute($col_result , OCI_DEFAULT);
+    ocifetchstatement($col_result, $ora_cols, 0, -1, OCI_ASSOC);
+
+    // Check + Build $col_arr
+    if (! is_array($col_arr)) {
+      //populate $col_list
+      $col_arr = $ora_cols['COLUMN_NAME'];
+      for($i=0; $i < count($col_arr);$i++) {
+        // If column name is in lower case, it needs to have "" around it since its a reserved word
+        if ($col_arr[$i] == strtolower($col_arr[$i])){
+          $col_arr[$i] = "\"" . $col_arr[$i] . "\"";
+        }
+      }
+      array_change_key_case($col_arr, CASE_LOWER);
+    }
+
+    //Clean $col_arr and $val_arr
+    foreach ($col_arr as $key => $value) {
+      $col_arr[$key] = trim($value);
+    }
+    foreach ($val_arr as $key => $value) {
+      $val_arr[$key] = trim($value);
+    }
+
+    //array of arrays, the items below are defined as...
+    // $lob_cols[n] => [0] Column Name, [1] Value to be stored
+    $lob_cols = array();
+
+    // Check for CLOB/BLOB
+    for($i=0; $i < count($col_arr);$i++) {
+      $query_col = trim(str_replace("\"", "", strtolower($col_arr[$i])));
+      for($j=0; $j < count($ora_cols['COLUMN_NAME']);$j++) {
+        $ora_col = trim(strtolower($ora_cols['COLUMN_NAME'][$j]));
+        if ($query_col == $ora_col) {
+          if ($ora_cols['DATA_TYPE'][$j] == "CLOB" || $ora_cols['DATA_TYPE'][$j] == "BLOB") {
+            // Replace that value with a bind variable if the data is not ''
+            if ($val_arr[$i] != "''") {
+              $lob_cols[] = array($col_arr[$i], $val_arr[$i]);
+            }
+            $val_arr[$i] = "EMPTY_" . $ora_cols['DATA_TYPE'][$j] . "()";
+          }
+          break;
+        }
+      }
+    }
+
+    //Rebuild Query
+    $newquery = "UPDATE " . $tablename . " SET ";
+    for($i=0; $i < count($col_arr); $i++) {
+      $newquery .= $col_arr[$i] . " = " . $val_arr[$i];
+      if ($i < (count($col_arr)-1)) $newquery .= ", ";
+    }
+    if ($whereclause != "") {
+      $newquery .= " where " . $whereclause;
+    }
+
+    //Build RETURNING string
+    if (count($lob_cols) > 0) {
+      $lobcollist = null;
+      $bindvarlist = null;
+      for($i=0;$i < count($lob_cols);$i++) {
+        $lobcollist .=  "," . $lob_cols[$i][0];
+        $bindvarlist .= "," . ":" . $lob_cols[$i][0];
+      }
+      $lobcollist = substr($lobcollist,1);
+      $bindvarlist = substr($bindvarlist,1);
+      $newquery .= " RETURNING " . $lobcollist . " INTO " . $bindvarlist;
+    }
+
+    $result = OCIParse($active_db, $newquery);
+    $clobs = array();
+
+    //Create bind variables
+    if (count($lob_cols) > 0) {
+      for($i=0;$i < count($lob_cols);$i++) {
+        $clob = ocinewdescriptor($active_db, OCI_D_LOB);
+        ocibindbyname($result, ':' . $lob_cols[$i][0], $clob, -1, OCI_B_CLOB);
+        $clobs[] = $clob;
+      }
+    }
+    OCIExecute($result, OCI_DEFAULT) or die($newquery);
+
+    //Bind CLOB data
+    if (count($lob_cols) > 0) {
+      for($i=0;$i < count($lob_cols);$i++) {
+        $clob = $clobs[$i];
+        $data = $lob_cols[$i][1];
+        //Strip ' from the beginning and end of the string (since they'll be
+        //  interpreted literally and not as string encapsulators)
+        if (substr($data,0,1) == "'" && substr($data,-1,1) == "'") {
+          $data = substr($data,1,-1);
+
+          // Remove duplicated quote for further steps
+          $tmp_str = str_replace("''", "'", $data);
+          $data = $tmp_str;
+        }
+
+        // In order to update the table, a row must have been found in the where clause!
+        if (ocirowcount($result) > 0) {
+          $clob->save($data);
+        }
+      }
+    }
+
+    OCICommit($active_db);
+
+    //Free CLOBs
+    if (count($lob_cols) > 0) {
+      for($i=0;$i < count($lob_cols);$i++) {
+        $clob = $clobs[$i];
+        $clob->free();
+      }
+    }
+  } // end (eregi('^(UPDATE )', $query))
+  else if (ereg('^(SELECT DISTINCT)', $query)) {
+    // Oracle cannot SELECT DISTINCT(clob_field) ...
+    $query = str_replace("DISTINCT", "", $query);
+    $query = _db_rewrite_reserved($query);
+    $result = OCIParse($active_db, $query);
+    OCIExecute($result, OCI_DEFAULT) or die($query);
+
+    //Re-execute query for when the app requests a rowcount (so it wont disturb the cursor of the real result set)
+    $result_rc = OCIParse($active_db, $query);
+    OCIExecute($result_rc, OCI_DEFAULT) or die($query);
+    _db_set_num_rows($result, $result_rc);
+  }
+  else if (ereg('^(LOCK TABLE)', $query)) {
+    // Lock Table has a keyword thats in the reserved word list, we don't want it in ""s
+    $re = "/^(lock table\s+)([a-z0-9_]+)(\\s+.*)$/si";
+    preg_match($re, $query, $parts);
+    $parts[2] = _db_rewrite_reserved($parts[2]);
+    array_shift($parts); //shift off the entire string
+    $query = implode("",$parts);
+    $result = OCIParse($active_db, $query);
+    OCIExecute($result, OCI_DEFAULT);
+  }
+  else if (ereg('^(SELECT)', $query)) {
+    //Limit double query for row count to SELECT statements
+    //echo $query . "<br/>";
+    //$query = _db_rewrite_reserved($query);
+
+    	// Parsing SELECT query, because we need to manage CLOB and BLOBs in WHERE clause
+  	// This is only for simple queries
+  	// TODO: think of better $re to handle complex queries (JOINS)
+
+  	if (strpos($query, 'JOIN') || strpos($query, 'ORDER') || strpos($query, 'GROUP') || strpos($query, 'LIKE') || !strpos($query, 'WHERE')) {
+          $query = _regexp_select_recur($query);
+            $result = OCIParse($active_db, $query);
+	    OCIExecute($result, OCI_DEFAULT);
+
+      // Re-execute query for when the app requests a rowcount (so it wont disturb the cursor of the real result set)
+	    $result_rc = OCIParse($active_db, $query);
+	    OCIExecute($result_rc, OCI_DEFAULT);
+            _db_set_num_rows($result, $result_rc);
+  	}
+  	else {
+          //$re = "/select\\s*(.*)\\s*from\\s*(.*)\\s*where\\s*(.*)/si";
+            $re = "/\sfrom\s*(\w+)/si";
+            preg_match($re, $query, $tmpArr);
+            $tablename = trim($tmpArr[1]);
+            if (!$tablename) {
+              $query .= " FROM DUAL";
+            }
+
+      /*
+	     * Determine whether or not each column is a LOB (CLOB or BLOB) and
+	     * substitute a bind variable. If the column list is not provided,
+       * we'll need to obtain it from the DB.
+       *
+       * TODO: Augment this query with some sort of caching system, so it knows
+       *       which columns are LOBs without having to ask the DB every time.
+	     */
+	    $col_query = "select column_name, data_type, column_id
+	                from USER_TAB_COLUMNS
+	                where table_name = '" . strtoupper($tablename) . "'
+	                order by column_id";
+
+	    $col_result = oci_parse($active_db, $col_query);
+	    oci_execute($col_result , OCI_DEFAULT);
+	    oci_fetch_all($col_result, $ora_cols, 0, -1, OCI_ASSOC);
+
+	    for ($i=0; $i<sizeof($ora_cols["DATA_TYPE"]); $i++) {
+	    	if ($ora_cols["DATA_TYPE"][$i] == 'CLOB') {
+	    		$CLOB_vars[] = $ora_cols["COLUMN_NAME"][$i];
+	    	}
+	    }
+
+      // Write deeper parsing for nested SELECT query
+      $newSQL = _regexp_select_recur($query);
+	    if (sizeof($CLOB_vars)) {
+        $CLOB_str = implode("|", $CLOB_vars);
+        $sql = str_replace("\'", chr(0), $query);
+
+        // Replace the escaped single quote
+        // so no need to do in regexp
+        $sql = str_replace("''", chr(1), $sql);
+
+		    $pattern = "/(" . $CLOB_str . ")\s*(=|!=)\s*('.*?')/si";
+		    $containerArr = array();
+		    preg_match_all($pattern, $sql, $containerArr);
+
+		    for ($j=0; $j<sizeof($containerArr[0]); $j++) {
+		      $sql = str_replace($containerArr[0][$j], " dbms_lob.instr(" . $containerArr[1][$j] . ", " . $containerArr[3][$j] . ") > 0 ", $sql);
+		    }
+		    $newSQL = str_replace(chr(1), "''", $sql);
+		    $newSQL = str_replace(chr(0), "\'", $newSQL);
+		    $newSQL = _db_rewrite_reserved($newSQL);
+            }
+            $result = OCIParse($active_db, $newSQL);
+            OCIExecute($result, OCI_DEFAULT);
+
+      // Re-execute query for when the app requests a rowcount (so it wont disturb the cursor of the real result set)
+	    $result_rc = OCIParse($active_db, $newSQL);
+	    OCIExecute($result_rc, OCI_DEFAULT);
+            _db_set_num_rows($result, $result_rc);
+  	}
+  }
+  else {
+    $query = _db_rewrite_reserved($query);
+    $result = OCIParse($active_db, $query);
+    OCIExecute($result, OCI_DEFAULT) or die($query);
+  }
+
+  $last_result = $result;
+
+  $error = ocierror($result);
+
+  if (variable_get('dev_query', 0)) {
+    $bt = debug_backtrace();
+    $query = $bt[2]['function'] . "\n" . $query;
+    list($usec, $sec) = explode(' ', microtime());
+    $stop = (float)$usec + (float)$sec;
+    $diff = $stop - $timer;
+    $queries[] = array($query, $diff);
+  }
+
+  if ($debug) {
+    print '<p>query: '. $query .'<br />error:'. $error['message'] .'</p>';
+  }
+
+  OCICommit($active_db);
+  if ($last_result !== FALSE) {
+    return $result;
+  }
+  else {
+    trigger_error(check_plain($error['message']."\nquery: ". $error['sqltext']), E_USER_WARNING);
+    return FALSE;
+  }
+}
+
+/**
+ * Fetch one result row from the previous query as an object.
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   An object representing the next row of the result. The attributes of this
+ *   object are the table fields selected by the query.
+ *
+ * NOTE: - Oracle returns table fields in uppercase, so we convert them into lowercase.
+ *       - Oracle will return the " marks around reserved column names - remove then or
+ *         it will break other items down the line.
+ *       - Oracle Clob are Objects, so we read their value.
+ *       - To maintain compatibility with PHP4 as well as PHP5, OCI_FETCH_OBJECT is
+ *         not used (it does not exist in PHP4), instead OCIFETCHINTO is used.
+ */
+function db_fetch_object($result) {
+  if ($result) {
+    $ret = null;
+    @ocifetchinto($result, $array_result, OCI_ASSOC + OCI_RETURN_NULLS);
+    if (is_array($array_result)) {
+      foreach ($array_result as $key => $value) {
+        $lower_key = str_replace("\"", "" ,strtolower($key));
+        $ret->$lower_key = is_object($value) ? $value->load() : $value; // Return LOB value.
+      }
+    }
+    db_unlock_tables();
+    return $ret;
+  }
+}
+
+/**
+ * Fetch one result row from the previous query as an array.
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   An associative array representing the next row of the result. The keys of
+ *   this object are the names of the table fields selected by the query, and
+ *   the values are the field values for this result row.
+ *
+ * NOTE: - Oracle returns table fields in uppercase, so we convert them into lowercase.
+ */
+function db_fetch_array($result) {
+  if ($result) {
+    $ret = null;
+    ocifetchinto($result, $array_result, OCI_ASSOC + OCI_RETURN_NULLS + OCI_RETURN_LOBS);
+    if (is_array($array_result)) {
+      $ret = array_change_key_case($array_result, CASE_LOWER);
+    }
+    db_unlock_tables();
+    return $ret;
+  }
+}
+
+/**
+ * Determine how many result rows were found by the preceding query.
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @return
+ *   The number of result rows.
+ */
+function db_num_rows($result) {
+  global $_query_rc_result;
+
+  // Just test $result with OCIROWCOUNT (this is appropriate for INSERT/UPDATE/DELETE statements)
+  $ret = $_query_rc_result[$result] ? $_query_rc_result[$result] : ocirowcount($result);
+
+  db_unlock_tables();
+  return $ret;
+}
+
+/**
+ * Return an individual result field from the previous query.
+ *
+ * Only use this function if exactly one field is being selected; otherwise,
+ * use db_fetch_object() or db_fetch_array().
+ *
+ * @param $result
+ *   A database query result resource, as returned from db_query().
+ * @param $row
+ *   The index of the row whose result is needed.
+ * @return
+ *   The resulting field.
+ */
+function db_result($result, $row = 0) {
+  // First we have to fetch the first row then we return the first column.
+  // TODO: validate the number of rows ? This would generate problems for
+  //       db_next_id query as we need to run query one time to get the number
+  //       of rows, and then run it a second time to get the result, so we will
+  //       "jumped" some id.
+  $ret = @OCIFetchInto($result, $result_row, OCI_NUM + OCI_RETURN_LOBS) ? $result_row[0] : FALSE;
+
+  db_unlock_tables();
+  return $ret;
+}
+
+/**
+ * Determine whether the previous query caused an error.
+ */
+function db_error() {
+  $error = ocierror();
+  return $error['message'];
+}
+
+/**
+ * Return a new unique ID in the given sequence.
+ *
+ * For compatibility reasons, Drupal does not use auto-numbered fields in its
+ * database tables. Instead, this function is used to return a new unique ID
+ * of the type requested. With the Oracle DB implementation, all sequences
+ * must be created at the time of Oracle scema creation.
+ */
+function db_next_id($name) {
+  global $active_db;
+
+  $name = db_prefix_tables($name);
+  // FIXME: Temporary solution for Max. 30 limitation.
+  $name = strlen($name) > 26 ? substr($name, 0, 26) : $name;
+
+  // FIXME: Cant use db_query(),
+  //        seq num additionally +2 for each call..
+  //        not know the reason
+  $stmt = OCIParse($active_db, "SELECT " . $name . "_seq.nextval FROM DUAL");
+  $result = OCIExecute($stmt, OCI_DEFAULT);
+  return db_result($stmt);
+}
+
+/**
+ * Determine the number of rows changed by the preceding query.
+ */
+function db_affected_rows() {
+  global $last_result;
+
+  $ret = ocirowcount($last_result);
+
+  db_unlock_tables();
+  return $ret;
+}
+
+/**
+ * Runs a limited-range query in the active database.
+ *
+ * Use this as a substitute for db_query() when a subset of the query is to be
+ * returned.
+ * User-supplied arguments to the query should be passed in as separate parameters
+ * so that they can be properly escaped to avoid SQL injection attacks.
+ *
+ * Note that if you need to know how many results were returned, you should do
+ * a SELECT COUNT(*) on the temporary table afterwards. db_num_rows() and
+ * db_affected_rows() do not give consistent result across different database
+ * types in this case.
+ *
+ * @param $query
+ *   A string containing an SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. The query arguments can be enclosed in one
+ *   array instead.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $from
+ *   The first result row to return.
+ * @param $count
+ *   The maximum number of result rows to return.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_range($query) {
+  $args = func_get_args();
+  $count = array_pop($args);
+  $from = array_pop($args);
+  array_shift($args);
+
+  $query = db_prefix_tables($query);
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  $query = 'SELECT * FROM (SELECT sub.*, rownum AS line FROM ('. $query .') sub) WHERE line BETWEEN '. ($from + 1) .' AND '. ($from + $count);
+  return _db_query($query);
+}
+
+/**
+ * Runs a SELECT query and stores its results in a temporary table.
+ *
+ * Use this as a substitute for db_query() when the results need to stored
+ * in a temporary table. Temporary tables exist for the duration of the page
+ * request.
+ * User-supplied arguments to the query should be passed in as separate parameters
+ * so that they can be properly escaped to avoid SQL injection attacks.
+ *
+ * Note that if you need to know how many results were returned, you should do
+ * a SELECT COUNT(*) on the temporary table afterwards. db_num_rows() and
+ * db_affected_rows() do not give consistent result across different database
+ * types in this case.
+ *
+ * @param $query
+ *   A string containing a normal SELECT SQL query.
+ * @param ...
+ *   A variable number of arguments which are substituted into the query
+ *   using printf() syntax. The query arguments can be enclosed in one
+ *   array instead.
+ *   Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose
+ *   in '') and %%.
+ *
+ *   NOTE: using this syntax will cast NULL and FALSE values to decimal 0,
+ *   and TRUE values to decimal 1.
+ *
+ * @param $table
+ *   The name of the temporary table to select into. This name will not be
+ *   prefixed as there is no risk of collision.
+ * @return
+ *   A database query result resource, or FALSE if the query was not executed
+ *   correctly.
+ */
+function db_query_temporary($query) {
+  $args = func_get_args();
+  $tablename = strtoupper(array_pop($args));
+  array_shift($args);
+
+  // Check if table exists. If so, DROP it
+  $result = db_fetch_object(db_query("SELECT table_name FROM user_tables WHERE table_name = '" . $tablename . "' "));
+  if (is_array($result) && ($result->table_name == $tablename)) {
+    db_query("DROP TABLE " . $tablename);
+  }
+
+  $query = preg_replace('/^SELECT/i', 'CREATE GLOBAL TEMPORARY TABLE '. $tablename .' ON COMMIT PRESERVE ROWS AS SELECT ', db_prefix_tables($query));
+  if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax
+    $args = $args[0];
+  }
+  _db_query_callback($args, TRUE);
+  $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query);
+  return _db_query($query);
+}
+
+/**
+ * Returns a properly formatted Binary Large OBject value.
+ *
+ * @param $data
+ *   Data to encode.
+ * @return
+ *  Encoded data.
+ */
+function db_encode_blob($data) {
+  // No processing is needed here, since LOBs in Oracle are input via LOB->Save() and
+  // dont need any escaping of charecters like \ or '
+  return "'" . db_escape_string($data) . "'";
+}
+
+/**
+ * Returns text from a Binary Large Object value.
+ *
+ * @param $data
+ *   Data to decode.
+ * @return
+ *  Decoded data.
+ */
+function db_decode_blob($data) {
+  // No processing is needed here, since LOBs in Oracle are read via LOB->Load()
+  return $data;
+}
+
+/**
+ * Prepare user input for use in a database query, preventing SQL injection attacks.
+ */
+function db_escape_string($text) {
+  // Replace any single ' with ''
+  return str_replace("'", "''", $text);
+}
+
+/**
+ * Lock a table.
+ */
+function db_lock_table($table) {
+  // FIXME: should we put a NOWAIT clause ?
+  db_query('LOCK TABLE {%s} IN EXCLUSIVE MODE', $table);
+}
+
+/**
+ * Unlock all locked tables.
+ */
+function db_unlock_tables() {
+  global $active_db;
+
+  // FIXME: Don't use db_query('COMMIT')
+  //        So can use in whatever location without dependence
+  OCICommit($active_db);
+}
+
+/**
+ * Check if a table exists.
+ */
+function db_table_exists($table) {
+  return db_num_rows(db_query("SELECT table_name FROM user_tables WHERE table_name LIKE UPPER('{" . db_escape_table($table) . "}')"));
+}
+
+/**
+ * Check if a column exists in the given table.
+ */
+function db_column_exists($table, $column) {
+  return db_num_rows(db_query("SELECT column_name FROM user_tab_cols WHERE table_name LIKE UPPER('{". db_escape_table($table) ."}') AND column_name LIKE UPPER(TRIM('$column'))"));
+}
+
+/**
+ * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to
+ * the SELECT list entry of the given query and the resulting query is returned.
+ * This function only applies the wrapper if a DISTINCT doesn't already exist in
+ * the query.
+ *
+ * @param $table Table containing the field to set as DISTINCT
+ * @param $field Field to set as DISTINCT
+ * @param $query Query to apply the wrapper to
+ * @return SQL query with the DISTINCT wrapper surrounding the given table.field.
+ */
+function db_distinct_field($table, $field, $query) {
+  $field_to_select = 'DISTINCT('. $table .'.'. $field .')';
+  // (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT).
+  return preg_replace('/(SELECT.*)(?:'. $table .'\.|\s)(?<!DISTINCT\()(?<!DISTINCT\('. $table .'\.)'. $field .'(.*FROM )/AUsi', '\1 '. $field_to_select .'\2', $query);
+}
+
+/**
+ * @} End of "ingroup database".
+ */
+
+/**
+ * @ingroup schemaapi
+ * @{
+ */
+
+/**
+ * This maps a generic data type in combination with its data size
+ * to the engine-specific data type.
+ */
+function db_type_map() {
+  // Put :normal last so it gets preserved by array_flip.  This makes
+  // it much easier for modules (such as schema.module) to map
+  // database types back into schema types.
+  $map = array(
+    'varchar:normal'  => 'VARCHAR2',
+
+    'text:tiny'       => 'VARCHAR2(4000)',
+    'text:small'      => 'VARCHAR2(4000)',
+    'text:medium'     => 'VARCHAR2(4000)',
+    'text:big'        => 'CLOB',
+    'text:normal'     => 'VARCHAR2(4000)',
+
+    'int:tiny'        => 'INTEGER',
+    'int:small'       => 'INTEGER',
+    'int:medium'      => 'INTEGER',
+    'int:big'         => 'INTEGER',
+    'int:normal'      => 'INTEGER',
+
+    'float:tiny'      => 'FLOAT',
+    'float:small'     => 'FLOAT',
+    'float:medium'    => 'FLOAT',
+    'float:big'       => 'DOUBLE PRECISION',
+    'float:normal'    => 'FLOAT',
+
+    'numeric:normal'  => 'NUMERIC',
+
+    // FIXME: Use CLOB instead of BLOB (cache)
+    'blob:big'        => 'CLOB',
+    'blob:normal'     => 'CLOB',
+
+    'datetime:normal' => 'TIMESTAMP',
+
+    'serial:tiny'     => 'INTEGER',
+    'serial:small'    => 'INTEGER',
+    'serial:medium'   => 'INTEGER',
+    'serial:big'      => 'INTEGER',
+    'serial:normal'   => 'INTEGER',
+  );
+  return $map;
+}
+
+/**
+ *  Generate SQL to create a new table from a Drupal schema definition.
+ *
+ * @param $table
+ *   A valid Drupal table definition array.
+ * @return $statements
+ *   An array of SQL statements to create the table.
+ *
+ */
+$_index_list = array();
+$_seq_trg_list = array();
+function db_create_table_sql($table) {
+  global $_index_list, $_seq_trg_list;
+  $sql_fields = array();
+  $sql_keys = array();
+  $_seq_trg_list = array();
+
+  foreach ($table['fields'] as $name => $field) {
+    $sql_fields[] = _db_create_field_sql($name, _db_process_field($field), $table['name']);
+  }
+  if (isset($table['primary key']) && is_array($table['primary key'])) {
+    $sql_keys[] = 'PRIMARY KEY ('. implode(', ', $table['primary key']) .')';
+
+    // Record indexed column
+    $_index_table_name = $table['name'];
+    $_index_keys =  str_replace(',','_',(implode(',', $table['primary key'])));
+    $_index_list[$_index_table_name][$_index_keys] = 1;
+  }
+  if (isset($table['unique keys']) && is_array($table['unique keys'])) {
+    foreach ($table['unique keys'] as $keyname => $key) {
+      $sql_keys[] = 'CONSTRAINT {'. $table['name'] .'}_'. $keyname .'_uni UNIQUE ('. implode(', ', $key) .')';
+    }
+  }
+
+  $sql  = "CREATE TABLE {". $table['name'] ."} (\n\t";
+  $sql .= implode(",\n\t", $sql_fields);
+  if (count($sql_keys) > 0) {
+    $sql .= ",\n\t";
+  }
+  $sql .= implode(",\n\t", $sql_keys);
+  $sql .= "\n)";
+  $statements[] = $sql;
+
+  if (isset($table['indexes']) && is_array($table['indexes'])) {
+    foreach ($table['indexes'] as $keyname => $key) {
+      $_index_table_name = $table['name'];
+      $_index_keys = str_replace(', ','_',(_db_create_key_sql($key)));
+      if (!isset($_index_list[$_index_table_name][$_index_keys]) || !($_index_list[$_index_table_name][$_index_keys])) {
+        $statements[] = _db_create_index_sql($table['name'], $keyname, $key);
+      }
+    }
+  }
+
+  foreach ($_seq_trg_list as $query) {
+    $statements[] = $query;
+  }
+
+  return $statements;
+}
+
+function _db_create_sequence_sql($table, $field) {
+  $seq_name  = "{" . $table . "}_" . $field;
+  // FIXME: Temporary solution for Max. 30 limitation.
+  $seq_name = strlen($seq_name) > 26 ? substr($seq_name, 0, 26) : $seq_name;
+  $seq_name .= "_seq";
+
+  $query  = "CREATE SEQUENCE ". $seq_name . " MINVALUE 1 INCREMENT BY 1 START WITH 1 NOCACHE NOORDER NOCYCLE";
+  return $query;
+}
+
+function _db_create_trigger_sql($table, $field) {
+  $trg_name = "{" . $table . "}_" . $field;
+  // FIXME: Temporary solution for Max. 30 limitation.
+  $trg_name = strlen($trg_name) > 24 ? substr($trg_name, 0, 24) : $trg_name;
+  $trg_name .= "_be_in";
+
+  $seq_name  = "{" . $table . "}_" . $field;
+  // FIXME: Temporary solution for Max. 30 limitation.
+  $seq_name = strlen($seq_name) > 26 ? substr($seq_name, 0, 26) : $seq_name;
+  $seq_name .= "_seq";
+
+  $query = <<<EOD
+    CREATE OR REPLACE TRIGGER $trg_name
+    BEFORE INSERT ON {$table}
+    FOR EACH ROW
+    BEGIN
+      IF :NEW.$field IS NULL THEN
+        SELECT $seq_name.NEXTVAL
+        INTO :NEW.$field
+        FROM DUAL;
+      END IF;
+    END;
+EOD;
+
+  return $query;
+}
+
+function _db_create_index_sql($table, $name, $fields) {
+  $idx_name  = "{" . $table . "}_" . $name;
+  // FIXME: Temporary solution for Max. 30 limitation.
+  $idx_name = strlen($idx_name) > 26 ? substr($idx_name, 0, 26) : $idx_name;
+  $idx_name .= "_idx";
+
+  $query  = 'CREATE INDEX '. $idx_name . ' ON {'. $table .'} (' . _db_create_key_sql($fields) .')';
+  return $query;
+}
+
+function _db_create_key_sql($fields) {
+  $ret = array();
+  foreach ($fields as $field) {
+    if (is_array($field)) {
+      $ret[] = 'substr('. $field[0] .', 1, '. $field[1] .')';
+    }
+    else {
+      $ret[] = $field;
+    }
+  }
+  return implode(', ', $ret);
+}
+
+/**
+ * Set database-engine specific properties for a field.
+ *
+ * @param $field
+ *   A field description array, as specified in the schema documentation.
+ */
+function _db_process_field($field) {
+  if (!isset($field['size'])) {
+    $field['size'] = 'normal';
+  }
+  // Set the correct database-engine specific datatype.
+  if (!isset($field['oracle_type'])) {
+    $map = db_type_map();
+    $field['oracle_type'] = $map[$field['type'] .':'. $field['size']];
+  }
+
+  if ($field['type'] == 'serial') {
+    unset($field['not null']);
+  }
+
+  return $field;
+}
+
+/**
+ * Create an SQL string for a field to be used in table creation or alteration.
+ *
+ * Before passing a field out of a schema definition into this function it has
+ * to be processed by _db_process_field().
+ *
+ * @param $name
+ *    Name of the field.
+ * @param $spec
+ *    The field specification, as per the schema data structure format.
+ * @param $tablename
+ *    Name of the table. Useful if required to create sequence and trigger
+ */
+function _db_create_field_sql($name, $spec, $tablename = '') {
+  global $_seq_trg_list;
+
+  $sql = $name .' '. $spec['oracle_type'];
+
+  if ($spec['type'] == 'serial') {
+    unset($spec['not null']);
+    $_seq_trg_list[] = _db_create_sequence_sql($tablename, $name);
+    $_seq_trg_list[] = _db_create_trigger_sql($tablename, $name);
+  }
+
+  if (($spec['type'] == 'text') || ($spec['type'] == 'varchar') || ($spec['type'] == 'blob')) {
+    unset($spec['not null']);
+  }
+
+  if (isset($spec['length'])) {
+    $sql .= '('. $spec['length'] .')';
+  }
+  elseif (isset($spec['precision']) && isset($spec['scale'])) {
+    $sql .= '('. $spec['scale'] .', '. $spec['precision'] .')';
+  }
+
+  if (isset($spec['default'])) {
+    $default = is_string($spec['default']) ? "'". $spec['default'] ."'" : $spec['default'];
+    $sql .= " DEFAULT $default";
+  }
+
+  if (isset($spec['not null']) && $spec['not null']) {
+    $sql .= ' NOT NULL';
+  }
+
+  return $sql;
+}
+
+/**
+ * Rename a table.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be renamed.
+ * @param $new_name
+ *   The new name for the table.
+ */
+function db_rename_table(&$ret, $table, $new_name) {
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}');
+}
+
+/**
+ * Drop a table.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be dropped.
+ */
+function db_drop_table(&$ret, $table) {
+  $ret[] = update_sql('DROP TABLE {' . $table . '} CASCADE CONSTRAINTS');
+}
+
+/**
+ * Add a new field to a table.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   Name of the table to be altered.
+ * @param $field
+ *   Name of the field to be added.
+ * @param $spec
+ *   The field specification array, as taken from a schema definition
+ */
+function db_add_field(&$ret, $table, $field, $spec) {
+  $query = 'ALTER TABLE {'. $table .'} ADD ';
+  $tmp_field_info = _db_create_field_sql($field, _db_process_field($spec));
+  $query .= $tmp_field_info['sql'];
+  $ret[] = update_sql($query);
+}
+
+/**
+ * Drop a field.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $field
+ *   The field to be dropped.
+ */
+function db_drop_field(&$ret, $table, $field) {
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP COLUMN ' . $field);
+}
+
+/**
+ * Set the default value for a field.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $field
+ *   The field to be altered.
+ * @param $default
+ *   Default value to be set. NULL for 'default NULL'.
+ */
+function db_field_set_default(&$ret, $table, $field, $default) {
+  if ($default == NULL) {
+    $default = 'NULL';
+  }
+  else {
+    $default = is_string($default) ? "'$default'" : $default;
+  }
+
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} MODIFY (' . $field . ' DEFAULT ' . $default . ')');
+}
+
+/**
+ * Set a field to have no default value.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $field
+ *   The field to be altered.
+ */
+function db_field_set_no_default(&$ret, $table, $field) {
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} MODIFY (' . $field . ' DEFAULT NULL)');
+}
+
+/**
+ * Add a primary key.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $fields
+ *   Fields for the primary key.
+ */
+function db_add_primary_key(&$ret, $table, $fields) {
+  $name = '{' . $table . '}_pk';
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} ADD CONSTRAINT ' . $name . ' PRIMARY KEY (' . implode(',', $fields) .')');
+}
+
+/**
+ * Drop the primary key.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ */
+function db_drop_primary_key(&$ret, $table) {
+  $name = '{' . $table . '}_pk';
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP CONSTRAINT ' . $name);
+}
+
+/**
+ * Add a unique key.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the key.
+ * @param $fields
+ *   An array of field names.
+ */
+function db_add_unique_key(&$ret, $table, $name, $fields) {
+  $name = '{' . $table . '}_' . $name . '_uni';
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} ADD CONSTRAINT '. $name . ' UNIQUE (' . implode(',', $fields) . ')');
+}
+
+/**
+ * Drop a unique key.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the key.
+ */
+function db_drop_unique_key(&$ret, $table, $name) {
+  $name = '{' . $table . '}_' . $name . '_uni';
+  $ret[] = update_sql('ALTER TABLE {' . $table . '} DROP CONSTRAINT '. $name);
+}
+
+/**
+ * Add an index.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the index.
+ * @param $fields
+ *   An array of field names.
+ */
+function db_add_index(&$ret, $table, $name, $fields) {
+  $_index_table_name = $table['name'];
+  $_index_keys = str_replace(', ', '_', (_db_create_key_sql($fields)));
+  if (!isset($_index_list[$_index_table_name][$_index_keys]) || !($_index_list[$_index_table_name][$_index_keys])) {
+    $ret[] = update_sql(_db_create_index_sql($table, $name, $fields));
+  }
+}
+
+/**
+ * Drop an index.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the index.
+ */
+function db_drop_index(&$ret, $table, $name) {
+  $name = '{' . $table . '}_' . $name . '_idx';
+  $ret[] = update_sql('DROP INDEX ' . $name);
+}
+
+/**
+ * Change a field definition.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   Name of the table.
+ * @param $field
+ *   Name of the field to change.
+ * @param $field_new
+ *   New name for the field (set to the same as $field if you don't want to change the name).
+ * @param $spec
+ *   The field specification for the new field.
+ */
+function db_change_field(&$ret, $table, $field, $field_new, $spec) {
+  if ($field == $field_new) {
+    $mid_field = $field . '_old';
+    $ret[] = update_sql("ALTER TABLE {" . $table . "} RENAME COLUMN " . $field . " TO " . $mid_field);
+  } else {
+    $mid_field = $field;
+  }
+
+  $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE;
+  db_add_field($ret, $table, $field_new, $spec);
+  $ret[] = update_sql("UPDATE {" . $table . "} SET " . $field_new . " = " . $mid_field);
+  if ($not_null) {
+    $ret[] = update_sql("ALTER TABLE {" . $table . "} MODIFY (" . $field_new . " NOT NULL)");
+  }
+  db_drop_field($ret, $table, $mid_field);
+}
+
+/**
+ * Update a field definition to match its schema. If the field is
+ * involved in any keys or indexes, recreate them.
+ *
+ * @param $ret
+ *   Array to which query results will be added.
+ * @param $table
+ *   Name of the table.
+ * @param $field
+ *   Name of the field to update.
+ */
+function db_update_field(&$ret, $table, $field) {
+  $spec = drupal_get_schema($table);
+
+  db_change_field($ret, $table, $field, $field, $spec['fields'][$field]);
+  if (isset($spec['primary key'])) {
+    if (array_search($field, db_field_names($spec['primary key'])) !== FALSE) {
+      db_add_primary_key($ret, $table, $spec['primary key']);
+    }
+  }
+  if (isset($spec['unique keys'])) {
+    foreach ($spec['unique keys'] as $name => $fields) {
+      if (array_search($field, db_field_names($fields)) !== FALSE) {
+        db_add_unique_key($ret, $table, $fields);
+      }
+    }
+  }
+  if (isset($spec['indexes'])) {
+    foreach ($spec['indexes'] as $name => $fields) {
+      if (array_search($field, db_field_names($fields)) !== FALSE) {
+        db_add_index($ret, $table, $fields);
+      }
+    }
+  }
+}
+
+/**
+ * @} End of "ingroup schemaapi".
+ */
+
diff -urpN drupal/includes/install.inc drupal-6.x-dev-with-oracle-5.4/includes/install.inc
--- drupal/includes/install.inc	2007-05-21 18:56:05.000000000 +0800
+++ drupal-6.x-dev-with-oracle-5.4/includes/install.inc	2007-05-30 23:57:05.000000000 +0800
@@ -144,7 +144,7 @@ function drupal_detect_baseurl($file = '
 function drupal_detect_database_types() {
   $databases = array();
 
-  foreach (array('mysql', 'mysqli', 'pgsql') as $type) {
+  foreach (array('mysql', 'mysqli', 'pgsql', 'oracle') as $type) {
     if (file_exists('./includes/install.'. $type .'.inc')) {
       include_once './includes/install.'. $type .'.inc';
       $function = $type .'_is_available';
diff -urpN drupal/includes/install.oracle.inc drupal-6.x-dev-with-oracle-5.4/includes/install.oracle.inc
--- drupal/includes/install.oracle.inc	1970-01-01 08:00:00.000000000 +0800
+++ drupal-6.x-dev-with-oracle-5.4/includes/install.oracle.inc	2007-06-04 02:45:46.000000000 +0800
@@ -0,0 +1,187 @@
+<?php
+// $Id$
+
+// Oracle specific install functions
+
+/**
+ * Check if Oracle is available.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function oracle_is_available() {
+  return function_exists('oci_connect');
+}
+
+/**
+ * Check if we can connect to Oracle.
+ *
+ * @return
+ *  TRUE/FALSE
+ */
+function drupal_test_oracle($url, &$success) {
+  if (!oracle_is_available()) {
+    drupal_set_message(st('PHP OCI8 support not enabled.'), 'error');
+    return FALSE;
+  }
+
+  $url = parse_url($url);
+
+  // Decode url-encoded information in the db connection string.
+  $url['user'] = urldecode($url['user']);
+  $url['pass'] = urldecode($url['pass']);
+  $url['host'] = urldecode($url['host']);
+  $url['path'] = urldecode($url['path']);
+
+  // Build oracle connection string and allow for non-standard Oracle port.
+  list($url['path'], $url['charset']) = explode('/', substr($url['path'], 1));
+  $conn_string = '//' . $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '') . '/' . $url['path'];
+
+  // Test connecting to the database.
+  $connection = isset($url['charset']) && $url['charset'] ?
+    @oci_connect($url['user'], $url['pass'], $conn_string, $url['charset']) :
+    @oci_connect($url['user'], $url['pass'], $conn_string);
+  if (!$connection && ($error = oci_error())) {
+    drupal_set_message(st('Failure to connect to your Oracle database server. Oracle reports the following message: %error.<ul><li>Are you sure you have the correct username and password?</li><li>Are you sure that you have typed the correct database hostname?</li><li>Are you sure that the database server is running?</li><li>Are you sure you typed the correct database name?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.<br />Error: ' . $error, array('%error' => 'Connection failed. See log file for failure reason')), 'error');
+    return FALSE;
+  }
+
+  $success = array('CONNECT');
+
+  // Test CREATE.
+  $query = 'CREATE TABLE drupal_install_test (id integer NOT NULL)';
+  $stmt = @OCIParse($connection, $query);
+  if (!$stmt && ($error = oci_error($connection))) {
+    drupal_set_message(st('We were unable to create a test table on your Oracle database server with the command %query. Oracle reports the following message: %error.<ul><li>Are you sure the configured username has the necessary Oracle permissions to create tables in the database?</li></ul>For more help, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.', array('%query' => $query, '%error' => $error)), 'error');
+    return FALSE;
+  }
+  $result = @OCIExecute($stmt);
+  $err = FALSE;
+  $success[] = 'SELECT';
+  $success[] = 'CREATE';
+
+  // Test INSERT.
+  $query = 'INSERT INTO drupal_install_test (id) VALUES (1)';
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to insert a value into a test table on your Oracle database server. We tried inserting a value with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'INSERT';
+  }
+
+  // Test UPDATE.
+  $query = 'UPDATE drupal_install_test SET id = 2';
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to update a value in a test table on your Oracle database server. We tried updating a value with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UPDATE';
+  }
+
+  // Test LOCK.
+  $query = 'LOCK TABLE drupal_install_test IN EXCLUSIVE MODE';
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to lock a test table on your Oracle database server. We tried locking a table with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'LOCK';
+  }
+
+  // Test UNLOCK, which is done automatically upon transaction end in Oracle
+  $query = 'COMMIT';
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to unlock a test table on your Oracle database server. We tried unlocking a table with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'UNLOCK';
+  }
+
+  // Test DELETE.
+  $query = 'DELETE FROM drupal_install_test';
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to delete a value from a test table on your Oracle database server. We tried deleting a value with the command %query and Oracle reported the following error: %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DELETE';
+  }
+
+  // Test DROP.
+  $query = 'DROP TABLE drupal_install_test';
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to drop a test table from your Oracle database server. We tried dropping a table with the command %query and Oracle reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+  else {
+    $success[] = 'DROP';
+  }
+
+  // CREATE FUNCTION from_unixtime()
+  $query = <<<EOD
+    CREATE OR REPLACE FUNCTION from_unixtime(sttTime number)
+    RETURN date IS
+      BEGIN
+        RETURN to_date(19700101, 'yyyymmdd') + sttTime/24/60/60;
+      END;
+EOD;
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to create function from_unixtime() for your Oracle database server. We tried creating function with the command %query and Oracle reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+
+  // CREATE FUNCTION to_unixtime()
+  $query = <<<EOD
+    CREATE OR REPLACE FUNCTION to_unixtime(oracleDate date)
+    RETURN number IS
+    BEGIN
+      RETURN (oracleDate - to_date(19700101, 'yyyymmdd'))*24*60*60;
+    END;
+EOD;
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to create function to_unixtime() for your Oracle database server. We tried creating function with the command %query and Oracle reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+
+  // CREATE FUNCTION POW()
+  $query = <<<EOD
+    CREATE OR REPLACE FUNCTION POW(base_num IN number, pow_num IN number)
+    RETURN number IS
+    BEGIN
+      RETURN POWER(base_num, pow_num);
+    END;
+EOD;
+  $stmt = @OCIParse($connection, $query);
+  $result = @OCIExecute($stmt);
+  if ($error = oci_error($stmt)) {
+    drupal_set_message(st('We were unable to create function POW() for your Oracle database server. We tried creating function with the command %query and Oracle reported the following error %error.', array('%query' => $query, '%error' => $error)), 'error');
+    $err = TRUE;
+  }
+
+  if ($err) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+?>
