Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  /**
   3   * Portable version of Oracle oci8 driver
   4   *
   5   * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
   6   *
   7   * Portable version of oci8 driver, to make it more similar to other database
   8   * drivers. The main differences are
   9   * 1. that the OCI_ASSOC names are in lowercase instead of uppercase.
  10   * 2. bind variables are mapped using ? instead of :<bindvar>
  11   *
  12   * @package ADOdb
  13   * @link https://adodb.org Project's web site and documentation
  14   * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
  15   *
  16   * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
  17   * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
  18   * any later version. This means you can use it in proprietary products.
  19   * See the LICENSE.md file distributed with this source code for details.
  20   * @license BSD-3-Clause
  21   * @license LGPL-2.1-or-later
  22   *
  23   * @copyright 2000-2013 John Lim
  24   * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
  25   */
  26  
  27  // security - hide paths
  28  if (!defined('ADODB_DIR')) die();
  29  
  30  include_once(ADODB_DIR.'/drivers/adodb-oci8.inc.php');
  31  
  32  class ADODB_oci8po extends ADODB_oci8 {
  33  	 var $databaseType = 'oci8po';
  34  	 var $dataProvider = 'oci8';
  35  	 var $metaColumnsSQL = "select lower(cname),coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net
  36  	 var $metaTablesSQL = "select lower(table_name),table_type from cat where table_type in ('TABLE','VIEW')";
  37  
  38  	function Param($name,$type='C')
  39  	 {
  40  	 	 return '?';
  41  	 }
  42  
  43  	function Prepare($sql,$cursor=false)
  44  	 {
  45  	 	 $sqlarr = explode('?',$sql);
  46  	 	 $sql = $sqlarr[0];
  47  	 	 for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) {
  48  	 	 	 $sql .=  ':'.($i-1) . $sqlarr[$i];
  49  	 	 }
  50  	 	 return ADODB_oci8::Prepare($sql,$cursor);
  51  	 }
  52  
  53  	function Execute($sql,$inputarr=false)
  54  	 {
  55  	 	 return ADOConnection::Execute($sql,$inputarr);
  56  	 }
  57  
  58  	 /**
  59  	  * The optimizations performed by ADODB_oci8::SelectLimit() are not
  60  	  * compatible with the oci8po driver, so we rely on the slower method
  61  	  * from the base class.
  62  	  * We can't properly handle prepared statements either due to preprocessing
  63  	  * of query parameters, so we treat them as regular SQL statements.
  64  	  */
  65  	function SelectLimit($sql, $nrows=-1, $offset=-1, $inputarr=false, $secs2cache=0)
  66  	 {
  67  	 	 if(is_array($sql)) {
  68  //	 	 	 $sql = $sql[0];
  69  	 	 }
  70  	 	 return ADOConnection::SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
  71  	 }
  72  
  73  	 // emulate handling of parameters ? ?, replacing with :bind0 :bind1
  74  	function _query($sql,$inputarr=false)
  75  	 {
  76  	 	 if (is_array($inputarr)) {
  77  	 	 	 $i = 0;
  78  	 	 	 if (is_array($sql)) {
  79  	 	 	 	 foreach($inputarr as $v) {
  80  	 	 	 	 	 $arr['bind'.$i++] = $v;
  81  	 	 	 	 }
  82  	 	 	 } else {
  83  	 	 	 	 $sql = $this->extractBinds($sql,$inputarr);
  84  	 	 	 }
  85  	 	 }
  86  	 	 return ADODB_oci8::_query($sql,$inputarr);
  87  	 }
  88  	 
  89  	 /**
  90  	 * Replaces compatibility bind markers with oracle ones and returns a
  91  	 * valid sql statement
  92  	 *
  93  	 * This replaces a regexp based section of code that has been subject
  94  	 * to numerous tweaks, as more extreme test cases have appeared. This
  95  	 * is now done this like this to help maintainability and avoid the 
  96  	 * need to rely on regexp experienced maintainers
  97  	 *
  98  	 * @param	 string	 	 $sql	 	 The sql statement
  99  	 * @param	 string[]	 $inputarr	 The bind array
 100  	 *
 101  	 * @return	 string	 The modified statement
 102  	 */	 
 103  	private function extractBinds($sql,$inputarr)
 104  	 {
 105  	 	 $inString  = false;
 106  	 	 $escaped   = 0;
 107  	 	 $sqlLength = strlen($sql) - 1;
 108  	 	 $newSql    = '';
 109  	 	 $bindCount = 0;
 110  	 	 
 111  	 	 /*
 112  	 	 * inputarr is the passed in bind list, which is associative, but
 113  	 	 * we only want the keys here
 114  	 	 */
 115  	 	 $inputKeys = array_keys($inputarr);
 116  	 	 
 117  	 	 
 118  	 	 for ($i=0;$i<=$sqlLength;$i++)
 119  	 	 {
 120  	 	 	 /*
 121  	 	 	 * find the next character of the string
 122  	 	 	 */
 123  	 	 	 $c = $sql[$i];
 124  
 125  	 	 	 if ($c == "'" && !$inString && $escaped==0)
 126  	 	 	 	 /*
 127  	 	 	 	 * Found the start of a string inside the statement
 128  	 	 	 	 */
 129  	 	 	 	 $inString = true;
 130  	 	 	 elseif ($c == "\\" && $escaped==0)
 131  	 	 	 	 /*
 132  	 	 	 	 * The next character will be escaped
 133  	 	 	 	 */
 134  	 	 	 	 $escaped = 1;
 135  	 	 	 elseif ($c == "'" && $inString && $escaped==0)
 136  	 	 	 	 /*
 137  	 	 	 	 * We found the end of the string
 138  	 	 	 	 */
 139  	 	 	 	 $inString = false;
 140  	 	 	 
 141  	 	 	 if ($escaped == 2)
 142  	 	 	 	 $escaped = 0;
 143  
 144  	 	 	 if ($escaped==0 && !$inString && $c == '?')
 145  	 	 	 	 /*
 146  	 	 	 	 * We found a bind symbol, replace it with the oracle equivalent
 147  	 	 	 	 */
 148  	 	 	 	 $newSql .= ':' . $inputKeys[$bindCount++];
 149  	 	 	 else
 150  	 	 	 	 /*
 151  	 	 	 	 * Add the current character the pile
 152  	 	 	 	 */
 153  	 	 	 	 $newSql .= $c;
 154  	 	 	 
 155  	 	 	 if ($escaped == 1)
 156  	 	 	 	 /*
 157  	 	 	 	 * We have just found an escape character, make sure we ignore the
 158  	 	 	 	 * next one that comes along, it might be a ' character
 159  	 	 	 	 */
 160  	 	 	 	 $escaped = 2;
 161  	 	 }
 162  	 	 
 163  	 	 return $newSql;
 164  	 	 	 
 165  	 }
 166  }
 167  
 168  /*--------------------------------------------------------------------------------------
 169  	 	  Class Name: Recordset
 170  --------------------------------------------------------------------------------------*/
 171  
 172  class ADORecordset_oci8po extends ADORecordset_oci8 {
 173  
 174  	 var $databaseType = 'oci8po';
 175  
 176  	function Fields($colname)
 177  	 {
 178  	 	 if ($this->fetchMode & OCI_ASSOC) return $this->fields[$colname];
 179  
 180  	 	 if (!$this->bind) {
 181  	 	 	 $this->bind = array();
 182  	 	 	 for ($i=0; $i < $this->_numOfFields; $i++) {
 183  	 	 	 	 $o = $this->FetchField($i);
 184  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
 185  	 	 	 }
 186  	 	 }
 187  	 	  return $this->fields[$this->bind[strtoupper($colname)]];
 188  	 }
 189  
 190  	 // lowercase field names...
 191  	function _FetchField($fieldOffset = -1)
 192  	 {
 193  	 	 $fld = new ADOFieldObject;
 194  	 	 $fieldOffset += 1;
 195  	 	 $fld->name = OCIcolumnname($this->_queryID, $fieldOffset);
 196  	 	 if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) {
 197  	 	 	 $fld->name = strtolower($fld->name);
 198  	 	 }
 199  	 	 $fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
 200  	 	 $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
 201  	 	 if ($fld->type == 'NUMBER') {
 202  	 	 	 $sc = OCIColumnScale($this->_queryID, $fieldOffset);
 203  	 	 	 if ($sc == 0) {
 204  	 	 	 	 $fld->type = 'INT';
 205  	 	 	 }
 206  	 	 }
 207  	 	 return $fld;
 208  	 }
 209  
 210  	 // 10% speedup to move MoveNext to child class
 211  	function MoveNext()
 212  	 {
 213  	 	 $ret = @oci_fetch_array($this->_queryID,$this->fetchMode);
 214  	 	 if($ret !== false) {
 215  	 	 global $ADODB_ANSI_PADDING_OFF;
 216  	 	 	 $this->fields = $ret;
 217  	 	 	 $this->_currentRow++;
 218  	 	 	 $this->_updatefields();
 219  
 220  	 	 	 if (!empty($ADODB_ANSI_PADDING_OFF)) {
 221  	 	 	 	 foreach($this->fields as $k => $v) {
 222  	 	 	 	 	 if (is_string($v)) $this->fields[$k] = rtrim($v);
 223  	 	 	 	 }
 224  	 	 	 }
 225  	 	 	 return true;
 226  	 	 }
 227  	 	 if (!$this->EOF) {
 228  	 	 	 $this->EOF = true;
 229  	 	 	 $this->_currentRow++;
 230  	 	 }
 231  	 	 return false;
 232  	 }
 233  
 234  	 /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
 235  	function GetArrayLimit($nrows,$offset=-1)
 236  	 {
 237  	 	 if ($offset <= 0) {
 238  	 	 	 $arr = $this->GetArray($nrows);
 239  	 	 	 return $arr;
 240  	 	 }
 241  	 	 for ($i=1; $i < $offset; $i++)
 242  	 	 	 if (!@OCIFetch($this->_queryID)) {
 243  	 	 	 	 $arr = array();
 244  	 	 	 	 return $arr;
 245  	 	 	 }
 246  	 	 $ret = @oci_fetch_array($this->_queryID,$this->fetchMode);
 247  	 	 if ($ret === false) {
 248  	 	 	 $arr = array();
 249  	 	 	 return $arr;
 250  	 	 }
 251  	 	 $this->fields = $ret;
 252  	 	 $this->_updatefields();
 253  	 	 $results = array();
 254  	 	 $cnt = 0;
 255  	 	 while (!$this->EOF && $nrows != $cnt) {
 256  	 	 	 $results[$cnt++] = $this->fields;
 257  	 	 	 $this->MoveNext();
 258  	 	 }
 259  
 260  	 	 return $results;
 261  	 }
 262  
 263  	function _fetch()
 264  	 {
 265  	 	 global $ADODB_ANSI_PADDING_OFF;
 266  
 267  	 	 $ret = @oci_fetch_array($this->_queryID,$this->fetchMode);
 268  	 	 if ($ret) {
 269  	 	 	 $this->fields = $ret;
 270  	 	 	 $this->_updatefields();
 271  
 272  	 	 	 if (!empty($ADODB_ANSI_PADDING_OFF)) {
 273  	 	 	 	 foreach($this->fields as $k => $v) {
 274  	 	 	 	 	 if (is_string($v)) $this->fields[$k] = rtrim($v);
 275  	 	 	 	 }
 276  	 	 	 }
 277  	 	 }
 278  	 	 return $ret !== false;
 279  	 }
 280  
 281  }