Просмотр исходного кода

generalize OCI handling of queries and results

El RIDO 4 лет назад
Родитель
Сommit
2182cdd44f
4 измененных файлов с 100 добавлено и 156 удалено
  1. 6 5
      CHANGELOG.md
  2. 1 0
      CREDITS.md
  3. 81 151
      lib/Data/Database.php
  4. 12 0
      tst/Data/DatabaseTest.php

+ 6 - 5
CHANGELOG.md

@@ -7,6 +7,7 @@
     * ADDED: Opt-out of federated learning of cohorts (FLoC) (#776)
     * ADDED: Configuration option to exempt IPs from the rate-limiter (#787)
     * ADDED: Google Cloud Storage backend support (#795)
+    * ADDED: Oracle database support (#868)
     * CHANGED: Language selection cookie only transmitted over HTTPS (#472)
     * CHANGED: Upgrading libraries to: random_compat 2.0.20
     * CHANGED: Removed automatic `.ini` configuration file migration (#808)
@@ -65,7 +66,7 @@
     * CHANGED: Upgrading libraries to: DOMpurify 2.0.1
     * FIXED: Enabling browsers without WASM to create pastes and read uncompressed ones (#454)
     * FIXED: Cloning related issues (#489, #491, #493, #494)
-    * FIXED: Enable file operation only when editing (#497) 
+    * FIXED: Enable file operation only when editing (#497)
     * FIXED: Clicking 'New' on a previously submitted paste does not blank address bar (#354)
     * FIXED: Clear address bar when create new paste from existing paste (#479)
     * FIXED: Discussion section not hiding when new/clone paste is clicked on (#484)
@@ -228,7 +229,7 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u
     * FIXED: 2 minor corrections to avoid notices in php log.
     * FIXED: Sources converted to UTF-8.
   * **Alpha 0.14 (2012-04-20):**
-    * ADDED: GD presence is checked. 
+    * ADDED: GD presence is checked.
     * CHANGED: Traffic limiter data files moved to data/ (→easier rights management)
     * ADDED: "Burn after reading" implemented. Opening the URL will display the paste and immediately destroy it on server.
   * **Alpha 0.13 (2012-04-18):**
@@ -236,16 +237,16 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u
     * FIXED: $error not properly initialized in index.php
   * **Alpha 0.12 (2012-04-18):**
     * **DISCUSSIONS !** Now you can enable discussions on your pastes. Of course, posted comments and nickname are also encrypted and the server cannot see them.
-    * This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin. 
+    * This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin.
     * Added [[php:vizhash_gd|Vizhash]] as avatars, so you can match posters IP addresses without revealing them. (Same image = same IP). Of course the IP address cannot be deduced from the Vizhash.
     * Remaining time before expiration is now displayed.
-    * Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser. 
+    * Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser.
     * Better cleaning of the URL (to make sure the key is not broken by some stupid redirection service)
   * **Alpha 0.11 (2012-04-12):**
     * Automatically ignore parameters (such as &utm_source=...) added //after// the anchor by some stupid Web 2.0 services.
     * First public release.
   * **Alpha 0.10 (2012-04-12):**
-    * IE9 does not seem to correctly support ''pre-wrap'' either. Special handling mode activated for all version of IE<10. (Note: **ALL other browsers** correctly support this feature.) 
+    * IE9 does not seem to correctly support ''pre-wrap'' either. Special handling mode activated for all version of IE<10. (Note: **ALL other browsers** correctly support this feature.)
   * **Alpha 0.9 (2012-04-11):**
     * Oh bummer... IE 8 is as shitty as IE6/7: Its does not seem to support ''white-space:pre-wrap'' correctly. I had to activate the special handling mode. I still have to test IE 9.
   * **Alpha 0.8 (2012-04-11):**

+ 1 - 0
CREDITS.md

@@ -29,6 +29,7 @@ Sébastien Sauvage - original idea and main developer
 * Lucas Savva - configurable config file location, NixOS packaging
 * rodehoed - option to exempt ips from the rate-limiter
 * Mark van Holsteijn - Google Cloud Storage backend
+* Austin Huang - Oracle database support
 
 ## Translations
 * Hexalyse - French

+ 81 - 151
lib/Data/Database.php

@@ -199,31 +199,12 @@ class Database extends AbstractData
             $burnafterreading = $paste['adata'][3];
         }
         try {
-            $big_string       = $isVersion1 ? $paste['data'] : Json::encode($paste);
-            $metajson         = Json::encode($meta);
-            if (self::$_type === 'oci') {
-                // It is not possible to execute in the normal way if strlen($big_string) >= 4000
-                $stmt = self::$_db->prepare(
-                    'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
-                    ' VALUES(?,?,?,?,?,?,?,?,?)'
-                );
-                $stmt->bindParam(1, $pasteid);
-                $stmt->bindParam(2, $big_string, PDO::PARAM_STR, strlen($big_string));
-                $stmt->bindParam(3, $created, PDO::PARAM_INT);
-                $stmt->bindParam(4, $expire_date, PDO::PARAM_INT);
-                $stmt->bindParam(5, $opendiscussion, PDO::PARAM_INT);
-                $stmt->bindParam(6, $burnafterreading, PDO::PARAM_INT);
-                $stmt->bindParam(7, $metajson);
-                $stmt->bindParam(8, $attachment, PDO::PARAM_STR, strlen($attachment));
-                $stmt->bindParam(9, $attachmentname);
-                return $stmt->execute();
-            }
             return self::_exec(
                 'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
                 ' VALUES(?,?,?,?,?,?,?,?,?)',
                 array(
                     $pasteid,
-                    $big_string,
+                    $isVersion1 ? $paste['data'] : Json::encode($paste),
                     $created,
                     $expire_date,
                     (int) $opendiscussion,
@@ -252,15 +233,11 @@ class Database extends AbstractData
         }
 
         self::$_cache[$pasteid] = false;
-        $rawData                = '';
         try {
             $paste = self::_select(
                 'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
                 ' WHERE dataid = ?', array($pasteid), true
             );
-            if ($paste !== false) {
-                $rawData = self::$_type === 'oci' ? self::_clob($paste['DATA']) : $paste['data'];
-            }
         } catch (Exception $e) {
             $paste = false;
         }
@@ -268,25 +245,25 @@ class Database extends AbstractData
             return false;
         }
         // create array
-        $data       = Json::decode($rawData);
+        $data       = Json::decode($paste['data']);
         $isVersion2 = array_key_exists('v', $data) && $data['v'] >= 2;
         if ($isVersion2) {
             self::$_cache[$pasteid] = $data;
             list($createdKey)       = self::_getVersionedKeys(2);
         } else {
-            self::$_cache[$pasteid] = array('data' => $paste[self::_sanitizeColumn('data')]);
+            self::$_cache[$pasteid] = array('data' => $paste['data']);
             list($createdKey)       = self::_getVersionedKeys(1);
         }
 
         try {
-            $paste['meta'] = Json::decode($paste[self::_sanitizeColumn('meta')]);
+            $paste['meta'] = Json::decode($paste['meta']);
         } catch (Exception $e) {
             $paste['meta'] = array();
         }
         $paste                                       = self::upgradePreV1Format($paste);
         self::$_cache[$pasteid]['meta']              = $paste['meta'];
-        self::$_cache[$pasteid]['meta'][$createdKey] = (int) $paste[self::_sanitizeColumn('postdate')];
-        $expire_date                                 = (int) $paste[self::_sanitizeColumn('expiredate')];
+        self::$_cache[$pasteid]['meta'][$createdKey] = (int) $paste['postdate'];
+        $expire_date                                 = (int) $paste['expiredate'];
         if ($expire_date > 0) {
             self::$_cache[$pasteid]['meta']['expire_date'] = $expire_date;
         }
@@ -295,16 +272,16 @@ class Database extends AbstractData
         }
 
         // support v1 attachments
-        if (array_key_exists(self::_sanitizeColumn('attachment'), $paste) && strlen($paste[self::_sanitizeColumn('attachment')])) {
-            self::$_cache[$pasteid]['attachment'] = $paste[self::_sanitizeColumn('attachment')];
-            if (array_key_exists(self::_sanitizeColumn('attachmentname'), $paste) && strlen($paste[self::_sanitizeColumn('attachmentname')])) {
-                self::$_cache[$pasteid]['attachmentname'] = $paste[self::_sanitizeColumn('attachmentname')];
+        if (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) {
+            self::$_cache[$pasteid]['attachment'] = $paste['attachment'];
+            if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) {
+                self::$_cache[$pasteid]['attachmentname'] = $paste['attachmentname'];
             }
         }
-        if ($paste[self::_sanitizeColumn('opendiscussion')]) {
+        if ($paste['opendiscussion']) {
             self::$_cache[$pasteid]['meta']['opendiscussion'] = true;
         }
-        if ($paste[self::_sanitizeColumn('burnafterreading')]) {
+        if ($paste['burnafterreading']) {
             self::$_cache[$pasteid]['meta']['burnafterreading'] = true;
         }
 
@@ -379,21 +356,6 @@ class Database extends AbstractData
             }
         }
         try {
-            if (self::$_type === 'oci') {
-                // It is not possible to execute in the normal way if strlen($big_string) >= 4000
-                $stmt = self::$_db->prepare(
-                    'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
-                    ' VALUES(?,?,?,?,?,?,?)'
-                );
-                $stmt->bindParam(1, $commentid);
-                $stmt->bindParam(2, $pasteid);
-                $stmt->bindParam(3, $parentid);
-                $stmt->bindParam(4, $data, PDO::PARAM_STR, strlen($data));
-                $stmt->bindParam(5, $meta['nickname']);
-                $stmt->bindParam(6, $meta[$iconKey]);
-                $stmt->bindParam(7, $meta[$createdKey], PDO::PARAM_INT);
-                return $stmt->execute();
-            }
             return self::_exec(
                 'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
                 ' VALUES(?,?,?,?,?,?,?)',
@@ -430,32 +392,22 @@ class Database extends AbstractData
         $comments = array();
         if (count($rows)) {
             foreach ($rows as $row) {
-                $i      = $this->getOpenSlot($comments, (int) $row[self::_sanitizeColumn('postdate')]);
-                $id     = $row[self::_sanitizeColumn('dataid')];
-                if (self::$_type === 'oci') {
-                    $newrow = self::_select(
-                        'SELECT data FROM ' . self::_sanitizeIdentifier('comment') .
-                        ' WHERE dataid = ?', array($id), true
-                    );
-                    $rawData = self::_clob($newrow['DATA']);
-                } else {
-                    $rawData = $row['data'];
-                }
-                $data   = Json::decode($rawData);
+                $i    = $this->getOpenSlot($comments, (int) $row['postdate']);
+                $data = Json::decode($row['data']);
                 if (array_key_exists('v', $data) && $data['v'] >= 2) {
                     $version      = 2;
                     $comments[$i] = $data;
                 } else {
                     $version      = 1;
-                    $comments[$i] = array('data' => $rawData);
+                    $comments[$i] = array('data' => $row['data']);
                 }
                 list($createdKey, $iconKey) = self::_getVersionedKeys($version);
-                $comments[$i]['id']         = $id;
-                $comments[$i]['parentid']   = $row[self::_sanitizeColumn('parentid')];
-                $comments[$i]['meta']       = array($createdKey => (int) $row[self::_sanitizeColumn('postdate')]);
+                $comments[$i]['id']         = $row['dataid'];
+                $comments[$i]['parentid']   = $row['parentid'];
+                $comments[$i]['meta']       = array($createdKey => (int) $row['postdate']);
                 foreach (array('nickname' => 'nickname', 'vizhash' => $iconKey) as $rowKey => $commentKey) {
-                    if (array_key_exists(self::_sanitizeColumn($rowKey), $row) && !empty($row[self::_sanitizeColumn($rowKey)])) {
-                        $comments[$i]['meta'][$commentKey] = $row[self::_sanitizeColumn($rowKey)];
+                    if (array_key_exists($rowKey, $row) && !empty($row[$rowKey])) {
+                        $comments[$i]['meta'][$commentKey] = $row[$rowKey];
                     }
                 }
             }
@@ -572,7 +524,7 @@ class Database extends AbstractData
         );
         if (count($rows)) {
             foreach ($rows as $row) {
-                $pastes[] = $row[self::_sanitizeIdentifier('dataid')];
+                $pastes[] = $row['dataid'];
             }
         }
         return $pastes;
@@ -591,7 +543,22 @@ class Database extends AbstractData
     private static function _exec($sql, array $params)
     {
         $statement = self::$_db->prepare($sql);
-        $result    = $statement->execute($params);
+        if (self::$_type === 'oci') {
+            // It is not possible to execute in the normal way if strlen($param) >= 4000
+            foreach ($params as $key => $parameter) {
+                $position = $key + 1;
+                if (is_int($parameter)) {
+                    $statement->bindParam($position, $parameter, PDO::PARAM_INT);
+                } elseif ($length = strlen($parameter) >= 4000) {
+                    $statement->bindParam($position, $parameter, PDO::PARAM_STR, $length);
+                } else {
+                    $statement->bindParam($position, $parameter);
+                }
+            }
+            $result = $statement->execute();
+        } else {
+            $result = $statement->execute($params);
+        }
         $statement->closeCursor();
         return $result;
     }
@@ -615,6 +582,14 @@ class Database extends AbstractData
             $statement->fetch(PDO::FETCH_ASSOC) :
             $statement->fetchAll(PDO::FETCH_ASSOC);
         $statement->closeCursor();
+        if (self::$_type === 'oci') {
+            // returned column names are all upper case, convert these back
+            // returned CLOB values are streams, convert these into strings
+            $result = array_combine(
+                array_map('strtolower', array_keys($result)),
+                array_map('self::_sanitizeClob', array_values($result))
+            );
+        }
         return $result;
     }
 
@@ -707,19 +682,7 @@ class Database extends AbstractData
         } catch (PDOException $e) {
             return '';
         }
-        return $row ? $row[self::_sanitizeColumn('value')] : '';
-    }
-
-    /**
-     * OCI cannot accept semicolons
-     *
-     * @access private
-     * @static
-     * @return string
-     */
-    private static function _getSemicolon()
-    {
-        return self::$_type === 'oci' ? '' : ';';
+        return $row ? $row['value'] : '';
     }
 
     /**
@@ -744,7 +707,7 @@ class Database extends AbstractData
     /**
      * get the data type, depending on the database driver
      *
-     * PostgreSQL uses a different API for BLOBs then SQL, hence we use TEXT
+     * PostgreSQL and OCI uses a different API for BLOBs then SQL, hence we use TEXT and CLOB
      *
      * @access private
      * @static
@@ -752,7 +715,7 @@ class Database extends AbstractData
      */
     private static function _getDataType()
     {
-        return self::$_type === 'pgsql' ? 'TEXT' : (self::$_type === 'oci' ? 'VARCHAR2(4000)' : 'BLOB');
+        return self::$_type === 'pgsql' ? 'TEXT' : (self::$_type === 'oci' ? 'CLOB' : 'BLOB');
     }
 
     /**
@@ -772,7 +735,7 @@ class Database extends AbstractData
     /**
      * get the meta type, depending on the database driver
      *
-     * OCI can't even accept TEXT so it has to be VARCHAR2(200)
+     * OCI doesn't accept TEXT so it has to be VARCHAR2(4000)
      *
      * @access private
      * @static
@@ -798,31 +761,17 @@ class Database extends AbstractData
         self::$_db->exec(
             'CREATE TABLE ' . self::_sanitizeIdentifier('paste') . ' ( ' .
             "dataid CHAR(16) NOT NULL$main_key, " .
-            "data $attachmentType, " .
+            "data $dataType, " .
             'postdate INT, ' .
             'expiredate INT, ' .
             'opendiscussion INT, ' .
             'burnafterreading INT, ' .
             "meta $metaType, " .
             "attachment $attachmentType, " .
-            "attachmentname $dataType$after_key )" . self::_getSemicolon()
+            "attachmentname $dataType$after_key )"
         );
     }
 
-    /**
-     * get the nullable text type, depending on the database driver
-     *
-     * OCI will pad CHAR columns with spaces, hence VARCHAR2
-     *
-     * @access private
-     * @static
-     * @return string
-     */
-    private static function _getParentType()
-    {
-        return self::$_type === 'oci' ? 'VARCHAR2(16)' : 'CHAR(16)';
-    }
-
     /**
      * create the paste table
      *
@@ -833,21 +782,19 @@ class Database extends AbstractData
     {
         list($main_key, $after_key) = self::_getPrimaryKeyClauses();
         $dataType                   = self::_getDataType();
-        $parentType                 = self::_getParentType();
-        $attachmentType             = self::_getAttachmentType();
         self::$_db->exec(
             'CREATE TABLE ' . self::_sanitizeIdentifier('comment') . ' ( ' .
             "dataid CHAR(16) NOT NULL$main_key, " .
             'pasteid CHAR(16), ' .
-            "parentid $parentType, " .
-            "data $attachmentType, " .
+            'parentid CHAR(16), ' .
+            "data $dataType, " .
             "nickname $dataType, " .
             "vizhash $dataType, " .
-            "postdate INT$after_key )" . self::_getSemicolon()
+            "postdate INT$after_key )"
         );
         self::$_db->exec(
-            'CREATE INDEX comment_parent ON ' .
-            self::_sanitizeIdentifier('comment') . '(pasteid)' . self::_getSemicolon()
+            'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
+            self::_sanitizeIdentifier('comment') . '(pasteid)'
         );
     }
 
@@ -864,7 +811,7 @@ class Database extends AbstractData
         $textType                   = self::$_type === 'oci' ? 'VARCHAR2(4000)' : 'TEXT';
         self::$_db->exec(
             'CREATE TABLE ' . self::_sanitizeIdentifier('config') .
-            " ( id $charType NOT NULL$main_key, value $textType$after_key )" . self::_getSemicolon()
+            " ( id $charType NOT NULL$main_key, value $textType$after_key )"
         );
         self::_exec(
             'INSERT INTO ' . self::_sanitizeIdentifier('config') .
@@ -874,30 +821,34 @@ class Database extends AbstractData
     }
 
     /**
-     * sanitizes identifiers
+     * sanitizes CLOB values used with OCI
+     *
+     * From: https://stackoverflow.com/questions/36200534/pdo-oci-into-a-clob-field
      *
      * @access private
      * @static
-     * @param  string $identifier
-     * @return string
+     * @param  int|string|resource $value
+     * @return int|string
      */
-    private static function _sanitizeIdentifier($identifier)
+    public static function _sanitizeClob($value)
     {
-        $id = preg_replace('/[^A-Za-z0-9_]+/', '', self::$_prefix . $identifier);
-        return self::_sanitizeColumn($id);
+        if (is_resource($value)) {
+            $value = stream_get_contents($value);
+        }
+        return $value;
     }
 
     /**
-     * sanitizes column name because OCI
+     * sanitizes identifiers
      *
      * @access private
      * @static
-     * @param  string $name
+     * @param  string $identifier
      * @return string
      */
-    private static function _sanitizeColumn($name)
+    private static function _sanitizeIdentifier($identifier)
     {
-        return self::$_type === 'oci' ? strtoupper($name) : $name;
+        return preg_replace('/[^A-Za-z0-9_]+/', '', self::$_prefix . $identifier);
     }
 
     /**
@@ -915,43 +866,43 @@ class Database extends AbstractData
             case '0.21':
                 // create the meta column if necessary (pre 0.21 change)
                 try {
-                    self::$_db->exec('SELECT meta FROM ' . self::_sanitizeIdentifier('paste') . ' LIMIT 1;');
+                    self::$_db->exec('SELECT meta FROM ' . self::_sanitizeIdentifier('paste') . ' LIMIT 1');
                 } catch (PDOException $e) {
-                    self::$_db->exec('ALTER TABLE ' . self::_sanitizeIdentifier('paste') . ' ADD COLUMN meta TEXT;');
+                    self::$_db->exec('ALTER TABLE ' . self::_sanitizeIdentifier('paste') . ' ADD COLUMN meta TEXT');
                 }
                 // SQLite only allows one ALTER statement at a time...
                 self::$_db->exec(
                     'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
-                    " ADD COLUMN attachment $attachmentType;"
+                    " ADD COLUMN attachment $attachmentType"
                 );
                 self::$_db->exec(
-                    'ALTER TABLE ' . self::_sanitizeIdentifier('paste') . " ADD COLUMN attachmentname $dataType;"
+                    'ALTER TABLE ' . self::_sanitizeIdentifier('paste') . " ADD COLUMN attachmentname $dataType"
                 );
                 // SQLite doesn't support MODIFY, but it allows TEXT of similar
                 // size as BLOB, so there is no need to change it there
                 if (self::$_type !== 'sqlite') {
                     self::$_db->exec(
                         'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
-                        " ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType;"
+                        " ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType"
                     );
                     self::$_db->exec(
                         'ALTER TABLE ' . self::_sanitizeIdentifier('comment') .
                         " ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType, " .
-                        "MODIFY COLUMN nickname $dataType, MODIFY COLUMN vizhash $dataType;"
+                        "MODIFY COLUMN nickname $dataType, MODIFY COLUMN vizhash $dataType"
                     );
                 } else {
                     self::$_db->exec(
                         'CREATE UNIQUE INDEX IF NOT EXISTS paste_dataid ON ' .
-                        self::_sanitizeIdentifier('paste') . '(dataid);'
+                        self::_sanitizeIdentifier('paste') . '(dataid)'
                     );
                     self::$_db->exec(
                         'CREATE UNIQUE INDEX IF NOT EXISTS comment_dataid ON ' .
-                        self::_sanitizeIdentifier('comment') . '(dataid);'
+                        self::_sanitizeIdentifier('comment') . '(dataid)'
                     );
                 }
                 self::$_db->exec(
                     'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
-                    self::_sanitizeIdentifier('comment') . '(pasteid);'
+                    self::_sanitizeIdentifier('comment') . '(pasteid)'
                 );
                 // no break, continue with updates for 0.22 and later
             case '1.3':
@@ -961,7 +912,7 @@ class Database extends AbstractData
                 if (self::$_type !== 'sqlite' && self::$_type !== 'pgsql') {
                     self::$_db->exec(
                         'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
-                        " MODIFY COLUMN data $attachmentType;"
+                        " MODIFY COLUMN data $attachmentType"
                     );
                 }
                 // no break, continue with updates for all newer versions
@@ -973,25 +924,4 @@ class Database extends AbstractData
                 );
         }
     }
-
-    /**
-     * read CLOB for OCI
-     * https://stackoverflow.com/questions/36200534/pdo-oci-into-a-clob-field
-     *
-     * @access private
-     * @static
-     * @param  resource $column
-     * @return string
-     */
-    private static function _clob($column)
-    {
-        if ($column == null) {
-            return null;
-        }
-        $str = '';
-        while ($tmp = fread($column, 1024)) {
-            $str .= $tmp;
-        }
-        return $str;
-    }
 }

+ 12 - 0
tst/Data/DatabaseTest.php

@@ -388,4 +388,16 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(Controller::VERSION, $result['value']);
         Helper::rmDir($this->_path);
     }
+
+    public function testOciClob()
+    {
+        $int = (int) random_bytes(1);
+        $string = random_bytes(10);
+        $clob = fopen('php://memory', 'r+');
+        fwrite($clob, $string);
+        rewind($clob);
+        $this->assertEquals($int, Database::_sanitizeClob($int));
+        $this->assertEquals($string, Database::_sanitizeClob($string));
+        $this->assertEquals($string, Database::_sanitizeClob($clob));
+    }
 }