1 |
<?php |
2 |
// |
3 |
// +----------------------------------------------------------------------+ |
4 |
// | PHP Version 4 | |
5 |
// +----------------------------------------------------------------------+ |
6 |
// | Copyright (c) 1997-2002 The PHP Group | |
7 |
// +----------------------------------------------------------------------+ |
8 |
// | This source file is subject to version 2.02 of the PHP license, | |
9 |
// | that is bundled with this package in the file LICENSE, and is | |
10 |
// | available at through the world-wide-web at | |
11 |
// | http://www.php.net/license/2_02.txt. | |
12 |
// | If you did not receive a copy of the PHP license and are unable to | |
13 |
// | obtain it through the world-wide-web, please send a note to | |
14 |
// | license@php.net so we can mail you a copy immediately. | |
15 |
// +----------------------------------------------------------------------+ |
16 |
// | Author: Sterling Hughes <sterling@php.net> | |
17 |
// +----------------------------------------------------------------------+ |
18 |
// |
19 |
// $Id: ibase.php,v 1.36.2.2 2002/04/09 19:04:13 ssb Exp $ |
20 |
// |
21 |
// Database independent query interface definition for PHP's Interbase |
22 |
// extension. |
23 |
// |
24 |
|
25 |
require_once 'DB/common.php'; |
26 |
|
27 |
class DB_ibase extends DB_common |
28 |
{ |
29 |
var $connection; |
30 |
var $phptype, $dbsyntax; |
31 |
var $autocommit = 1; |
32 |
var $manip_query = array(); |
33 |
|
34 |
function DB_ibase() |
35 |
{ |
36 |
$this->DB_common(); |
37 |
$this->phptype = 'ibase'; |
38 |
$this->dbsyntax = 'ibase'; |
39 |
$this->features = array( |
40 |
'prepare' => true, |
41 |
'pconnect' => true, |
42 |
'transactions' => true, |
43 |
'limit' => false |
44 |
); |
45 |
// just a few of the tons of Interbase error codes listed in the |
46 |
// Language Reference section of the Interbase manual |
47 |
$this->errorcode_map = array( |
48 |
-104 => DB_ERROR_SYNTAX, |
49 |
-150 => DB_ERROR_ACCESS_VIOLATION, |
50 |
-151 => DB_ERROR_ACCESS_VIOLATION, |
51 |
-155 => DB_ERROR_NOSUCHTABLE, |
52 |
-157 => DB_ERROR_NOSUCHFIELD, |
53 |
-158 => DB_ERROR_VALUE_COUNT_ON_ROW, |
54 |
-170 => DB_ERROR_MISMATCH, |
55 |
-171 => DB_ERROR_MISMATCH, |
56 |
-172 => DB_ERROR_INVALID, |
57 |
-204 => DB_ERROR_INVALID, |
58 |
-205 => DB_ERROR_NOSUCHFIELD, |
59 |
-206 => DB_ERROR_NOSUCHFIELD, |
60 |
-208 => DB_ERROR_INVALID, |
61 |
-219 => DB_ERROR_NOSUCHTABLE, |
62 |
-297 => DB_ERROR_CONSTRAINT, |
63 |
-530 => DB_ERROR_CONSTRAINT, |
64 |
-803 => DB_ERROR_CONSTRAINT, |
65 |
-551 => DB_ERROR_ACCESS_VIOLATION, |
66 |
-552 => DB_ERROR_ACCESS_VIOLATION, |
67 |
-922 => DB_ERROR_NOSUCHDB, |
68 |
-923 => DB_ERROR_CONNECT_FAILED, |
69 |
-924 => DB_ERROR_CONNECT_FAILED |
70 |
); |
71 |
} |
72 |
|
73 |
function connect($dsninfo, $persistent = false) |
74 |
{ |
75 |
if (!DB::assertExtension('interbase')) { |
76 |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
77 |
} |
78 |
$this->dsn = $dsninfo; |
79 |
$user = $dsninfo['username']; |
80 |
$pw = $dsninfo['password']; |
81 |
$dbhost = $dsninfo['hostspec'] ? |
82 |
($dsninfo['hostspec'] . ':/' . $dsninfo['database']) : |
83 |
$dsninfo['database']; |
84 |
|
85 |
$connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; |
86 |
|
87 |
$params = array(); |
88 |
$params[] = $dbhost; |
89 |
$params[] = !empty($user) ? $user : null; |
90 |
$params[] = !empty($pw) ? $pw : null; |
91 |
$params[] = isset($dsninfo['charset']) ? $dsninfo['charset'] : null; |
92 |
$params[] = isset($dsninfo['buffers']) ? $dsninfo['buffers'] : null; |
93 |
$params[] = isset($dsninfo['dialect']) ? $dsninfo['dialect'] : null; |
94 |
$params[] = isset($dsninfo['role']) ? $dsninfo['role'] : null; |
95 |
|
96 |
/* |
97 |
if ($dbhost && $user && $pw) { |
98 |
$conn = $connect_function($dbhost, $user, $pw); |
99 |
} elseif ($dbhost && $user) { |
100 |
$conn = $connect_function($dbhost, $user); |
101 |
} elseif ($dbhost) { |
102 |
$conn = $connect_function($dbhost); |
103 |
} else { |
104 |
return $this->raiseError("no host, user or password"); |
105 |
} |
106 |
*/ |
107 |
$conn = @call_user_func_array($connect_function, $params); |
108 |
if (!$conn) { |
109 |
return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); |
110 |
} |
111 |
$this->connection = $conn; |
112 |
return DB_OK; |
113 |
} |
114 |
|
115 |
function disconnect() |
116 |
{ |
117 |
$ret = @ibase_close($this->connection); |
118 |
$this->connection = null; |
119 |
return $ret; |
120 |
} |
121 |
|
122 |
function simpleQuery($query) |
123 |
{ |
124 |
$ismanip = DB::isManip($query); |
125 |
$this->last_query = $query; |
126 |
$query = $this->modifyQuery($query); |
127 |
$result = @ibase_query($this->connection, $query); |
128 |
if (!$result) { |
129 |
return $this->ibaseRaiseError(); |
130 |
} |
131 |
if ($this->autocommit && $ismanip) { |
132 |
ibase_commit($this->connection); |
133 |
} |
134 |
// Determine which queries that should return data, and which |
135 |
// should return an error code only. |
136 |
return DB::isManip($query) ? DB_OK : $result; |
137 |
} |
138 |
|
139 |
// {{{ modifyLimitQuery() |
140 |
|
141 |
/** |
142 |
* This method is used by backends to alter limited queries |
143 |
* Uses the new FIRST n SKIP n Firebird 1.0 syntax, so it is |
144 |
* only compatible with Firebird 1.x |
145 |
* |
146 |
* @param string $query query to modify |
147 |
* @param integer $from the row to start to fetching |
148 |
* @param integer $count the numbers of rows to fetch |
149 |
* |
150 |
* @return the new (modified) query |
151 |
* @author Ludovico Magnocavallo <ludo@sumatrasolutions.com> |
152 |
* @access private |
153 |
*/ |
154 |
|
155 |
function modifyLimitQuery($query, $from, $count) |
156 |
{ |
157 |
if ($this->dsn['dbsyntax'] == 'firebird') { |
158 |
$from++; // SKIP starts from 1, ie SKIP 1 starts from the first record |
159 |
$query = preg_replace('/^\s*select\s(.*)$/is', |
160 |
"SELECT FIRST $count SKIP $from $1", $query); |
161 |
} |
162 |
return $query; |
163 |
} |
164 |
|
165 |
// }}} |
166 |
|
167 |
|
168 |
// {{{ nextResult() |
169 |
|
170 |
/** |
171 |
* Move the internal ibase result pointer to the next available result |
172 |
* |
173 |
* @param a valid fbsql result resource |
174 |
* |
175 |
* @access public |
176 |
* |
177 |
* @return true if a result is available otherwise return false |
178 |
*/ |
179 |
function nextResult($result) |
180 |
{ |
181 |
return false; |
182 |
} |
183 |
|
184 |
// }}} |
185 |
|
186 |
function fetchInto($result, &$ar, $fetchmode, $rownum = null) |
187 |
{ |
188 |
if ($rownum !== NULL) { |
189 |
return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); |
190 |
} |
191 |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
192 |
$ar = get_object_vars(ibase_fetch_object($result)); |
193 |
if ($ar && $this->options['optimize'] == 'portability') { |
194 |
$ar = array_change_key_case($ar, CASE_LOWER); |
195 |
} |
196 |
} else { |
197 |
$ar = ibase_fetch_row($result); |
198 |
} |
199 |
if (!$ar) { |
200 |
if ($errmsg = ibase_errmsg()) { |
201 |
return $this->ibaseRaiseError(null, $errmsg); |
202 |
} else { |
203 |
return null; |
204 |
} |
205 |
} |
206 |
return DB_OK; |
207 |
} |
208 |
|
209 |
function freeResult() |
210 |
{ |
211 |
if (is_resource($result)) { |
212 |
return ibase_free_result($result); |
213 |
} |
214 |
if (!isset($this->prepare_tokens[(int)$result])) { |
215 |
return false; |
216 |
} |
217 |
unset($this->prepare_tokens[(int)$result]); |
218 |
unset($this->prepare_types[(int)$result]); |
219 |
return true; |
220 |
} |
221 |
|
222 |
function freeQuery($query) |
223 |
{ |
224 |
ibase_free_query($query); |
225 |
return true; |
226 |
} |
227 |
|
228 |
function numCols($result) |
229 |
{ |
230 |
$cols = ibase_num_fields($result); |
231 |
if (!$cols) { |
232 |
return $this->ibaseRaiseError(); |
233 |
} |
234 |
return $cols; |
235 |
} |
236 |
|
237 |
function prepare($query) |
238 |
{ |
239 |
$this->last_query = $query; |
240 |
$query = $this->modifyQuery($query); |
241 |
$stmt = ibase_prepare($query); |
242 |
$this->manip_query[(int)$stmt] = DB::isManip($query); |
243 |
return $stmt; |
244 |
} |
245 |
|
246 |
function execute($stmt, $data = false) |
247 |
{ |
248 |
$result = ibase_execute($stmt, $data); |
249 |
if (!$result) { |
250 |
return $this->ibaseRaiseError(); |
251 |
} |
252 |
if ($this->autocommit) { |
253 |
ibase_commit($this->connection); |
254 |
} |
255 |
return DB::isManip($this->manip_query[(int)$stmt]) ? DB_OK : new DB_result($this, $result); |
256 |
} |
257 |
|
258 |
function autoCommit($onoff = false) |
259 |
{ |
260 |
$this->autocommit = $onoff ? 1 : 0; |
261 |
return DB_OK; |
262 |
} |
263 |
|
264 |
function commit() |
265 |
{ |
266 |
return ibase_commit($this->connection); |
267 |
} |
268 |
|
269 |
function rollback($trans_number) |
270 |
{ |
271 |
return ibase_rollback($this->connection, $trans_number); |
272 |
} |
273 |
|
274 |
function transactionInit($trans_args = 0) |
275 |
{ |
276 |
return $trans_args ? ibase_trans($trans_args, $this->connection) : ibase_trans(); |
277 |
} |
278 |
|
279 |
// {{{ nextId() |
280 |
/** |
281 |
* Get the next value in a sequence. |
282 |
* |
283 |
* If the sequence does not exist, it will be created, |
284 |
* unless $ondemand is false. |
285 |
* |
286 |
* @access public |
287 |
* @param string $seq_name the name of the sequence |
288 |
* @param bool $ondemand whether to create the sequence on demand |
289 |
* @return a sequence integer, or a DB error |
290 |
*/ |
291 |
function nextId($seq_name, $ondemand = true) |
292 |
{ |
293 |
$sqn = strtoupper(preg_replace('/[^a-z0-9_]/i', '_', $seq_name)); |
294 |
$repeat = 0; |
295 |
do { |
296 |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
297 |
$result = $this->query("SELECT GEN_ID(${sqn}_SEQ, 1) FROM RDB\$GENERATORS" |
298 |
." WHERE RDB\$GENERATOR_NAME='${sqn}_SEQ'"); |
299 |
$this->popErrorHandling(); |
300 |
if ($ondemand && DB::isError($result)) { |
301 |
$repeat = 1; |
302 |
$result = $this->createSequence($seq_name); |
303 |
if (DB::isError($result)) { |
304 |
return $result; |
305 |
} |
306 |
} else { |
307 |
$repeat = 0; |
308 |
} |
309 |
} while ($repeat); |
310 |
if (DB::isError($result)) { |
311 |
return $result; |
312 |
} |
313 |
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
314 |
$result->free(); |
315 |
return $arr[0]; |
316 |
} |
317 |
|
318 |
// }}} |
319 |
// {{{ createSequence() |
320 |
|
321 |
/** |
322 |
* Create the sequence |
323 |
* |
324 |
* @param string $seq_name the name of the sequence |
325 |
* @return mixed DB_OK on success or DB error on error |
326 |
* @access public |
327 |
*/ |
328 |
function createSequence($seq_name) |
329 |
{ |
330 |
$sqn = strtoupper(preg_replace('/[^a-z0-9_]/i', '_', $seq_name)); |
331 |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
332 |
$result = $this->query("CREATE GENERATOR ${sqn}_SEQ"); |
333 |
$this->popErrorHandling(); |
334 |
|
335 |
return $result; |
336 |
} |
337 |
|
338 |
// }}} |
339 |
// {{{ dropSequence() |
340 |
|
341 |
/** |
342 |
* Drop a sequence |
343 |
* |
344 |
* @param string $seq_name the name of the sequence |
345 |
* @return mixed DB_OK on success or DB error on error |
346 |
* @access public |
347 |
*/ |
348 |
function dropSequence($seq_name) |
349 |
{ |
350 |
$sqn = strtoupper(preg_replace('/[^a-z0-9_]/i', '_', $seq_name)); |
351 |
return $this->query("DELETE FROM RDB\$GENERATORS WHERE RDB\$GENERATOR_NAME='${sqn}_SEQ'"); |
352 |
} |
353 |
|
354 |
// }}} |
355 |
// {{{ _ibaseFieldFlags() |
356 |
|
357 |
/** |
358 |
* get the Flags of a Field |
359 |
* |
360 |
* @param string $field_name the name of the field |
361 |
* @param string $table_name the name of the table |
362 |
* |
363 |
* @return string The flags of the field ("primary_key", "unique_key", "not_null" |
364 |
* "default", "computed" and "blob" are supported) |
365 |
* @access private |
366 |
*/ |
367 |
function _ibaseFieldFlags($field_name, $table_name) |
368 |
{ |
369 |
|
370 |
$sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' |
371 |
.' FROM RDB$INDEX_SEGMENTS I' |
372 |
.' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' |
373 |
.' WHERE I.RDB$FIELD_NAME=\''.$field_name.'\'' |
374 |
.' AND R.RDB$RELATION_NAME=\''.$table_name.'\''; |
375 |
$result = ibase_query($this->connection, $sql); |
376 |
if (empty($result)) { |
377 |
return $this->ibaseRaiseError(); |
378 |
} |
379 |
if ($obj = @ibase_fetch_object($result)) { |
380 |
ibase_free_result($result); |
381 |
if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { |
382 |
$flags = 'primary_key '; |
383 |
} |
384 |
if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { |
385 |
$flags .= 'unique_key '; |
386 |
} |
387 |
} |
388 |
|
389 |
$sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' |
390 |
.' R.RDB$DEFAULT_SOURCE AS DSOURCE,' |
391 |
.' F.RDB$FIELD_TYPE AS FTYPE,' |
392 |
.' F.RDB$COMPUTED_SOURCE AS CSOURCE' |
393 |
.' FROM RDB$RELATION_FIELDS R ' |
394 |
.' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' |
395 |
.' WHERE R.RDB$RELATION_NAME=\''.$table_name.'\'' |
396 |
.' AND R.RDB$FIELD_NAME=\''.$field_name.'\''; |
397 |
$result = ibase_query($this->connection, $sql); |
398 |
if (empty($result)) { |
399 |
return $this->ibaseRaiseError(); |
400 |
} |
401 |
if ($obj = @ibase_fetch_object($result)) { |
402 |
ibase_free_result($result); |
403 |
if (isset($obj->NFLAG)) { |
404 |
$flags .= 'not_null '; |
405 |
} |
406 |
if (isset($obj->DSOURCE)) { |
407 |
$flags .= 'default '; |
408 |
} |
409 |
if (isset($obj->CSOURCE)) { |
410 |
$flags .= 'computed '; |
411 |
} |
412 |
if (isset($obj->FTYPE) && $obj->FTYPE == 261) { |
413 |
$flags .= 'blob '; |
414 |
} |
415 |
} |
416 |
|
417 |
return trim($flags); |
418 |
} |
419 |
|
420 |
// }}} |
421 |
// {{{ tableInfo() |
422 |
|
423 |
/** |
424 |
* Returns information about a table or a result set |
425 |
* |
426 |
* NOTE: doesn't support 'flags'and 'table' if called from a db_result |
427 |
* |
428 |
* @param mixed $resource Interbase result identifier or table name |
429 |
* @param int $mode A valid tableInfo mode (DB_TABLEINFO_ORDERTABLE or |
430 |
* DB_TABLEINFO_ORDER) |
431 |
* |
432 |
* @return array An array with all the information |
433 |
*/ |
434 |
function tableInfo($result, $mode = null) |
435 |
{ |
436 |
$count = 0; |
437 |
$id = 0; |
438 |
$res = array(); |
439 |
|
440 |
/* |
441 |
* depending on $mode, metadata returns the following values: |
442 |
* |
443 |
* - mode is false (default): |
444 |
* $result[]: |
445 |
* [0]["table"] table name |
446 |
* [0]["name"] field name |
447 |
* [0]["type"] field type |
448 |
* [0]["len"] field length |
449 |
* [0]["flags"] field flags |
450 |
* |
451 |
* - mode is DB_TABLEINFO_ORDER |
452 |
* $result[]: |
453 |
* ["num_fields"] number of metadata records |
454 |
* [0]["table"] table name |
455 |
* [0]["name"] field name |
456 |
* [0]["type"] field type |
457 |
* [0]["len"] field length |
458 |
* [0]["flags"] field flags |
459 |
* ["order"][field name] index of field named "field name" |
460 |
* The last one is used, if you have a field name, but no index. |
461 |
* Test: if (isset($result['meta']['myfield'])) { ... |
462 |
* |
463 |
* - mode is DB_TABLEINFO_ORDERTABLE |
464 |
* the same as above. but additionally |
465 |
* ["ordertable"][table name][field name] index of field |
466 |
* named "field name" |
467 |
* |
468 |
* this is, because if you have fields from different |
469 |
* tables with the same field name * they override each |
470 |
* other with DB_TABLEINFO_ORDER |
471 |
* |
472 |
* you can combine DB_TABLEINFO_ORDER and |
473 |
* DB_TABLEINFO_ORDERTABLE with DB_TABLEINFO_ORDER | |
474 |
* DB_TABLEINFO_ORDERTABLE * or with DB_TABLEINFO_FULL |
475 |
*/ |
476 |
|
477 |
// if $result is a string, then we want information about a |
478 |
// table without a resultset |
479 |
|
480 |
if (is_string($result)) { |
481 |
$id = ibase_query($this->connection,"SELECT * FROM $result"); |
482 |
if (empty($id)) { |
483 |
return $this->ibaseRaiseError(); |
484 |
} |
485 |
} else { // else we want information about a resultset |
486 |
$id = $result; |
487 |
if (empty($id)) { |
488 |
return $this->ibaseRaiseError(); |
489 |
} |
490 |
} |
491 |
|
492 |
$count = @ibase_num_fields($id); |
493 |
|
494 |
// made this IF due to performance (one if is faster than $count if's) |
495 |
if (empty($mode)) { |
496 |
|
497 |
for ($i=0; $i<$count; $i++) { |
498 |
$info = @ibase_field_info($id, $i); |
499 |
$res[$i]['table'] = (is_string($result)) ? $result : ''; |
500 |
$res[$i]['name'] = $info['name']; |
501 |
$res[$i]['type'] = $info['type']; |
502 |
$res[$i]['len'] = $info['length']; |
503 |
$res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; |
504 |
} |
505 |
|
506 |
} else { // full |
507 |
$res["num_fields"]= $count; |
508 |
|
509 |
for ($i=0; $i<$count; $i++) { |
510 |
$info = @ibase_field_info($id, $i); |
511 |
$res[$i]['table'] = (is_string($result)) ? $result : ''; |
512 |
$res[$i]['name'] = $info['name']; |
513 |
$res[$i]['type'] = $info['type']; |
514 |
$res[$i]['len'] = $info['length']; |
515 |
$res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; |
516 |
if ($mode & DB_TABLEINFO_ORDER) { |
517 |
$res['order'][$res[$i]['name']] = $i; |
518 |
} |
519 |
if ($mode & DB_TABLEINFO_ORDERTABLE) { |
520 |
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
521 |
} |
522 |
} |
523 |
} |
524 |
|
525 |
// free the result only if we were called on a table |
526 |
if (is_resource($id)) { |
527 |
ibase_free_result($id); |
528 |
} |
529 |
return $res; |
530 |
} |
531 |
|
532 |
// }}} |
533 |
// {{{ getSpecialQuery() |
534 |
|
535 |
/** |
536 |
* Returns the query needed to get some backend info |
537 |
* @param string $type What kind of info you want to retrieve |
538 |
* @return string The SQL query string |
539 |
*/ |
540 |
function getSpecialQuery($type) |
541 |
{ |
542 |
switch ($type) { |
543 |
case 'tables': |
544 |
default: |
545 |
return null; |
546 |
} |
547 |
return $sql; |
548 |
} |
549 |
|
550 |
// }}} |
551 |
// {{{ ibaseRaiseError() |
552 |
|
553 |
function ibaseRaiseError($errno = null, $errmsg = null) |
554 |
{ |
555 |
if ($errmsg === null) |
556 |
$errmsg = ibase_errmsg(); |
557 |
// memo for the interbase php module hackers: we need something similar |
558 |
// to mysql_errno() to retrieve error codes instead of this ugly hack |
559 |
if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $errmsg, $m)) { |
560 |
if ($errno === null) { |
561 |
$ibase_errno = (int)$m[2]; |
562 |
// try to interpret Interbase error code (that's why we need ibase_errno() |
563 |
// in the interbase module to return the real error code) |
564 |
switch ($ibase_errno) { |
565 |
case -204: |
566 |
if (is_int(strpos($m[3], 'Table unknown'))) { |
567 |
$errno = DB_ERROR_NOSUCHTABLE; |
568 |
} |
569 |
break; |
570 |
default: |
571 |
$errno = $this->errorCode($ibase_errno); |
572 |
} |
573 |
} |
574 |
$errmsg = $m[2] . ' ' . $m[3]; |
575 |
} |
576 |
|
577 |
return $this->raiseError($errno, null, null, $errmsg, |
578 |
$this->last_query); |
579 |
} |
580 |
|
581 |
// }}} |
582 |
|
583 |
} |
584 |
|
585 |
/* |
586 |
* Local variables: |
587 |
* tab-width: 4 |
588 |
* c-basic-offset: 4 |
589 |
* End: |
590 |
*/ |
591 |
|
592 |
?> |