3 * DbSimple_Ibase: Interbase/Firebird database.
4 * (C) Dk Lab, http://en.dklab.ru
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 * See http://www.gnu.org/copyleft/lesser.html
12 * Placeholders are emulated because of logging purposes.
14 * @author Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/
15 * @author Konstantin Zhinko, http://forum.dklab.ru/users/KonstantinGinkoTit/
17 * @version 2.x $Id: Ibase.php 221 2007-07-27 23:24:35Z dk $
19 require_once dirname(__FILE__
) . '/Generic.php';
22 * Best transaction parameters for script queries.
23 * They never give us update conflicts (unlike others)!
26 define('IBASE_BEST_TRANSACTION', IBASE_COMMITTED + IBASE_WAIT + IBASE_REC_VERSION
);
27 define('IBASE_BEST_FETCH', IBASE_UNIXTIME
);
30 * Database class for Interbase/Firebird.
33 class DbSimple_Ibase
extends DbSimple_Generic_Database
35 var $DbSimple_Ibase_BEST_TRANSACTION = IBASE_BEST_TRANSACTION
;
36 var $DbSimple_Ibase_USE_NATIVE_PHOLDERS = true;
37 var $fetchFlags = IBASE_BEST_FETCH
;
40 var $prepareCache = array();
43 * constructor(string $dsn)
44 * Connect to Interbase/Firebird.
46 function DbSimple_Ibase($dsn)
48 $p = DbSimple_Generic
::parseDSN($dsn);
49 if (!is_callable('ibase_connect')) {
50 return $this->_setLastError("-1", "Interbase/Firebird extension is not loaded", "ibase_connect");
52 $ok = $this->link
= ibase_connect(
53 $p['host'] . (empty($p['port'])?
"" : ":".$p['port']) .':'.preg_replace('{^/}s', '', $p['path']),
56 isset($p['CHARSET']) ?
$p['CHARSET'] : 'win1251',
57 isset($p['BUFFERS']) ?
$p['BUFFERS'] : 0,
58 isset($p['DIALECT']) ?
$p['DIALECT'] : 3,
59 isset($p['ROLE']) ?
$p['ROLE'] : ''
61 if (isset($p['TRANSACTION'])) $this->DbSimple_Ibase_BEST_TRANSACTION
= eval($p['TRANSACTION'].";");
62 $this->_resetLastError();
63 if (!$ok) return $this->_setDbError('ibase_connect()');
66 function _performEscape($s, $isIdent=false)
69 return "'" . str_replace("'", "''", $s) . "'";
71 return '"' . str_replace('"', '_', $s) . '"';
74 function _performTransaction($parameters=null)
76 if ($parameters === null) $parameters = $this->DbSimple_Ibase_BEST_TRANSACTION
;
77 $this->trans
= @ibase_trans
($parameters, $this->link
);
80 function& _performNewBlob($blobid=null)
82 $obj =& new DbSimple_Ibase_Blob($this, $blobid);
86 function _performGetBlobFieldNames($result)
88 $blobFields = array();
89 for ($i=ibase_num_fields($result)-1; $i>=0; $i--) {
90 $info = ibase_field_info($result, $i);
91 if ($info['type'] === "BLOB") $blobFields[] = $info['name'];
96 function _performGetPlaceholderIgnoreRe()
99 " (?> [^"\\\\]+|\\\\"|\\\\)* " |
100 \' (?> [^\'\\\\]+|\\\\\'|\\\\)* \' |
101 ` (?> [^`]+ | ``)* ` | # backticks
102 /\* .*? \*/ # comments
106 function _performCommit()
108 if (!is_resource($this->trans
)) return false;
109 $result = @ibase_commit
($this->trans
);
110 if (true === $result) {
117 function _performRollback()
119 if (!is_resource($this->trans
)) return false;
120 $result = @ibase_rollback
($this->trans
);
121 if (true === $result) {
127 function _performTransformQuery(&$queryMain, $how)
129 // If we also need to calculate total number of found rows...
131 // Prepare total calculation (if possible)
136 // Perform total calculation.
138 // TODO: GROUP BY ... -> COUNT(DISTINCT ...)
140 (?> -- [^\r\n]* | \s+)*
142 ((?:FIRST \s+ \S+ \s+ (?:SKIP \s+ \S+ \s+)? )?) #2
144 (\s+ FROM \s+ .*?) #4
145 ((?:\s+ ORDER \s+ BY \s+ .*)?) #5
148 if (preg_match($re, $queryMain[0], $m)) {
149 $queryMain[0] = $m[1] . $this->_fieldList2Count($m[3]) . " AS C" . $m[4];
150 $skipHead = substr_count($m[2], '?');
151 if ($skipHead) array_splice($queryMain, 1, $skipHead);
152 $skipTail = substr_count($m[5], '?');
153 if ($skipTail) array_splice($queryMain, -$skipTail);
161 function _performQuery($queryMain)
163 $this->_lastQuery
= $queryMain;
164 $this->_expandPlaceholders($queryMain, $this->DbSimple_Ibase_USE_NATIVE_PHOLDERS
);
166 $hash = $queryMain[0];
168 if (!isset($this->prepareCache
[$hash])) {
169 $this->prepareCache
[$hash] = @ibase_prepare
((is_resource($this->trans
) ?
$this->trans
: $this->link
), $queryMain[0]);
171 // Prepare cache hit!
174 $prepared = $this->prepareCache
[$hash];
175 if (!$prepared) return $this->_setDbError($queryMain[0]);
176 $queryMain[0] = $prepared;
177 $result = @call_user_func_array
('ibase_execute', $queryMain);
179 // WE MUST save prepared ID (stored in $prepared variable) somewhere
180 // before returning $result because of ibase destructor. Now it is done
181 // by $this->prepareCache. When variable $prepared goes out of scope, it
182 // is destroyed, and memory for result also freed by PHP. Totally we
183 // got "Invalud statement handle" error message.
185 if ($result === false) return $this->_setDbError($queryMain[0]);
186 if (!is_resource($result)) {
187 // Non-SELECT queries return number of affected rows, SELECT - resource.
188 return @ibase_affected_rows
((is_resource($this->trans
) ?
$this->trans
: $this->link
));
193 function _performFetch($result)
195 // Select fetch mode.
196 $flags = $this->fetchFlags
;
197 if (empty($this->attributes
['BLOB_OBJ'])) $flags = $flags | IBASE_TEXT
;
198 else $flags = $flags & ~IBASE_TEXT
;
200 $row = @ibase_fetch_assoc
($result, $flags);
201 if (ibase_errmsg()) return $this->_setDbError($this->_lastQuery
);
202 if ($row === false) return null;
207 function _setDbError($query)
209 return $this->_setLastError(ibase_errcode(), ibase_errmsg(), $query);
214 class DbSimple_Ibase_Blob
extends DbSimple_Generic_Blob
216 var $blob; // resourse link
220 function DbSimple_Ibase_Blob(&$database, $id=null)
222 $this->database
=& $database;
229 if ($this->id
=== false) return ''; // wr-only blob
230 if (!($e=$this->_firstUse())) return $e;
231 $data = @ibase_blob_get
($this->blob
, $len);
232 if ($data === false) return $this->_setDbError('read');
236 function write($data)
238 if (!($e=$this->_firstUse())) return $e;
239 $ok = @ibase_blob_add
($this->blob
, $data);
240 if ($ok === false) return $this->_setDbError('add data to');
246 if (!($e=$this->_firstUse())) return $e;
248 $id = @ibase_blob_close
($this->blob
);
249 if ($id === false) return $this->_setDbError('close');
254 return $this->id ?
$this->id
: $id;
259 if ($this->id
=== false) return 0; // wr-only blob
260 if (!($e=$this->_firstUse())) return $e;
261 $info = @ibase_blob_info
($this->id
);
262 if (!$info) return $this->_setDbError('get length of');
266 function _setDbError($query)
268 $hId = $this->id
=== null ?
"null" : ($this->id
=== false ?
"false" : $this->id
);
269 $query = "-- $query BLOB $hId";
270 $this->database
->_setDbError($query);
273 // Called on each blob use (reading or writing).
276 // BLOB is opened - nothing to do.
277 if (is_resource($this->blob
)) return true;
278 // Open or create blob.
279 if ($this->id
!== null) {
280 $this->blob
= @ibase_blob_open
($this->id
);
281 if ($this->blob
=== false) return $this->_setDbError('open');
283 $this->blob
= @ibase_blob_create
($this->database
->link
);
284 if ($this->blob
=== false) return $this->_setDbError('create');