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