1<?php
2/*
3@version   v5.20.20  01-Feb-2021
4@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
5@copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
6  Released under both BSD license and Lesser GPL library license.
7  Whenever there is any discrepancy between the two licenses,
8  the BSD license will take precedence.
9
10  Latest version is available at http://adodb.org/
11
12  Oracle data driver. Requires Oracle client. Works on Windows and Unix and Oracle 7.
13
14  If you are using Oracle 8 or later, use the oci8 driver which is much better and more reliable.
15*/
16
17// security - hide paths
18if (!defined('ADODB_DIR')) die();
19
20class ADODB_oracle extends ADOConnection {
21	var $databaseType = "oracle";
22	var $replaceQuote = "''"; // string to use to replace quotes
23	var $concat_operator='||';
24	var $_curs;
25	var $_initdate = true; // init date to YYYY-MM-DD
26	var $metaTablesSQL = 'select table_name from cat';
27	var $metaColumnsSQL = "select cname,coltype,width from col where tname='%s' order by colno";
28	var $sysDate = "TO_DATE(TO_CHAR(SYSDATE,'YYYY-MM-DD'),'YYYY-MM-DD')";
29	var $sysTimeStamp = 'SYSDATE';
30	var $connectSID = true;
31
32	function __construct()
33	{
34	}
35
36	// format and return date string in database date format
37	function DBDate($d, $isfld = false)
38	{
39		if (is_string($d)) $d = ADORecordSet::UnixDate($d);
40		if (is_object($d)) $ds = $d->format($this->fmtDate);
41		else $ds = adodb_date($this->fmtDate,$d);
42		return 'TO_DATE('.$ds.",'YYYY-MM-DD')";
43	}
44
45	// format and return date string in database timestamp format
46	function DBTimeStamp($ts, $isfld = false)
47	{
48
49		if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
50		if (is_object($ts)) $ds = $ts->format($this->fmtDate);
51		else $ds = adodb_date($this->fmtTimeStamp,$ts);
52		return 'TO_DATE('.$ds.",'RRRR-MM-DD, HH:MI:SS AM')";
53	}
54
55
56	function BindDate($d)
57	{
58		$d = ADOConnection::DBDate($d);
59		if (strncmp($d,"'",1)) return $d;
60
61		return substr($d,1,strlen($d)-2);
62	}
63
64	function BindTimeStamp($d)
65	{
66		$d = ADOConnection::DBTimeStamp($d);
67		if (strncmp($d,"'",1)) return $d;
68
69		return substr($d,1,strlen($d)-2);
70	}
71
72
73
74	function BeginTrans()
75	{
76		 $this->autoCommit = false;
77		 ora_commitoff($this->_connectionID);
78		 return true;
79	}
80
81
82	function CommitTrans($ok=true)
83	{
84		   if (!$ok) return $this->RollbackTrans();
85		   $ret = ora_commit($this->_connectionID);
86		   ora_commiton($this->_connectionID);
87		   return $ret;
88	}
89
90
91	function RollbackTrans()
92	{
93		$ret = ora_rollback($this->_connectionID);
94		ora_commiton($this->_connectionID);
95		return $ret;
96	}
97
98
99	/* there seems to be a bug in the oracle extension -- always returns ORA-00000 - no error */
100	function ErrorMsg()
101 	{
102        if ($this->_errorMsg !== false) return $this->_errorMsg;
103
104        if (is_resource($this->_curs)) $this->_errorMsg = @ora_error($this->_curs);
105 		if (empty($this->_errorMsg)) $this->_errorMsg = @ora_error($this->_connectionID);
106		return $this->_errorMsg;
107	}
108
109
110	function ErrorNo()
111	{
112		if ($this->_errorCode !== false) return $this->_errorCode;
113
114		if (is_resource($this->_curs)) $this->_errorCode = @ora_errorcode($this->_curs);
115		if (empty($this->_errorCode)) $this->_errorCode = @ora_errorcode($this->_connectionID);
116        return $this->_errorCode;
117	}
118
119
120
121		// returns true or false
122		function _connect($argHostname, $argUsername, $argPassword, $argDatabasename, $mode=0)
123		{
124			if (!function_exists('ora_plogon')) return null;
125
126            // <G. Giunta 2003/03/03/> Reset error messages before connecting
127            $this->_errorMsg = false;
128		    $this->_errorCode = false;
129
130            // G. Giunta 2003/08/13 - This looks danegrously suspicious: why should we want to set
131            // the oracle home to the host name of remote DB?
132//			if ($argHostname) putenv("ORACLE_HOME=$argHostname");
133
134			if($argHostname) { // code copied from version submitted for oci8 by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
135				if (empty($argDatabasename)) $argDatabasename = $argHostname;
136				else {
137					if(strpos($argHostname,":")) {
138						$argHostinfo=explode(":",$argHostname);
139						$argHostname=$argHostinfo[0];
140						$argHostport=$argHostinfo[1];
141					} else {
142						$argHostport="1521";
143					}
144
145
146					if ($this->connectSID) {
147						$argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
148						.")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
149					} else
150						$argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
151						.")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
152				}
153
154			}
155
156			if ($argDatabasename) $argUsername .= "@$argDatabasename";
157
158		//if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
159			if ($mode == 1)
160				$this->_connectionID = ora_plogon($argUsername,$argPassword);
161			else
162				$this->_connectionID = ora_logon($argUsername,$argPassword);
163			if ($this->_connectionID === false) return false;
164			if ($this->autoCommit) ora_commiton($this->_connectionID);
165			if ($this->_initdate) {
166				$rs = $this->_query("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD'");
167				if ($rs) ora_close($rs);
168			}
169
170			return true;
171		}
172
173
174		// returns true or false
175		function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
176		{
177			return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, 1);
178		}
179
180
181		// returns query ID if successful, otherwise false
182		function _query($sql,$inputarr=false)
183		{
184            // <G. Giunta 2003/03/03/> Reset error messages before executing
185            $this->_errorMsg = false;
186		    $this->_errorCode = false;
187
188			$curs = ora_open($this->_connectionID);
189
190		 	if ($curs === false) return false;
191			$this->_curs = $curs;
192			if (!ora_parse($curs,$sql)) return false;
193			if (ora_exec($curs)) return $curs;
194            // <G. Giunta 2004/03/03> before we close the cursor, we have to store the error message
195            // that we can obtain ONLY from the cursor (and not from the connection)
196            $this->_errorCode = @ora_errorcode($curs);
197            $this->_errorMsg = @ora_error($curs);
198            // </G. Giunta 2004/03/03>
199		 	@ora_close($curs);
200			return false;
201		}
202
203
204		// returns true or false
205		function _close()
206		{
207			return @ora_logoff($this->_connectionID);
208		}
209
210
211
212}
213
214
215/*--------------------------------------------------------------------------------------
216		 Class Name: Recordset
217--------------------------------------------------------------------------------------*/
218
219class ADORecordset_oracle extends ADORecordSet {
220
221	var $databaseType = "oracle";
222	var $bind = false;
223
224	function __construct($queryID,$mode=false)
225	{
226
227		if ($mode === false) {
228			global $ADODB_FETCH_MODE;
229			$mode = $ADODB_FETCH_MODE;
230		}
231		$this->fetchMode = $mode;
232
233		$this->_queryID = $queryID;
234
235		$this->_inited = true;
236		$this->fields = array();
237		if ($queryID) {
238			$this->_currentRow = 0;
239			$this->EOF = !$this->_fetch();
240			@$this->_initrs();
241		} else {
242			$this->_numOfRows = 0;
243			$this->_numOfFields = 0;
244			$this->EOF = true;
245		}
246
247		return $this->_queryID;
248	}
249
250
251
252	   /*		Returns: an object containing field information.
253			   Get column information in the Recordset object. fetchField() can be used in order to obtain information about
254			   fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
255			   fetchField() is retrieved.		*/
256
257	   function FetchField($fieldOffset = -1)
258	   {
259			$fld = new ADOFieldObject;
260			$fld->name = ora_columnname($this->_queryID, $fieldOffset);
261			$fld->type = ora_columntype($this->_queryID, $fieldOffset);
262			$fld->max_length = ora_columnsize($this->_queryID, $fieldOffset);
263			return $fld;
264	   }
265
266	/* Use associative array to get fields array */
267	function Fields($colname)
268	{
269		if (!$this->bind) {
270			$this->bind = array();
271			for ($i=0; $i < $this->_numOfFields; $i++) {
272				$o = $this->FetchField($i);
273				$this->bind[strtoupper($o->name)] = $i;
274			}
275		}
276
277		 return $this->fields[$this->bind[strtoupper($colname)]];
278	}
279
280   function _initrs()
281   {
282		   $this->_numOfRows = -1;
283		   $this->_numOfFields = @ora_numcols($this->_queryID);
284   }
285
286
287   function _seek($row)
288   {
289		   return false;
290   }
291
292   function _fetch($ignore_fields=false) {
293// should remove call by reference, but ora_fetch_into requires it in 4.0.3pl1
294		if ($this->fetchMode & ADODB_FETCH_ASSOC)
295			return @ora_fetch_into($this->_queryID,$this->fields,ORA_FETCHINTO_NULLS|ORA_FETCHINTO_ASSOC);
296   		else
297			return @ora_fetch_into($this->_queryID,$this->fields,ORA_FETCHINTO_NULLS);
298   }
299
300   /*		close() only needs to be called if you are worried about using too much memory while your script
301		   is running. All associated result memory for the specified result identifier will automatically be freed.		*/
302
303   function _close()
304{
305		   return @ora_close($this->_queryID);
306   }
307
308	function MetaType($t, $len = -1, $fieldobj = false)
309	{
310		if (is_object($t)) {
311			$fieldobj = $t;
312			$t = $fieldobj->type;
313			$len = $fieldobj->max_length;
314		}
315
316		switch (strtoupper($t)) {
317		case 'VARCHAR':
318		case 'VARCHAR2':
319		case 'CHAR':
320		case 'VARBINARY':
321		case 'BINARY':
322				if ($len <= $this->blobSize) return 'C';
323		case 'LONG':
324		case 'LONG VARCHAR':
325		case 'CLOB':
326		return 'X';
327		case 'LONG RAW':
328		case 'LONG VARBINARY':
329		case 'BLOB':
330				return 'B';
331
332		case 'DATE': return 'D';
333
334		//case 'T': return 'T';
335
336		case 'BIT': return 'L';
337		case 'INT':
338		case 'SMALLINT':
339		case 'INTEGER': return 'I';
340		default: return 'N';
341		}
342	}
343}
344