1<?php
2/*
3@version   v5.20.20  01-Feb-2021
4@copyright (c) 2000-2013 John Lim. 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  Set tabs to 4 for best viewing.
10
11  Latest version is available at http://adodb.org/
12
13  Sybase driver contributed by Toni (toni.tunkkari@finebyte.com)
14
15  - MSSQL date patch applied.
16
17  Date patch by Toni 15 Feb 2002
18*/
19
20 // security - hide paths
21if (!defined('ADODB_DIR')) die();
22
23class ADODB_sybase extends ADOConnection {
24	var $databaseType = "sybase";
25	var $dataProvider = 'sybase';
26	var $replaceQuote = "''"; // string to use to replace quotes
27	var $fmtDate = "'Y-m-d'";
28	var $fmtTimeStamp = "'Y-m-d H:i:s'";
29	var $hasInsertID = true;
30	var $hasAffectedRows = true;
31  	var $metaTablesSQL="select name from sysobjects where type='U' or type='V'";
32	// see http://sybooks.sybase.com/onlinebooks/group-aw/awg0800e/dbrfen8/@ebt-link;pt=5981;uf=0?target=0;window=new;showtoc=true;book=dbrfen8
33	var $metaColumnsSQL = "SELECT c.column_name, c.column_type, c.width FROM syscolumn c, systable t WHERE t.table_name='%s' AND c.table_id=t.table_id AND t.table_type='BASE'";
34	/*
35	"select c.name,t.name,c.length from
36	syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id
37	where o.name='%s'";
38	*/
39	var $concat_operator = '+';
40	var $arrayClass = 'ADORecordSet_array_sybase';
41	var $sysDate = 'GetDate()';
42	var $leftOuter = '*=';
43	var $rightOuter = '=*';
44
45	var $port;
46
47	function __construct()
48	{
49	}
50
51	// might require begintrans -- committrans
52	function _insertid()
53	{
54		return $this->GetOne('select @@identity');
55	}
56	  // might require begintrans -- committrans
57	function _affectedrows()
58	{
59		return $this->GetOne('select @@rowcount');
60	}
61
62
63	function BeginTrans()
64	{
65
66		if ($this->transOff) return true;
67		$this->transCnt += 1;
68
69		$this->Execute('BEGIN TRAN');
70		return true;
71	}
72
73	function CommitTrans($ok=true)
74	{
75		if ($this->transOff) return true;
76
77		if (!$ok) return $this->RollbackTrans();
78
79		$this->transCnt -= 1;
80		$this->Execute('COMMIT TRAN');
81		return true;
82	}
83
84	function RollbackTrans()
85	{
86		if ($this->transOff) return true;
87		$this->transCnt -= 1;
88		$this->Execute('ROLLBACK TRAN');
89		return true;
90	}
91
92	// http://www.isug.com/Sybase_FAQ/ASE/section6.1.html#6.1.4
93	function RowLock($tables,$where,$col='top 1 null as ignore')
94	{
95		if (!$this->_hastrans) $this->BeginTrans();
96		$tables = str_replace(',',' HOLDLOCK,',$tables);
97		return $this->GetOne("select $col from $tables HOLDLOCK where $where");
98
99	}
100
101	function SelectDB($dbName)
102	{
103		$this->database = $dbName;
104		$this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
105		if ($this->_connectionID) {
106			return @sybase_select_db($dbName);
107		}
108		else return false;
109	}
110
111	/*	Returns: the last error message from previous database operation
112		Note: This function is NOT available for Microsoft SQL Server.	*/
113
114
115	function ErrorMsg()
116	{
117		if ($this->_logsql) return $this->_errorMsg;
118		if (function_exists('sybase_get_last_message'))
119			$this->_errorMsg = sybase_get_last_message();
120		else {
121			$this->_errorMsg = 'SYBASE error messages not supported on this platform';
122		}
123
124		return $this->_errorMsg;
125	}
126
127	// returns true or false
128	function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
129	{
130		if (!function_exists('sybase_connect')) return null;
131
132		// Sybase connection on custom port
133		if ($this->port) {
134			$argHostname .= ':' . $this->port;
135		}
136
137		if ($this->charSet) {
138			$this->_connectionID = @sybase_connect($argHostname,$argUsername,$argPassword, $this->charSet);
139		} else {
140			$this->_connectionID = @sybase_connect($argHostname,$argUsername,$argPassword);
141		}
142
143		if ($this->_connectionID === false) return false;
144		if ($argDatabasename) return $this->SelectDB($argDatabasename);
145		return true;
146	}
147
148	// returns true or false
149	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
150	{
151		if (!function_exists('sybase_connect')) return null;
152
153		// Sybase connection on custom port
154		if ($this->port) {
155			$argHostname .= ':' . $this->port;
156		}
157
158		if ($this->charSet) {
159			$this->_connectionID = @sybase_pconnect($argHostname,$argUsername,$argPassword, $this->charSet);
160		} else {
161			$this->_connectionID = @sybase_pconnect($argHostname,$argUsername,$argPassword);
162		}
163
164		if ($this->_connectionID === false) return false;
165		if ($argDatabasename) return $this->SelectDB($argDatabasename);
166		return true;
167	}
168
169	// returns query ID if successful, otherwise false
170	function _query($sql,$inputarr=false)
171	{
172	global $ADODB_COUNTRECS;
173
174		if ($ADODB_COUNTRECS == false && ADODB_PHPVER >= 0x4300)
175			return sybase_unbuffered_query($sql,$this->_connectionID);
176		else
177			return sybase_query($sql,$this->_connectionID);
178	}
179
180	// See http://www.isug.com/Sybase_FAQ/ASE/section6.2.html#6.2.12
181	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0)
182	{
183		if ($secs2cache > 0) {// we do not cache rowcount, so we have to load entire recordset
184			$rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
185			return $rs;
186		}
187
188		$nrows = (integer) $nrows;
189		$offset = (integer) $offset;
190
191		$cnt = ($nrows >= 0) ? $nrows : 999999999;
192		if ($offset > 0 && $cnt) $cnt += $offset;
193
194		$this->Execute("set rowcount $cnt");
195		$rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,0);
196		$this->Execute("set rowcount 0");
197
198		return $rs;
199	}
200
201	// returns true or false
202	function _close()
203	{
204		return @sybase_close($this->_connectionID);
205	}
206
207	static function UnixDate($v)
208	{
209		return ADORecordSet_array_sybase::UnixDate($v);
210	}
211
212	static function UnixTimeStamp($v)
213	{
214		return ADORecordSet_array_sybase::UnixTimeStamp($v);
215	}
216
217
218
219	# Added 2003-10-05 by Chris Phillipson
220	# Used ASA SQL Reference Manual -- http://sybooks.sybase.com/onlinebooks/group-aw/awg0800e/dbrfen8/@ebt-link;pt=16756?target=%25N%15_12018_START_RESTART_N%25
221	# to convert similar Microsoft SQL*Server (mssql) API into Sybase compatible version
222	// Format date column in sql string given an input format that understands Y M D
223	function SQLDate($fmt, $col=false)
224	{
225		if (!$col) $col = $this->sysTimeStamp;
226		$s = '';
227
228		$len = strlen($fmt);
229		for ($i=0; $i < $len; $i++) {
230			if ($s) $s .= '+';
231			$ch = $fmt[$i];
232			switch($ch) {
233			case 'Y':
234			case 'y':
235				$s .= "datename(yy,$col)";
236				break;
237			case 'M':
238				$s .= "convert(char(3),$col,0)";
239				break;
240			case 'm':
241				$s .= "str_replace(str(month($col),2),' ','0')";
242				break;
243			case 'Q':
244			case 'q':
245				$s .= "datename(qq,$col)";
246				break;
247			case 'D':
248			case 'd':
249				$s .= "str_replace(str(datepart(dd,$col),2),' ','0')";
250				break;
251			case 'h':
252				$s .= "substring(convert(char(14),$col,0),13,2)";
253				break;
254
255			case 'H':
256				$s .= "str_replace(str(datepart(hh,$col),2),' ','0')";
257				break;
258
259			case 'i':
260				$s .= "str_replace(str(datepart(mi,$col),2),' ','0')";
261				break;
262			case 's':
263				$s .= "str_replace(str(datepart(ss,$col),2),' ','0')";
264				break;
265			case 'a':
266			case 'A':
267				$s .= "substring(convert(char(19),$col,0),18,2)";
268				break;
269
270			default:
271				if ($ch == '\\') {
272					$i++;
273					$ch = substr($fmt,$i,1);
274				}
275				$s .= $this->qstr($ch);
276				break;
277			}
278		}
279		return $s;
280	}
281
282	# Added 2003-10-07 by Chris Phillipson
283	# Used ASA SQL Reference Manual -- http://sybooks.sybase.com/onlinebooks/group-aw/awg0800e/dbrfen8/@ebt-link;pt=5981;uf=0?target=0;window=new;showtoc=true;book=dbrfen8
284	# to convert similar Microsoft SQL*Server (mssql) API into Sybase compatible version
285	function MetaPrimaryKeys($table, $owner = false)
286	{
287		$sql = "SELECT c.column_name " .
288			   "FROM syscolumn c, systable t " .
289			   "WHERE t.table_name='$table' AND c.table_id=t.table_id " .
290			   "AND t.table_type='BASE' " .
291			   "AND c.pkey = 'Y' " .
292			   "ORDER BY c.column_id";
293
294		$a = $this->GetCol($sql);
295		if ($a && sizeof($a)>0) return $a;
296		return false;
297	}
298}
299
300/*--------------------------------------------------------------------------------------
301	 Class Name: Recordset
302--------------------------------------------------------------------------------------*/
303global $ADODB_sybase_mths;
304$ADODB_sybase_mths = array(
305	'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
306	'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
307
308class ADORecordset_sybase extends ADORecordSet {
309
310	var $databaseType = "sybase";
311	var $canSeek = true;
312	// _mths works only in non-localised system
313	var  $_mths = array('JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
314
315	function __construct($id,$mode=false)
316	{
317		if ($mode === false) {
318			global $ADODB_FETCH_MODE;
319			$mode = $ADODB_FETCH_MODE;
320		}
321		if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC;
322		else $this->fetchMode = $mode;
323		parent::__construct($id,$mode);
324	}
325
326	/*	Returns: an object containing field information.
327		Get column information in the Recordset object. fetchField() can be used in order to obtain information about
328		fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
329		fetchField() is retrieved.	*/
330	function FetchField($fieldOffset = -1)
331	{
332		if ($fieldOffset != -1) {
333			$o = @sybase_fetch_field($this->_queryID, $fieldOffset);
334		}
335		else if ($fieldOffset == -1) {	/*	The $fieldOffset argument is not provided thus its -1 	*/
336			$o = @sybase_fetch_field($this->_queryID);
337		}
338		// older versions of PHP did not support type, only numeric
339		if ($o && !isset($o->type)) $o->type = ($o->numeric) ? 'float' : 'varchar';
340		return $o;
341	}
342
343	function _initrs()
344	{
345	global $ADODB_COUNTRECS;
346		$this->_numOfRows = ($ADODB_COUNTRECS)? @sybase_num_rows($this->_queryID):-1;
347		$this->_numOfFields = @sybase_num_fields($this->_queryID);
348	}
349
350	function _seek($row)
351	{
352		return @sybase_data_seek($this->_queryID, $row);
353	}
354
355	function _fetch($ignore_fields=false)
356	{
357		if ($this->fetchMode == ADODB_FETCH_NUM) {
358			$this->fields = @sybase_fetch_row($this->_queryID);
359		} else if ($this->fetchMode == ADODB_FETCH_ASSOC) {
360			$this->fields = @sybase_fetch_assoc($this->_queryID);
361
362			if (is_array($this->fields)) {
363				$this->fields = $this->GetRowAssoc();
364				return true;
365			}
366			return false;
367		}  else {
368			$this->fields = @sybase_fetch_array($this->_queryID);
369		}
370		if ( is_array($this->fields)) {
371			return true;
372		}
373
374		return false;
375	}
376
377	/*	close() only needs to be called if you are worried about using too much memory while your script
378		is running. All associated result memory for the specified result identifier will automatically be freed.	*/
379	function _close() {
380		return @sybase_free_result($this->_queryID);
381	}
382
383	// sybase/mssql uses a default date like Dec 30 2000 12:00AM
384	static function UnixDate($v)
385	{
386		return ADORecordSet_array_sybase::UnixDate($v);
387	}
388
389	static function UnixTimeStamp($v)
390	{
391		return ADORecordSet_array_sybase::UnixTimeStamp($v);
392	}
393}
394
395class ADORecordSet_array_sybase extends ADORecordSet_array {
396	function __construct($id=-1)
397	{
398		parent::__construct($id);
399	}
400
401		// sybase/mssql uses a default date like Dec 30 2000 12:00AM
402	static function UnixDate($v)
403	{
404	global $ADODB_sybase_mths;
405
406		//Dec 30 2000 12:00AM
407		if (!preg_match( "/([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})/"
408			,$v, $rr)) return parent::UnixDate($v);
409
410		if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
411
412		$themth = substr(strtoupper($rr[1]),0,3);
413		$themth = $ADODB_sybase_mths[$themth];
414		if ($themth <= 0) return false;
415		// h-m-s-MM-DD-YY
416		return  adodb_mktime(0,0,0,$themth,$rr[2],$rr[3]);
417	}
418
419	static function UnixTimeStamp($v)
420	{
421	global $ADODB_sybase_mths;
422		//11.02.2001 Toni Tunkkari toni.tunkkari@finebyte.com
423		//Changed [0-9] to [0-9 ] in day conversion
424		if (!preg_match( "/([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})/"
425			,$v, $rr)) return parent::UnixTimeStamp($v);
426		if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
427
428		$themth = substr(strtoupper($rr[1]),0,3);
429		$themth = $ADODB_sybase_mths[$themth];
430		if ($themth <= 0) return false;
431
432		switch (strtoupper($rr[6])) {
433		case 'P':
434			if ($rr[4]<12) $rr[4] += 12;
435			break;
436		case 'A':
437			if ($rr[4]==12) $rr[4] = 0;
438			break;
439		default:
440			break;
441		}
442		// h-m-s-MM-DD-YY
443		return  adodb_mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]);
444	}
445}
446