]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/odbc.php
new PEAR DB 1.3 backends plus the experimental PEAR DB_Pager
[SourceForge/phpwiki.git] / lib / pear / DB / odbc.php
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: Stig Bakken <ssb@fast.no>                                    |
17 // +----------------------------------------------------------------------+
18 //
19 // $Id: odbc.php,v 1.1 2002-09-12 11:47:07 rurban Exp $
20 //
21 // Database independent query interface definition for PHP's ODBC
22 // extension.
23 //
24
25 //
26 // XXX legend:
27 //  More info on ODBC errors could be found here:
28 //  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
29 //
30 // XXX ERRORMSG: The error message from the odbc function should
31 //                 be registered here.
32 //
33 // Based on DB 1.3 from the pear.php.net repository. 
34 // The only modifications made have been modification of the include paths. 
35 //
36 rcs_id('$Id: odbc.php,v 1.1 2002-09-12 11:47:07 rurban Exp $');
37 rcs_id('From Pear CVS: Id: odbc.php,v 1.3 2002/05/09 12:29:53 ssb Exp');
38
39 require_once 'lib/pear/DB/common.php';
40
41 class DB_odbc extends DB_common
42 {
43     // {{{ properties
44
45     var $connection;
46     var $phptype, $dbsyntax;
47     var $row = array();
48
49     // }}}
50     // {{{ constructor
51
52     function DB_odbc()
53     {
54         $this->DB_common();
55         $this->phptype = 'odbc';
56         $this->dbsyntax = 'sql92';
57         $this->features = array(
58             'prepare' => true,
59             'pconnect' => true,
60             'transactions' => false,
61             'limit' => 'emulate'
62         );
63         $this->errorcode_map = array(
64             '01004' => DB_ERROR_TRUNCATED,
65             '07001' => DB_ERROR_MISMATCH,
66             '21S01' => DB_ERROR_MISMATCH,
67             '21S02' => DB_ERROR_MISMATCH,
68             '22003' => DB_ERROR_INVALID_NUMBER,
69             '22008' => DB_ERROR_INVALID_DATE,
70             '22012' => DB_ERROR_DIVZERO,
71             '23000' => DB_ERROR_CONSTRAINT,
72             '24000' => DB_ERROR_INVALID,
73             '34000' => DB_ERROR_INVALID,
74             '37000' => DB_ERROR_SYNTAX,
75             '42000' => DB_ERROR_SYNTAX,
76             'IM001' => DB_ERROR_UNSUPPORTED,
77             'S0000' => DB_ERROR_NOSUCHTABLE,
78             'S0001' => DB_ERROR_NOT_FOUND,
79             'S0002' => DB_ERROR_NOT_FOUND,
80             'S0011' => DB_ERROR_ALREADY_EXISTS,
81             'S0012' => DB_ERROR_NOT_FOUND,
82             'S0021' => DB_ERROR_ALREADY_EXISTS,
83             'S0022' => DB_ERROR_NOT_FOUND,
84             'S1009' => DB_ERROR_INVALID,
85             'S1090' => DB_ERROR_INVALID,
86             'S1C00' => DB_ERROR_NOT_CAPABLE
87         );
88     }
89
90     // }}}
91     // {{{ connect()
92
93     /**
94      * Connect to a database and log in as the specified user.
95      *
96      * @param $dsn the data source name (see DB::parseDSN for syntax)
97      * @param $persistent (optional) whether the connection should
98      *        be persistent
99      *
100      * @return int DB_OK on success, a DB error code on failure
101      */
102     function connect($dsninfo, $persistent = false)
103     {
104         if (!DB::assertExtension('odbc'))
105             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
106
107         $this->dsn = $dsninfo;
108         if (!empty($dsninfo['dbsyntax'])) {
109             $this->dbsyntax = $dsninfo['dbsyntax'];
110         }
111         switch ($this->dbsyntax) {
112             case 'solid':
113                 $this->features = array(
114                     'prepare' => true,
115                     'pconnect' => true,
116                     'transactions' => true
117                 );
118                 $default_dsn = 'localhost';
119                 break;
120             case 'navision':
121                 // the Navision driver doesn't support fetch row by number
122                 $this->features['limit'] = false;
123                 break;
124             default:
125                 break;
126         }
127         $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
128         $user = $dsninfo['username'];
129         $pw = $dsninfo['password'];
130         if ($this->provides('pconnect')) {
131             $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
132         } else {
133             $connect_function = 'odbc_connect';
134         }
135         $conn = @$connect_function($dbhost, $user, $pw);
136         if (!is_resource($conn)) {
137             return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
138                                          null, $this->errorNative());
139         }
140         $this->connection = $conn;
141         return DB_OK;
142     }
143
144     // }}}
145     // {{{ disconnect()
146
147     function disconnect()
148     {
149         $err = @odbc_close($this->connection);
150         $this->connection = null;
151         return $err;
152     }
153
154     // }}}
155     // {{{ simpleQuery()
156
157     /**
158      * Send a query to ODBC and return the results as a ODBC resource
159      * identifier.
160      *
161      * @param $query the SQL query
162      *
163      * @return int returns a valid ODBC result for successful SELECT
164      * queries, DB_OK for other successful queries.  A DB error code
165      * is returned on failure.
166      */
167     function simpleQuery($query)
168     {
169         $this->last_query = $query;
170         $query = $this->modifyQuery($query);
171         $result = @odbc_exec($this->connection, $query);
172         if (!$result) {
173             return $this->odbcRaiseError(); // XXX ERRORMSG
174         }
175         // Determine which queries that should return data, and which
176         // should return an error code only.
177         if (DB::isManip($query)) {
178             $this->manip_result = $result; // For affectedRows()
179             return DB_OK;
180         }
181         $this->row[$result] = 0;
182         $this->manip_result = 0;
183         return $result;
184     }
185
186     // }}}
187     // {{{ nextResult()
188
189     /**
190      * Move the internal odbc result pointer to the next available result
191      *
192      * @param a valid fbsql result resource
193      *
194      * @access public
195      *
196      * @return true if a result is available otherwise return false
197      */
198     function nextResult($result)
199     {
200         return odbc_next_result($result);
201     }
202
203     // }}}
204     // {{{ fetchRow()
205
206     function fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
207     {
208         if ($fetchmode == DB_FETCHMODE_DEFAULT) {
209             $fetchmode = $this->fetchmode;
210         }
211         $res = $this->fetchInto ($result, $arr, $fetchmode, $rownum);
212         if ($res !== DB_OK) {
213             return $res;
214         }
215         return $arr;
216     }
217
218     // }}}
219     // {{{ fetchInto()
220
221     function fetchInto($result, &$row, $fetchmode, $rownum=null)
222     {
223         $row = array();
224         if ($rownum !== null) {
225             $rownum++; // ODBC first row is 1
226             if (!function_exists('version_compare') || version_compare(phpversion(), "4.0.5", "lt")) {
227                 $cols = odbc_fetch_into($result, $rownum, &$row);
228             } elseif (version_compare(phpversion(), '4.2.0', 'ge')) {
229                 $cols = odbc_fetch_into($result, $row, $rownum);
230             } else {
231                 $cols = odbc_fetch_into($result, $rownum, $row);
232             }
233         } else {
234             if (!function_exists('version_compare') || version_compare(phpversion(), "4.0.5", "lt")) {
235                 $cols = odbc_fetch_into($result, &$row);
236             } else {
237                 $cols = odbc_fetch_into($result, $row);
238             }
239         }
240
241         if (!$cols) {
242             /* XXX FIXME: doesn't work with unixODBC and easysoft
243                           (get corrupted $errno values)
244             if ($errno = odbc_error($this->connection)) {
245                 return $this->RaiseError($errno);
246             }*/
247             return null;
248         }
249         if ($fetchmode !== DB_FETCHMODE_ORDERED) {
250             for ($i = 0; $i < count($row); $i++) {
251                 $colName = odbc_field_name($result, $i+1);
252                 $a[$colName] = $row[$i];
253             }
254             $row = $a;
255         }
256         return DB_OK;
257     }
258
259     // }}}
260     // {{{ freeResult()
261
262     function freeResult($result)
263     {
264         if (is_resource($result)) {
265             // Always return true
266             return odbc_free_result($result);
267         }
268         if (!isset($this->prepare_tokens[(int)$result])) {
269             return false;
270         }
271         unset($this->prepare_tokens[(int)$result]);
272         unset($this->prepare_types[(int)$result]);
273         return true;
274     }
275
276     // }}}
277     // {{{ numCols()
278
279     function numCols($result)
280     {
281         $cols = @odbc_num_fields($result);
282         if (!$cols) {
283             return $this->odbcRaiseError();
284         }
285         return $cols;
286     }
287
288     // }}}
289     // {{{ affectedRows()
290
291     /**
292     * Returns the number of rows affected by a manipulative query
293     * (INSERT, DELETE, UPDATE)
294     * @return mixed int affected rows, 0 when non manip queries or
295     *               DB error on error
296     */
297     function affectedRows()
298     {
299         if (empty($this->manip_result)) {  // In case of SELECT stms
300             return 0;
301         }
302         $nrows = odbc_num_rows($this->manip_result);
303         if ($nrows == -1) {
304             return $this->odbcRaiseError();
305         }
306         return $nrows;
307     }
308
309     // }}}
310     // {{{ numRows()
311
312     /**
313      * ODBC may or may not support counting rows in the result set of
314      * SELECTs.
315      *
316      * @param $result the odbc result resource
317      * @return the number of rows, or 0
318      */
319     function numRows($result)
320     {
321         $nrows = odbc_num_rows($result);
322         if ($nrows == -1) {
323             return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
324         }
325         return $nrows;
326     }
327
328     // }}}
329     // {{{ errorNative()
330
331     /**
332      * Get the native error code of the last error (if any) that
333      * occured on the current connection.
334      *
335      * @access public
336      *
337      * @return int ODBC error code
338      */
339
340     function errorNative()
341     {
342         if (!isset($this->connection) || !is_resource($this->connection)) {
343             return odbc_error() . ' ' . odbc_errormsg();
344         }
345         return odbc_error($this->connection) . ' ' . odbc_errormsg($this->connection);
346     }
347
348     // }}}
349     // {{{ nextId()
350
351     /**
352      * Get the next value in a sequence.  We emulate sequences
353      * for odbc. Will create the sequence if it does not exist.
354      *
355      * @access public
356      *
357      * @param $seq_name the name of the sequence
358      *
359      * @param $ondemand whether to create the sequence table on demand
360      * (default is true)
361      *
362      * @return a sequence integer, or a DB error
363      */
364     function nextId($seq_name, $ondemand = true)
365     {
366         $seqname = $this->getSequenceName($seq_name);
367         $repeat = 0;
368         do {
369             $result = $this->query("update ${seqname} set id = id + 1");
370             if ($ondemand && DB::isError($result) &&
371                 $result->getCode() == DB_ERROR_NOT_FOUND) {
372                 $repeat = 1;
373                 $result = $this->createSequence($seq_name);
374                 if (DB::isError($result)) {
375                     return $result;
376                 }
377                 $result = $this->query("insert into ${seqname} (id) values(0)");
378             } else {
379                 $repeat = 0;
380             }
381         } while ($repeat);
382
383         if (DB::isError($result)) {
384             return $result;
385         }
386
387         $result = $this->query("select id from ${seqname}");
388         if (DB::isError($result)) {
389             return $result;
390         }
391
392         $row = $result->fetchRow(DB_FETCHMODE_ASSOC);
393         if (DB::isError($row || !$row)) {
394             return $row;
395         }
396
397         return $row['id'];
398     }
399
400     // }}}
401     // {{{ createSequence()
402
403     function createSequence($seq_name)
404     {
405         $seqname = $this->getSequenceName($seq_name);
406         return $this->query("CREATE TABLE ${seqname} ".
407                             '(id bigint NOT NULL,'.
408                             ' PRIMARY KEY(id))');
409     }
410
411     // }}}
412     // {{{ dropSequence()
413
414     function dropSequence($seq_name)
415     {
416         $seqname = $this->getSequenceName($seq_name);
417         return $this->query("DROP TABLE ${seqname}");
418     }
419
420     // }}}
421     // {{{ autoCommit()
422
423     function autoCommit($onoff = false)
424     {
425         if (!@odbc_autocommit($this->connection, $onoff)) {
426             return $this->odbcRaiseError();
427         }
428         return DB_OK;
429     }
430
431     // }}}
432     // {{{ commit()
433
434     function commit()
435     {
436         if (!@odbc_commit($this->connection)) {
437             return $this->odbcRaiseError();
438         }
439         return DB_OK;
440     }
441
442     // }}}
443     // {{{ rollback()
444
445     function rollback()
446     {
447         if (!@odbc_rollback($this->connection)) {
448             return $this->odbcRaiseError();
449         }
450         return DB_OK;
451     }
452
453     // }}}
454     // {{{ odbcRaiseError()
455
456     function odbcRaiseError($errno = null)
457     {
458         if ($errno === null) {
459             $errno = $this->errorCode(odbc_error($this->connection));
460         }
461         return $this->raiseError($errno, null, null, null,
462                         $this->errorNative());
463     }
464
465     // }}}
466     // {{{ getSpecialQuery()
467
468     /**
469     * Returns the query needed to get some backend info
470     * @param string $type What kind of info you want to retrieve
471     * @return string The SQL query string
472     */
473     function getSpecialQuery($type)
474     {
475         switch ($type) {
476             case 'tables':
477             default:
478                 return null;
479         }
480         return $sql;
481     }
482
483     // }}}
484
485 }
486
487 // Local variables:
488 // tab-width: 4
489 // c-basic-offset: 4
490 // End:
491 ?>