Хранилища Subversion ant

Редакция

Редакция 2 | Только различия | Не учитывать пробелы | Содержимое файла | Авторство | Последнее изменение | Открыть журнал | RSS

Редакция 2 Редакция 3
1
<?php
1
<?php
2
2
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
4
5
/**
5
/**
6
 * The PEAR DB driver for PHP's interbase extension
6
 * The PEAR DB driver for PHP's interbase extension
7
 * for interacting with Interbase and Firebird databases
7
 * for interacting with Interbase and Firebird databases
8
 *
8
 *
9
 * While this class works with PHP 4, PHP's InterBase extension is
9
 * While this class works with PHP 4, PHP's InterBase extension is
10
 * unstable in PHP 4.  Use PHP 5.
10
 * unstable in PHP 4.  Use PHP 5.
11
 *
11
 *
12
 * PHP versions 4 and 5
12
 * PHP versions 4 and 5
13
 *
13
 *
14
 * LICENSE: This source file is subject to version 3.0 of the PHP license
14
 * LICENSE: This source file is subject to version 3.0 of the PHP license
15
 * that is available through the world-wide-web at the following URI:
15
 * that is available through the world-wide-web at the following URI:
16
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
16
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
17
 * the PHP License and are unable to obtain it through the web, please
17
 * the PHP License and are unable to obtain it through the web, please
18
 * send a note to license@php.net so we can mail you a copy immediately.
18
 * send a note to license@php.net so we can mail you a copy immediately.
19
 *
19
 *
20
 * @category   Database
20
 * @category   Database
21
 * @package    DB
21
 * @package    DB
22
 * @author     Sterling Hughes <sterling@php.net>
22
 * @author     Sterling Hughes <sterling@php.net>
23
 * @author     Daniel Convissor <danielc@php.net>
23
 * @author     Daniel Convissor <danielc@php.net>
24
 * @copyright  1997-2007 The PHP Group
24
 * @copyright  1997-2007 The PHP Group
25
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
25
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
26
 * @version    CVS: $Id: ibase.php,v 1.116 2007/09/21 13:40:41 aharvey Exp $
26
 * @version    CVS: $Id: ibase.php,v 1.116 2007/09/21 13:40:41 aharvey Exp $
27
 * @link       http://pear.php.net/package/DB
27
 * @link       http://pear.php.net/package/DB
28
 */
28
 */
29
29
30
/**
30
/**
31
 * Obtain the DB_common class so it can be extended from
31
 * Obtain the DB_common class so it can be extended from
32
 */
32
 */
33
require_once 'DB/common.php';
33
require_once 'DB/common.php';
34
34
35
/**
35
/**
36
 * The methods PEAR DB uses to interact with PHP's interbase extension
36
 * The methods PEAR DB uses to interact with PHP's interbase extension
37
 * for interacting with Interbase and Firebird databases
37
 * for interacting with Interbase and Firebird databases
38
 *
38
 *
39
 * These methods overload the ones declared in DB_common.
39
 * These methods overload the ones declared in DB_common.
40
 *
40
 *
41
 * While this class works with PHP 4, PHP's InterBase extension is
41
 * While this class works with PHP 4, PHP's InterBase extension is
42
 * unstable in PHP 4.  Use PHP 5.
42
 * unstable in PHP 4.  Use PHP 5.
43
 *
43
 *
44
 * NOTICE:  limitQuery() only works for Firebird.
44
 * NOTICE:  limitQuery() only works for Firebird.
45
 *
45
 *
46
 * @category   Database
46
 * @category   Database
47
 * @package    DB
47
 * @package    DB
48
 * @author     Sterling Hughes <sterling@php.net>
48
 * @author     Sterling Hughes <sterling@php.net>
49
 * @author     Daniel Convissor <danielc@php.net>
49
 * @author     Daniel Convissor <danielc@php.net>
50
 * @copyright  1997-2007 The PHP Group
50
 * @copyright  1997-2007 The PHP Group
51
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
51
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
52
 * @version    Release: 1.7.13
52
 * @version    Release: 1.7.13
53
 * @link       http://pear.php.net/package/DB
53
 * @link       http://pear.php.net/package/DB
54
 * @since      Class became stable in Release 1.7.0
54
 * @since      Class became stable in Release 1.7.0
55
 */
55
 */
56
class DB_ibase extends DB_common
56
class DB_ibase extends DB_common
57
{
57
{
58
    // {{{ properties
58
    // {{{ properties
59
59
60
    /**
60
    /**
61
     * The DB driver type (mysql, oci8, odbc, etc.)
61
     * The DB driver type (mysql, oci8, odbc, etc.)
62
     * @var string
62
     * @var string
63
     */
63
     */
64
    var $phptype = 'ibase';
64
    var $phptype = 'ibase';
65
65
66
    /**
66
    /**
67
     * The database syntax variant to be used (db2, access, etc.), if any
67
     * The database syntax variant to be used (db2, access, etc.), if any
68
     * @var string
68
     * @var string
69
     */
69
     */
70
    var $dbsyntax = 'ibase';
70
    var $dbsyntax = 'ibase';
71
71
72
    /**
72
    /**
73
     * The capabilities of this DB implementation
73
     * The capabilities of this DB implementation
74
     *
74
     *
75
     * The 'new_link' element contains the PHP version that first provided
75
     * The 'new_link' element contains the PHP version that first provided
76
     * new_link support for this DBMS.  Contains false if it's unsupported.
76
     * new_link support for this DBMS.  Contains false if it's unsupported.
77
     *
77
     *
78
     * Meaning of the 'limit' element:
78
     * Meaning of the 'limit' element:
79
     *   + 'emulate' = emulate with fetch row by number
79
     *   + 'emulate' = emulate with fetch row by number
80
     *   + 'alter'   = alter the query
80
     *   + 'alter'   = alter the query
81
     *   + false     = skip rows
81
     *   + false     = skip rows
82
     *
82
     *
83
     * NOTE: only firebird supports limit.
83
     * NOTE: only firebird supports limit.
84
     *
84
     *
85
     * @var array
85
     * @var array
86
     */
86
     */
87
    var $features = array(
87
    var $features = array(
88
        'limit'         => false,
88
        'limit'         => false,
89
        'new_link'      => false,
89
        'new_link'      => false,
90
        'numrows'       => 'emulate',
90
        'numrows'       => 'emulate',
91
        'pconnect'      => true,
91
        'pconnect'      => true,
92
        'prepare'       => true,
92
        'prepare'       => true,
93
        'ssl'           => false,
93
        'ssl'           => false,
94
        'transactions'  => true,
94
        'transactions'  => true,
95
    );
95
    );
96
96
97
    /**
97
    /**
98
     * A mapping of native error codes to DB error codes
98
     * A mapping of native error codes to DB error codes
99
     * @var array
99
     * @var array
100
     */
100
     */
101
    var $errorcode_map = array(
101
    var $errorcode_map = array(
102
        -104 => DB_ERROR_SYNTAX,
102
        -104 => DB_ERROR_SYNTAX,
103
        -150 => DB_ERROR_ACCESS_VIOLATION,
103
        -150 => DB_ERROR_ACCESS_VIOLATION,
104
        -151 => DB_ERROR_ACCESS_VIOLATION,
104
        -151 => DB_ERROR_ACCESS_VIOLATION,
105
        -155 => DB_ERROR_NOSUCHTABLE,
105
        -155 => DB_ERROR_NOSUCHTABLE,
106
        -157 => DB_ERROR_NOSUCHFIELD,
106
        -157 => DB_ERROR_NOSUCHFIELD,
107
        -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
107
        -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
108
        -170 => DB_ERROR_MISMATCH,
108
        -170 => DB_ERROR_MISMATCH,
109
        -171 => DB_ERROR_MISMATCH,
109
        -171 => DB_ERROR_MISMATCH,
110
        -172 => DB_ERROR_INVALID,
110
        -172 => DB_ERROR_INVALID,
111
        // -204 =>  // Covers too many errors, need to use regex on msg
111
        // -204 =>  // Covers too many errors, need to use regex on msg
112
        -205 => DB_ERROR_NOSUCHFIELD,
112
        -205 => DB_ERROR_NOSUCHFIELD,
113
        -206 => DB_ERROR_NOSUCHFIELD,
113
        -206 => DB_ERROR_NOSUCHFIELD,
114
        -208 => DB_ERROR_INVALID,
114
        -208 => DB_ERROR_INVALID,
115
        -219 => DB_ERROR_NOSUCHTABLE,
115
        -219 => DB_ERROR_NOSUCHTABLE,
116
        -297 => DB_ERROR_CONSTRAINT,
116
        -297 => DB_ERROR_CONSTRAINT,
117
        -303 => DB_ERROR_INVALID,
117
        -303 => DB_ERROR_INVALID,
118
        -413 => DB_ERROR_INVALID_NUMBER,
118
        -413 => DB_ERROR_INVALID_NUMBER,
119
        -530 => DB_ERROR_CONSTRAINT,
119
        -530 => DB_ERROR_CONSTRAINT,
120
        -551 => DB_ERROR_ACCESS_VIOLATION,
120
        -551 => DB_ERROR_ACCESS_VIOLATION,
121
        -552 => DB_ERROR_ACCESS_VIOLATION,
121
        -552 => DB_ERROR_ACCESS_VIOLATION,
122
        // -607 =>  // Covers too many errors, need to use regex on msg
122
        // -607 =>  // Covers too many errors, need to use regex on msg
123
        -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
123
        -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
124
        -803 => DB_ERROR_CONSTRAINT,
124
        -803 => DB_ERROR_CONSTRAINT,
125
        -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
125
        -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
126
        // -902 =>  // Covers too many errors, need to use regex on msg
126
        // -902 =>  // Covers too many errors, need to use regex on msg
127
        -904 => DB_ERROR_CONNECT_FAILED,
127
        -904 => DB_ERROR_CONNECT_FAILED,
128
        -922 => DB_ERROR_NOSUCHDB,
128
        -922 => DB_ERROR_NOSUCHDB,
129
        -923 => DB_ERROR_CONNECT_FAILED,
129
        -923 => DB_ERROR_CONNECT_FAILED,
130
        -924 => DB_ERROR_CONNECT_FAILED
130
        -924 => DB_ERROR_CONNECT_FAILED
131
    );
131
    );
132
132
133
    /**
133
    /**
134
     * The raw database connection created by PHP
134
     * The raw database connection created by PHP
135
     * @var resource
135
     * @var resource
136
     */
136
     */
137
    var $connection;
137
    var $connection;
138
138
139
    /**
139
    /**
140
     * The DSN information for connecting to a database
140
     * The DSN information for connecting to a database
141
     * @var array
141
     * @var array
142
     */
142
     */
143
    var $dsn = array();
143
    var $dsn = array();
144
144
145
145
146
    /**
146
    /**
147
     * The number of rows affected by a data manipulation query
147
     * The number of rows affected by a data manipulation query
148
     * @var integer
148
     * @var integer
149
     * @access private
149
     * @access private
150
     */
150
     */
151
    var $affected = 0;
151
    var $affected = 0;
152
152
153
    /**
153
    /**
154
     * Should data manipulation queries be committed automatically?
154
     * Should data manipulation queries be committed automatically?
155
     * @var bool
155
     * @var bool
156
     * @access private
156
     * @access private
157
     */
157
     */
158
    var $autocommit = true;
158
    var $autocommit = true;
159
159
160
    /**
160
    /**
161
     * The prepared statement handle from the most recently executed statement
161
     * The prepared statement handle from the most recently executed statement
162
     *
162
     *
163
     * {@internal  Mainly here because the InterBase/Firebird API is only
163
     * {@internal  Mainly here because the InterBase/Firebird API is only
164
     * able to retrieve data from result sets if the statemnt handle is
164
     * able to retrieve data from result sets if the statemnt handle is
165
     * still in scope.}}
165
     * still in scope.}}
166
     *
166
     *
167
     * @var resource
167
     * @var resource
168
     */
168
     */
169
    var $last_stmt;
169
    var $last_stmt;
170
170
171
    /**
171
    /**
172
     * Is the given prepared statement a data manipulation query?
172
     * Is the given prepared statement a data manipulation query?
173
     * @var array
173
     * @var array
174
     * @access private
174
     * @access private
175
     */
175
     */
176
    var $manip_query = array();
176
    var $manip_query = array();
177
177
178
178
179
    // }}}
179
    // }}}
180
    // {{{ constructor
180
    // {{{ constructor
181
181
182
    /**
182
    /**
183
     * This constructor calls <kbd>$this->DB_common()</kbd>
183
     * This constructor calls <kbd>$this->DB_common()</kbd>
184
     *
184
     *
185
     * @return void
185
     * @return void
186
     */
186
     */
187
    function DB_ibase()
187
    function DB_ibase()
188
    {
188
    {
189
        $this->DB_common();
189
        $this->DB_common();
190
    }
190
    }
191
191
192
    // }}}
192
    // }}}
193
    // {{{ connect()
193
    // {{{ connect()
194
194
195
    /**
195
    /**
196
     * Connect to the database server, log in and open the database
196
     * Connect to the database server, log in and open the database
197
     *
197
     *
198
     * Don't call this method directly.  Use DB::connect() instead.
198
     * Don't call this method directly.  Use DB::connect() instead.
199
     *
199
     *
200
     * PEAR DB's ibase driver supports the following extra DSN options:
200
     * PEAR DB's ibase driver supports the following extra DSN options:
201
     *   + buffers    The number of database buffers to allocate for the
201
     *   + buffers    The number of database buffers to allocate for the
202
     *                 server-side cache.
202
     *                 server-side cache.
203
     *   + charset    The default character set for a database.
203
     *   + charset    The default character set for a database.
204
     *   + dialect    The default SQL dialect for any statement
204
     *   + dialect    The default SQL dialect for any statement
205
     *                 executed within a connection.  Defaults to the
205
     *                 executed within a connection.  Defaults to the
206
     *                 highest one supported by client libraries.
206
     *                 highest one supported by client libraries.
207
     *                 Functional only with InterBase 6 and up.
207
     *                 Functional only with InterBase 6 and up.
208
     *   + role       Functional only with InterBase 5 and up.
208
     *   + role       Functional only with InterBase 5 and up.
209
     *
209
     *
210
     * @param array $dsn         the data source name
210
     * @param array $dsn         the data source name
211
     * @param bool  $persistent  should the connection be persistent?
211
     * @param bool  $persistent  should the connection be persistent?
212
     *
212
     *
213
     * @return int  DB_OK on success. A DB_Error object on failure.
213
     * @return int  DB_OK on success. A DB_Error object on failure.
214
     */
214
     */
215
    function connect($dsn, $persistent = false)
215
    function connect($dsn, $persistent = false)
216
    {
216
    {
217
        if (!PEAR::loadExtension('interbase')) {
217
        if (!PEAR::loadExtension('interbase')) {
218
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
218
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
219
        }
219
        }
220
220
221
        $this->dsn = $dsn;
221
        $this->dsn = $dsn;
222
        if ($dsn['dbsyntax']) {
222
        if ($dsn['dbsyntax']) {
223
            $this->dbsyntax = $dsn['dbsyntax'];
223
            $this->dbsyntax = $dsn['dbsyntax'];
224
        }
224
        }
225
        if ($this->dbsyntax == 'firebird') {
225
        if ($this->dbsyntax == 'firebird') {
226
            $this->features['limit'] = 'alter';
226
            $this->features['limit'] = 'alter';
227
        }
227
        }
228
228
229
        $params = array(
229
        $params = array(
230
            $dsn['hostspec']
230
            $dsn['hostspec']
231
                    ? ($dsn['hostspec'] . ':' . $dsn['database'])
231
                    ? ($dsn['hostspec'] . ':' . $dsn['database'])
232
                    : $dsn['database'],
232
                    : $dsn['database'],
233
            $dsn['username'] ? $dsn['username'] : null,
233
            $dsn['username'] ? $dsn['username'] : null,
234
            $dsn['password'] ? $dsn['password'] : null,
234
            $dsn['password'] ? $dsn['password'] : null,
235
            isset($dsn['charset']) ? $dsn['charset'] : null,
235
            isset($dsn['charset']) ? $dsn['charset'] : null,
236
            isset($dsn['buffers']) ? $dsn['buffers'] : null,
236
            isset($dsn['buffers']) ? $dsn['buffers'] : null,
237
            isset($dsn['dialect']) ? $dsn['dialect'] : null,
237
            isset($dsn['dialect']) ? $dsn['dialect'] : null,
238
            isset($dsn['role'])    ? $dsn['role'] : null,
238
            isset($dsn['role'])    ? $dsn['role'] : null,
239
        );
239
        );
240
240
241
        $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
241
        $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
242
242
243
        $this->connection = @call_user_func_array($connect_function, $params);
243
        $this->connection = @call_user_func_array($connect_function, $params);
244
        if (!$this->connection) {
244
        if (!$this->connection) {
245
            return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
245
            return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
246
        }
246
        }
247
        return DB_OK;
247
        return DB_OK;
248
    }
248
    }
249
249
250
    // }}}
250
    // }}}
251
    // {{{ disconnect()
251
    // {{{ disconnect()
252
252
253
    /**
253
    /**
254
     * Disconnects from the database server
254
     * Disconnects from the database server
255
     *
255
     *
256
     * @return bool  TRUE on success, FALSE on failure
256
     * @return bool  TRUE on success, FALSE on failure
257
     */
257
     */
258
    function disconnect()
258
    function disconnect()
259
    {
259
    {
260
        $ret = @ibase_close($this->connection);
260
        $ret = @ibase_close($this->connection);
261
        $this->connection = null;
261
        $this->connection = null;
262
        return $ret;
262
        return $ret;
263
    }
263
    }
264
264
265
    // }}}
265
    // }}}
266
    // {{{ simpleQuery()
266
    // {{{ simpleQuery()
267
267
268
    /**
268
    /**
269
     * Sends a query to the database server
269
     * Sends a query to the database server
270
     *
270
     *
271
     * @param string  the SQL query string
271
     * @param string  the SQL query string
272
     *
272
     *
273
     * @return mixed  + a PHP result resrouce for successful SELECT queries
273
     * @return mixed  + a PHP result resrouce for successful SELECT queries
274
     *                + the DB_OK constant for other successful queries
274
     *                + the DB_OK constant for other successful queries
275
     *                + a DB_Error object on failure
275
     *                + a DB_Error object on failure
276
     */
276
     */
277
    function simpleQuery($query)
277
    function simpleQuery($query)
278
    {
278
    {
279
        $ismanip = $this->_checkManip($query);
279
        $ismanip = $this->_checkManip($query);
280
        $this->last_query = $query;
280
        $this->last_query = $query;
281
        $query = $this->modifyQuery($query);
281
        $query = $this->modifyQuery($query);
282
        $result = @ibase_query($this->connection, $query);
282
        $result = @ibase_query($this->connection, $query);
283
283
284
        if (!$result) {
284
        if (!$result) {
285
            return $this->ibaseRaiseError();
285
            return $this->ibaseRaiseError();
286
        }
286
        }
287
        if ($this->autocommit && $ismanip) {
287
        if ($this->autocommit && $ismanip) {
288
            @ibase_commit($this->connection);
288
            @ibase_commit($this->connection);
289
        }
289
        }
290
        if ($ismanip) {
290
        if ($ismanip) {
291
            $this->affected = $result;
291
            $this->affected = $result;
292
            return DB_OK;
292
            return DB_OK;
293
        } else {
293
        } else {
294
            $this->affected = 0;
294
            $this->affected = 0;
295
            return $result;
295
            return $result;
296
        }
296
        }
297
    }
297
    }
298
298
299
    // }}}
299
    // }}}
300
    // {{{ modifyLimitQuery()
300
    // {{{ modifyLimitQuery()
301
301
302
    /**
302
    /**
303
     * Adds LIMIT clauses to a query string according to current DBMS standards
303
     * Adds LIMIT clauses to a query string according to current DBMS standards
304
     *
304
     *
305
     * Only works with Firebird.
305
     * Only works with Firebird.
306
     *
306
     *
307
     * @param string $query   the query to modify
307
     * @param string $query   the query to modify
308
     * @param int    $from    the row to start to fetching (0 = the first row)
308
     * @param int    $from    the row to start to fetching (0 = the first row)
309
     * @param int    $count   the numbers of rows to fetch
309
     * @param int    $count   the numbers of rows to fetch
310
     * @param mixed  $params  array, string or numeric data to be used in
310
     * @param mixed  $params  array, string or numeric data to be used in
311
     *                         execution of the statement.  Quantity of items
311
     *                         execution of the statement.  Quantity of items
312
     *                         passed must match quantity of placeholders in
312
     *                         passed must match quantity of placeholders in
313
     *                         query:  meaning 1 placeholder for non-array
313
     *                         query:  meaning 1 placeholder for non-array
314
     *                         parameters or 1 placeholder per array element.
314
     *                         parameters or 1 placeholder per array element.
315
     *
315
     *
316
     * @return string  the query string with LIMIT clauses added
316
     * @return string  the query string with LIMIT clauses added
317
     *
317
     *
318
     * @access protected
318
     * @access protected
319
     */
319
     */
320
    function modifyLimitQuery($query, $from, $count, $params = array())
320
    function modifyLimitQuery($query, $from, $count, $params = array())
321
    {
321
    {
322
        if ($this->dsn['dbsyntax'] == 'firebird') {
322
        if ($this->dsn['dbsyntax'] == 'firebird') {
323
            $query = preg_replace('/^([\s(])*SELECT/i',
323
            $query = preg_replace('/^([\s(])*SELECT/i',
324
                                  "SELECT FIRST $count SKIP $from", $query);
324
                                  "SELECT FIRST $count SKIP $from", $query);
325
        }
325
        }
326
        return $query;
326
        return $query;
327
    }
327
    }
328
328
329
    // }}}
329
    // }}}
330
    // {{{ nextResult()
330
    // {{{ nextResult()
331
331
332
    /**
332
    /**
333
     * Move the internal ibase result pointer to the next available result
333
     * Move the internal ibase result pointer to the next available result
334
     *
334
     *
335
     * @param a valid fbsql result resource
335
     * @param a valid fbsql result resource
336
     *
336
     *
337
     * @access public
337
     * @access public
338
     *
338
     *
339
     * @return true if a result is available otherwise return false
339
     * @return true if a result is available otherwise return false
340
     */
340
     */
341
    function nextResult($result)
341
    function nextResult($result)
342
    {
342
    {
343
        return false;
343
        return false;
344
    }
344
    }
345
345
346
    // }}}
346
    // }}}
347
    // {{{ fetchInto()
347
    // {{{ fetchInto()
348
348
349
    /**
349
    /**
350
     * Places a row from the result set into the given array
350
     * Places a row from the result set into the given array
351
     *
351
     *
352
     * Formating of the array and the data therein are configurable.
352
     * Formating of the array and the data therein are configurable.
353
     * See DB_result::fetchInto() for more information.
353
     * See DB_result::fetchInto() for more information.
354
     *
354
     *
355
     * This method is not meant to be called directly.  Use
355
     * This method is not meant to be called directly.  Use
356
     * DB_result::fetchInto() instead.  It can't be declared "protected"
356
     * DB_result::fetchInto() instead.  It can't be declared "protected"
357
     * because DB_result is a separate object.
357
     * because DB_result is a separate object.
358
     *
358
     *
359
     * @param resource $result    the query result resource
359
     * @param resource $result    the query result resource
360
     * @param array    $arr       the referenced array to put the data in
360
     * @param array    $arr       the referenced array to put the data in
361
     * @param int      $fetchmode how the resulting array should be indexed
361
     * @param int      $fetchmode how the resulting array should be indexed
362
     * @param int      $rownum    the row number to fetch (0 = first row)
362
     * @param int      $rownum    the row number to fetch (0 = first row)
363
     *
363
     *
364
     * @return mixed  DB_OK on success, NULL when the end of a result set is
364
     * @return mixed  DB_OK on success, NULL when the end of a result set is
365
     *                 reached or on failure
365
     *                 reached or on failure
366
     *
366
     *
367
     * @see DB_result::fetchInto()
367
     * @see DB_result::fetchInto()
368
     */
368
     */
369
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
369
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
370
    {
370
    {
371
        if ($rownum !== null) {
371
        if ($rownum !== null) {
372
            return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
372
            return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
373
        }
373
        }
374
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
374
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
375
            if (function_exists('ibase_fetch_assoc')) {
375
            if (function_exists('ibase_fetch_assoc')) {
376
                $arr = @ibase_fetch_assoc($result);
376
                $arr = @ibase_fetch_assoc($result);
377
            } else {
377
            } else {
378
                $arr = get_object_vars(ibase_fetch_object($result));
378
                $arr = get_object_vars(ibase_fetch_object($result));
379
            }
379
            }
380
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
380
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
381
                $arr = array_change_key_case($arr, CASE_LOWER);
381
                $arr = array_change_key_case($arr, CASE_LOWER);
382
            }
382
            }
383
        } else {
383
        } else {
384
            $arr = @ibase_fetch_row($result);
384
            $arr = @ibase_fetch_row($result);
385
        }
385
        }
386
        if (!$arr) {
386
        if (!$arr) {
387
            return null;
387
            return null;
388
        }
388
        }
389
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
389
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
390
            $this->_rtrimArrayValues($arr);
390
            $this->_rtrimArrayValues($arr);
391
        }
391
        }
392
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
392
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
393
            $this->_convertNullArrayValuesToEmpty($arr);
393
            $this->_convertNullArrayValuesToEmpty($arr);
394
        }
394
        }
395
        return DB_OK;
395
        return DB_OK;
396
    }
396
    }
397
397
398
    // }}}
398
    // }}}
399
    // {{{ freeResult()
399
    // {{{ freeResult()
400
400
401
    /**
401
    /**
402
     * Deletes the result set and frees the memory occupied by the result set
402
     * Deletes the result set and frees the memory occupied by the result set
403
     *
403
     *
404
     * This method is not meant to be called directly.  Use
404
     * This method is not meant to be called directly.  Use
405
     * DB_result::free() instead.  It can't be declared "protected"
405
     * DB_result::free() instead.  It can't be declared "protected"
406
     * because DB_result is a separate object.
406
     * because DB_result is a separate object.
407
     *
407
     *
408
     * @param resource $result  PHP's query result resource
408
     * @param resource $result  PHP's query result resource
409
     *
409
     *
410
     * @return bool  TRUE on success, FALSE if $result is invalid
410
     * @return bool  TRUE on success, FALSE if $result is invalid
411
     *
411
     *
412
     * @see DB_result::free()
412
     * @see DB_result::free()
413
     */
413
     */
414
    function freeResult($result)
414
    function freeResult($result)
415
    {
415
    {
416
        return is_resource($result) ? ibase_free_result($result) : false;
416
        return is_resource($result) ? ibase_free_result($result) : false;
417
    }
417
    }
418
418
419
    // }}}
419
    // }}}
420
    // {{{ freeQuery()
420
    // {{{ freeQuery()
421
421
422
    function freeQuery($query)
422
    function freeQuery($query)
423
    {
423
    {
424
        return is_resource($query) ? ibase_free_query($query) : false;
424
        return is_resource($query) ? ibase_free_query($query) : false;
425
    }
425
    }
426
426
427
    // }}}
427
    // }}}
428
    // {{{ affectedRows()
428
    // {{{ affectedRows()
429
429
430
    /**
430
    /**
431
     * Determines the number of rows affected by a data maniuplation query
431
     * Determines the number of rows affected by a data maniuplation query
432
     *
432
     *
433
     * 0 is returned for queries that don't manipulate data.
433
     * 0 is returned for queries that don't manipulate data.
434
     *
434
     *
435
     * @return int  the number of rows.  A DB_Error object on failure.
435
     * @return int  the number of rows.  A DB_Error object on failure.
436
     */
436
     */
437
    function affectedRows()
437
    function affectedRows()
438
    {
438
    {
439
        if (is_integer($this->affected)) {
439
        if (is_integer($this->affected)) {
440
            return $this->affected;
440
            return $this->affected;
441
        }
441
        }
442
        return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
442
        return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
443
    }
443
    }
444
444
445
    // }}}
445
    // }}}
446
    // {{{ numCols()
446
    // {{{ numCols()
447
447
448
    /**
448
    /**
449
     * Gets the number of columns in a result set
449
     * Gets the number of columns in a result set
450
     *
450
     *
451
     * This method is not meant to be called directly.  Use
451
     * This method is not meant to be called directly.  Use
452
     * DB_result::numCols() instead.  It can't be declared "protected"
452
     * DB_result::numCols() instead.  It can't be declared "protected"
453
     * because DB_result is a separate object.
453
     * because DB_result is a separate object.
454
     *
454
     *
455
     * @param resource $result  PHP's query result resource
455
     * @param resource $result  PHP's query result resource
456
     *
456
     *
457
     * @return int  the number of columns.  A DB_Error object on failure.
457
     * @return int  the number of columns.  A DB_Error object on failure.
458
     *
458
     *
459
     * @see DB_result::numCols()
459
     * @see DB_result::numCols()
460
     */
460
     */
461
    function numCols($result)
461
    function numCols($result)
462
    {
462
    {
463
        $cols = @ibase_num_fields($result);
463
        $cols = @ibase_num_fields($result);
464
        if (!$cols) {
464
        if (!$cols) {
465
            return $this->ibaseRaiseError();
465
            return $this->ibaseRaiseError();
466
        }
466
        }
467
        return $cols;
467
        return $cols;
468
    }
468
    }
469
469
470
    // }}}
470
    // }}}
471
    // {{{ prepare()
471
    // {{{ prepare()
472
472
473
    /**
473
    /**
474
     * Prepares a query for multiple execution with execute().
474
     * Prepares a query for multiple execution with execute().
475
     *
475
     *
476
     * prepare() requires a generic query as string like <code>
476
     * prepare() requires a generic query as string like <code>
477
     *    INSERT INTO numbers VALUES (?, ?, ?)
477
     *    INSERT INTO numbers VALUES (?, ?, ?)
478
     * </code>.  The <kbd>?</kbd> characters are placeholders.
478
     * </code>.  The <kbd>?</kbd> characters are placeholders.
479
     *
479
     *
480
     * Three types of placeholders can be used:
480
     * Three types of placeholders can be used:
481
     *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
481
     *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
482
     *   + <kbd>!</kbd>  value is inserted 'as is'
482
     *   + <kbd>!</kbd>  value is inserted 'as is'
483
     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
483
     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
484
     *                     inserted into the query (i.e. saving binary
484
     *                     inserted into the query (i.e. saving binary
485
     *                     data in a db)
485
     *                     data in a db)
486
     *
486
     *
487
     * Use backslashes to escape placeholder characters if you don't want
487
     * Use backslashes to escape placeholder characters if you don't want
488
     * them to be interpreted as placeholders.  Example: <code>
488
     * them to be interpreted as placeholders.  Example: <code>
489
     *    "UPDATE foo SET col=? WHERE col='over \& under'"
489
     *    "UPDATE foo SET col=? WHERE col='over \& under'"
490
     * </code>
490
     * </code>
491
     *
491
     *
492
     * @param string $query query to be prepared
492
     * @param string $query query to be prepared
493
     * @return mixed DB statement resource on success. DB_Error on failure.
493
     * @return mixed DB statement resource on success. DB_Error on failure.
494
     */
494
     */
495
    function prepare($query)
495
    function prepare($query)
496
    {
496
    {
497
        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
497
        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
498
                               PREG_SPLIT_DELIM_CAPTURE);
498
                               PREG_SPLIT_DELIM_CAPTURE);
499
        $token    = 0;
499
        $token    = 0;
500
        $types    = array();
500
        $types    = array();
501
        $newquery = '';
501
        $newquery = '';
502
502
503
        foreach ($tokens as $key => $val) {
503
        foreach ($tokens as $key => $val) {
504
            switch ($val) {
504
            switch ($val) {
505
                case '?':
505
                case '?':
506
                    $types[$token++] = DB_PARAM_SCALAR;
506
                    $types[$token++] = DB_PARAM_SCALAR;
507
                    break;
507
                    break;
508
                case '&':
508
                case '&':
509
                    $types[$token++] = DB_PARAM_OPAQUE;
509
                    $types[$token++] = DB_PARAM_OPAQUE;
510
                    break;
510
                    break;
511
                case '!':
511
                case '!':
512
                    $types[$token++] = DB_PARAM_MISC;
512
                    $types[$token++] = DB_PARAM_MISC;
513
                    break;
513
                    break;
514
                default:
514
                default:
515
                    $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
515
                    $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
516
                    $newquery .= $tokens[$key] . '?';
516
                    $newquery .= $tokens[$key] . '?';
517
            }
517
            }
518
        }
518
        }
519
519
520
        $newquery = substr($newquery, 0, -1);
520
        $newquery = substr($newquery, 0, -1);
521
        $this->last_query = $query;
521
        $this->last_query = $query;
522
        $newquery = $this->modifyQuery($newquery);
522
        $newquery = $this->modifyQuery($newquery);
523
        $stmt = @ibase_prepare($this->connection, $newquery);
523
        $stmt = @ibase_prepare($this->connection, $newquery);
524
524
525
        if ($stmt === false) {
525
        if ($stmt === false) {
526
            $stmt = $this->ibaseRaiseError();
526
            $stmt = $this->ibaseRaiseError();
527
        } else {
527
        } else {
528
            $this->prepare_types[(int)$stmt] = $types;
528
            $this->prepare_types[(int)$stmt] = $types;
529
            $this->manip_query[(int)$stmt]   = DB::isManip($query);
529
            $this->manip_query[(int)$stmt]   = DB::isManip($query);
530
        }
530
        }
531
531
532
        return $stmt;
532
        return $stmt;
533
    }
533
    }
534
534
535
    // }}}
535
    // }}}
536
    // {{{ execute()
536
    // {{{ execute()
537
537
538
    /**
538
    /**
539
     * Executes a DB statement prepared with prepare().
539
     * Executes a DB statement prepared with prepare().
540
     *
540
     *
541
     * @param resource  $stmt  a DB statement resource returned from prepare()
541
     * @param resource  $stmt  a DB statement resource returned from prepare()
542
     * @param mixed  $data  array, string or numeric data to be used in
542
     * @param mixed  $data  array, string or numeric data to be used in
543
     *                      execution of the statement.  Quantity of items
543
     *                      execution of the statement.  Quantity of items
544
     *                      passed must match quantity of placeholders in
544
     *                      passed must match quantity of placeholders in
545
     *                      query:  meaning 1 for non-array items or the
545
     *                      query:  meaning 1 for non-array items or the
546
     *                      quantity of elements in the array.
546
     *                      quantity of elements in the array.
547
     * @return object  a new DB_Result or a DB_Error when fail
547
     * @return object  a new DB_Result or a DB_Error when fail
548
     * @see DB_ibase::prepare()
548
     * @see DB_ibase::prepare()
549
     * @access public
549
     * @access public
550
     */
550
     */
551
    function &execute($stmt, $data = array())
551
    function &execute($stmt, $data = array())
552
    {
552
    {
553
        $data = (array)$data;
553
        $data = (array)$data;
554
        $this->last_parameters = $data;
554
        $this->last_parameters = $data;
555
555
556
        $types = $this->prepare_types[(int)$stmt];
556
        $types = $this->prepare_types[(int)$stmt];
557
        if (count($types) != count($data)) {
557
        if (count($types) != count($data)) {
558
            $tmp = $this->raiseError(DB_ERROR_MISMATCH);
558
            $tmp = $this->raiseError(DB_ERROR_MISMATCH);
559
            return $tmp;
559
            return $tmp;
560
        }
560
        }
561
561
562
        $i = 0;
562
        $i = 0;
563
        foreach ($data as $key => $value) {
563
        foreach ($data as $key => $value) {
564
            if ($types[$i] == DB_PARAM_MISC) {
564
            if ($types[$i] == DB_PARAM_MISC) {
565
                /*
565
                /*
566
                 * ibase doesn't seem to have the ability to pass a
566
                 * ibase doesn't seem to have the ability to pass a
567
                 * parameter along unchanged, so strip off quotes from start
567
                 * parameter along unchanged, so strip off quotes from start
568
                 * and end, plus turn two single quotes to one single quote,
568
                 * and end, plus turn two single quotes to one single quote,
569
                 * in order to avoid the quotes getting escaped by
569
                 * in order to avoid the quotes getting escaped by
570
                 * ibase and ending up in the database.
570
                 * ibase and ending up in the database.
571
                 */
571
                 */
572
                $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
572
                $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
573
                $data[$key] = str_replace("''", "'", $data[$key]);
573
                $data[$key] = str_replace("''", "'", $data[$key]);
574
            } elseif ($types[$i] == DB_PARAM_OPAQUE) {
574
            } elseif ($types[$i] == DB_PARAM_OPAQUE) {
575
                $fp = @fopen($data[$key], 'rb');
575
                $fp = @fopen($data[$key], 'rb');
576
                if (!$fp) {
576
                if (!$fp) {
577
                    $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
577
                    $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
578
                    return $tmp;
578
                    return $tmp;
579
                }
579
                }
580
                $data[$key] = fread($fp, filesize($data[$key]));
580
                $data[$key] = fread($fp, filesize($data[$key]));
581
                fclose($fp);
581
                fclose($fp);
582
            }
582
            }
583
            $i++;
583
            $i++;
584
        }
584
        }
585
585
586
        array_unshift($data, $stmt);
586
        array_unshift($data, $stmt);
587
587
588
        $res = call_user_func_array('ibase_execute', $data);
588
        $res = call_user_func_array('ibase_execute', $data);
589
        if (!$res) {
589
        if (!$res) {
590
            $tmp = $this->ibaseRaiseError();
590
            $tmp = $this->ibaseRaiseError();
591
            return $tmp;
591
            return $tmp;
592
        }
592
        }
593
        /* XXX need this?
593
        /* XXX need this?
594
        if ($this->autocommit && $this->manip_query[(int)$stmt]) {
594
        if ($this->autocommit && $this->manip_query[(int)$stmt]) {
595
            @ibase_commit($this->connection);
595
            @ibase_commit($this->connection);
596
        }*/
596
        }*/
597
        $this->last_stmt = $stmt;
597
        $this->last_stmt = $stmt;
598
        if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
598
        if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
599
            $this->_last_query_manip = true;
599
            $this->_last_query_manip = true;
600
            $this->_next_query_manip = false;
600
            $this->_next_query_manip = false;
601
            $tmp = DB_OK;
601
            $tmp = DB_OK;
602
        } else {
602
        } else {
603
            $this->_last_query_manip = false;
603
            $this->_last_query_manip = false;
604
            $tmp = new DB_result($this, $res);
604
            $tmp = new DB_result($this, $res);
605
        }
605
        }
606
        return $tmp;
606
        return $tmp;
607
    }
607
    }
608
608
609
    /**
609
    /**
610
     * Frees the internal resources associated with a prepared query
610
     * Frees the internal resources associated with a prepared query
611
     *
611
     *
612
     * @param resource $stmt           the prepared statement's PHP resource
612
     * @param resource $stmt           the prepared statement's PHP resource
613
     * @param bool     $free_resource  should the PHP resource be freed too?
613
     * @param bool     $free_resource  should the PHP resource be freed too?
614
     *                                  Use false if you need to get data
614
     *                                  Use false if you need to get data
615
     *                                  from the result set later.
615
     *                                  from the result set later.
616
     *
616
     *
617
     * @return bool  TRUE on success, FALSE if $result is invalid
617
     * @return bool  TRUE on success, FALSE if $result is invalid
618
     *
618
     *
619
     * @see DB_ibase::prepare()
619
     * @see DB_ibase::prepare()
620
     */
620
     */
621
    function freePrepared($stmt, $free_resource = true)
621
    function freePrepared($stmt, $free_resource = true)
622
    {
622
    {
623
        if (!is_resource($stmt)) {
623
        if (!is_resource($stmt)) {
624
            return false;
624
            return false;
625
        }
625
        }
626
        if ($free_resource) {
626
        if ($free_resource) {
627
            @ibase_free_query($stmt);
627
            @ibase_free_query($stmt);
628
        }
628
        }
629
        unset($this->prepare_tokens[(int)$stmt]);
629
        unset($this->prepare_tokens[(int)$stmt]);
630
        unset($this->prepare_types[(int)$stmt]);
630
        unset($this->prepare_types[(int)$stmt]);
631
        unset($this->manip_query[(int)$stmt]);
631
        unset($this->manip_query[(int)$stmt]);
632
        return true;
632
        return true;
633
    }
633
    }
634
634
635
    // }}}
635
    // }}}
636
    // {{{ autoCommit()
636
    // {{{ autoCommit()
637
637
638
    /**
638
    /**
639
     * Enables or disables automatic commits
639
     * Enables or disables automatic commits
640
     *
640
     *
641
     * @param bool $onoff  true turns it on, false turns it off
641
     * @param bool $onoff  true turns it on, false turns it off
642
     *
642
     *
643
     * @return int  DB_OK on success.  A DB_Error object if the driver
643
     * @return int  DB_OK on success.  A DB_Error object if the driver
644
     *               doesn't support auto-committing transactions.
644
     *               doesn't support auto-committing transactions.
645
     */
645
     */
646
    function autoCommit($onoff = false)
646
    function autoCommit($onoff = false)
647
    {
647
    {
648
        $this->autocommit = $onoff ? 1 : 0;
648
        $this->autocommit = $onoff ? 1 : 0;
649
        return DB_OK;
649
        return DB_OK;
650
    }
650
    }
651
651
652
    // }}}
652
    // }}}
653
    // {{{ commit()
653
    // {{{ commit()
654
654
655
    /**
655
    /**
656
     * Commits the current transaction
656
     * Commits the current transaction
657
     *
657
     *
658
     * @return int  DB_OK on success.  A DB_Error object on failure.
658
     * @return int  DB_OK on success.  A DB_Error object on failure.
659
     */
659
     */
660
    function commit()
660
    function commit()
661
    {
661
    {
662
        return @ibase_commit($this->connection);
662
        return @ibase_commit($this->connection);
663
    }
663
    }
664
664
665
    // }}}
665
    // }}}
666
    // {{{ rollback()
666
    // {{{ rollback()
667
667
668
    /**
668
    /**
669
     * Reverts the current transaction
669
     * Reverts the current transaction
670
     *
670
     *
671
     * @return int  DB_OK on success.  A DB_Error object on failure.
671
     * @return int  DB_OK on success.  A DB_Error object on failure.
672
     */
672
     */
673
    function rollback()
673
    function rollback()
674
    {
674
    {
675
        return @ibase_rollback($this->connection);
675
        return @ibase_rollback($this->connection);
676
    }
676
    }
677
677
678
    // }}}
678
    // }}}
679
    // {{{ transactionInit()
679
    // {{{ transactionInit()
680
680
681
    function transactionInit($trans_args = 0)
681
    function transactionInit($trans_args = 0)
682
    {
682
    {
683
        return $trans_args
683
        return $trans_args
684
                ? @ibase_trans($trans_args, $this->connection)
684
                ? @ibase_trans($trans_args, $this->connection)
685
                : @ibase_trans();
685
                : @ibase_trans();
686
    }
686
    }
687
687
688
    // }}}
688
    // }}}
689
    // {{{ nextId()
689
    // {{{ nextId()
690
690
691
    /**
691
    /**
692
     * Returns the next free id in a sequence
692
     * Returns the next free id in a sequence
693
     *
693
     *
694
     * @param string  $seq_name  name of the sequence
694
     * @param string  $seq_name  name of the sequence
695
     * @param boolean $ondemand  when true, the seqence is automatically
695
     * @param boolean $ondemand  when true, the seqence is automatically
696
     *                            created if it does not exist
696
     *                            created if it does not exist
697
     *
697
     *
698
     * @return int  the next id number in the sequence.
698
     * @return int  the next id number in the sequence.
699
     *               A DB_Error object on failure.
699
     *               A DB_Error object on failure.
700
     *
700
     *
701
     * @see DB_common::nextID(), DB_common::getSequenceName(),
701
     * @see DB_common::nextID(), DB_common::getSequenceName(),
702
     *      DB_ibase::createSequence(), DB_ibase::dropSequence()
702
     *      DB_ibase::createSequence(), DB_ibase::dropSequence()
703
     */
703
     */
704
    function nextId($seq_name, $ondemand = true)
704
    function nextId($seq_name, $ondemand = true)
705
    {
705
    {
706
        $sqn = strtoupper($this->getSequenceName($seq_name));
706
        $sqn = strtoupper($this->getSequenceName($seq_name));
707
        $repeat = 0;
707
        $repeat = 0;
708
        do {
708
        do {
709
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
709
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
710
            $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
710
            $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
711
                                   . 'FROM RDB$GENERATORS '
711
                                   . 'FROM RDB$GENERATORS '
712
                                   . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
712
                                   . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
713
            $this->popErrorHandling();
713
            $this->popErrorHandling();
714
            if ($ondemand && DB::isError($result)) {
714
            if ($ondemand && DB::isError($result)) {
715
                $repeat = 1;
715
                $repeat = 1;
716
                $result = $this->createSequence($seq_name);
716
                $result = $this->createSequence($seq_name);
717
                if (DB::isError($result)) {
717
                if (DB::isError($result)) {
718
                    return $result;
718
                    return $result;
719
                }
719
                }
720
            } else {
720
            } else {
721
                $repeat = 0;
721
                $repeat = 0;
722
            }
722
            }
723
        } while ($repeat);
723
        } while ($repeat);
724
        if (DB::isError($result)) {
724
        if (DB::isError($result)) {
725
            return $this->raiseError($result);
725
            return $this->raiseError($result);
726
        }
726
        }
727
        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
727
        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
728
        $result->free();
728
        $result->free();
729
        return $arr[0];
729
        return $arr[0];
730
    }
730
    }
731
731
732
    // }}}
732
    // }}}
733
    // {{{ createSequence()
733
    // {{{ createSequence()
734
734
735
    /**
735
    /**
736
     * Creates a new sequence
736
     * Creates a new sequence
737
     *
737
     *
738
     * @param string $seq_name  name of the new sequence
738
     * @param string $seq_name  name of the new sequence
739
     *
739
     *
740
     * @return int  DB_OK on success.  A DB_Error object on failure.
740
     * @return int  DB_OK on success.  A DB_Error object on failure.
741
     *
741
     *
742
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
742
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
743
     *      DB_ibase::nextID(), DB_ibase::dropSequence()
743
     *      DB_ibase::nextID(), DB_ibase::dropSequence()
744
     */
744
     */
745
    function createSequence($seq_name)
745
    function createSequence($seq_name)
746
    {
746
    {
747
        $sqn = strtoupper($this->getSequenceName($seq_name));
747
        $sqn = strtoupper($this->getSequenceName($seq_name));
748
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
748
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
749
        $result = $this->query("CREATE GENERATOR ${sqn}");
749
        $result = $this->query("CREATE GENERATOR ${sqn}");
750
        $this->popErrorHandling();
750
        $this->popErrorHandling();
751
751
752
        return $result;
752
        return $result;
753
    }
753
    }
754
754
755
    // }}}
755
    // }}}
756
    // {{{ dropSequence()
756
    // {{{ dropSequence()
757
757
758
    /**
758
    /**
759
     * Deletes a sequence
759
     * Deletes a sequence
760
     *
760
     *
761
     * @param string $seq_name  name of the sequence to be deleted
761
     * @param string $seq_name  name of the sequence to be deleted
762
     *
762
     *
763
     * @return int  DB_OK on success.  A DB_Error object on failure.
763
     * @return int  DB_OK on success.  A DB_Error object on failure.
764
     *
764
     *
765
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
765
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
766
     *      DB_ibase::nextID(), DB_ibase::createSequence()
766
     *      DB_ibase::nextID(), DB_ibase::createSequence()
767
     */
767
     */
768
    function dropSequence($seq_name)
768
    function dropSequence($seq_name)
769
    {
769
    {
770
        return $this->query('DELETE FROM RDB$GENERATORS '
770
        return $this->query('DELETE FROM RDB$GENERATORS '
771
                            . "WHERE RDB\$GENERATOR_NAME='"
771
                            . "WHERE RDB\$GENERATOR_NAME='"
772
                            . strtoupper($this->getSequenceName($seq_name))
772
                            . strtoupper($this->getSequenceName($seq_name))
773
                            . "'");
773
                            . "'");
774
    }
774
    }
775
775
776
    // }}}
776
    // }}}
777
    // {{{ _ibaseFieldFlags()
777
    // {{{ _ibaseFieldFlags()
778
778
779
    /**
779
    /**
780
     * Get the column's flags
780
     * Get the column's flags
781
     *
781
     *
782
     * Supports "primary_key", "unique_key", "not_null", "default",
782
     * Supports "primary_key", "unique_key", "not_null", "default",
783
     * "computed" and "blob".
783
     * "computed" and "blob".
784
     *
784
     *
785
     * @param string $field_name  the name of the field
785
     * @param string $field_name  the name of the field
786
     * @param string $table_name  the name of the table
786
     * @param string $table_name  the name of the table
787
     *
787
     *
788
     * @return string  the flags
788
     * @return string  the flags
789
     *
789
     *
790
     * @access private
790
     * @access private
791
     */
791
     */
792
    function _ibaseFieldFlags($field_name, $table_name)
792
    function _ibaseFieldFlags($field_name, $table_name)
793
    {
793
    {
794
        $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
794
        $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
795
               .' FROM RDB$INDEX_SEGMENTS I'
795
               .' FROM RDB$INDEX_SEGMENTS I'
796
               .'  JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
796
               .'  JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
797
               .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
797
               .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
798
               .'  AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
798
               .'  AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
799
799
800
        $result = @ibase_query($this->connection, $sql);
800
        $result = @ibase_query($this->connection, $sql);
801
        if (!$result) {
801
        if (!$result) {
802
            return $this->ibaseRaiseError();
802
            return $this->ibaseRaiseError();
803
        }
803
        }
804
804
805
        $flags = '';
805
        $flags = '';
806
        if ($obj = @ibase_fetch_object($result)) {
806
        if ($obj = @ibase_fetch_object($result)) {
807
            @ibase_free_result($result);
807
            @ibase_free_result($result);
808
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
808
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
809
                $flags .= 'primary_key ';
809
                $flags .= 'primary_key ';
810
            }
810
            }
811
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
811
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
812
                $flags .= 'unique_key ';
812
                $flags .= 'unique_key ';
813
            }
813
            }
814
        }
814
        }
815
815
816
        $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
816
        $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
817
               .'  R.RDB$DEFAULT_SOURCE AS DSOURCE,'
817
               .'  R.RDB$DEFAULT_SOURCE AS DSOURCE,'
818
               .'  F.RDB$FIELD_TYPE AS FTYPE,'
818
               .'  F.RDB$FIELD_TYPE AS FTYPE,'
819
               .'  F.RDB$COMPUTED_SOURCE AS CSOURCE'
819
               .'  F.RDB$COMPUTED_SOURCE AS CSOURCE'
820
               .' FROM RDB$RELATION_FIELDS R '
820
               .' FROM RDB$RELATION_FIELDS R '
821
               .'  JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
821
               .'  JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
822
               .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
822
               .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
823
               .'  AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
823
               .'  AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
824
824
825
        $result = @ibase_query($this->connection, $sql);
825
        $result = @ibase_query($this->connection, $sql);
826
        if (!$result) {
826
        if (!$result) {
827
            return $this->ibaseRaiseError();
827
            return $this->ibaseRaiseError();
828
        }
828
        }
829
        if ($obj = @ibase_fetch_object($result)) {
829
        if ($obj = @ibase_fetch_object($result)) {
830
            @ibase_free_result($result);
830
            @ibase_free_result($result);
831
            if (isset($obj->NFLAG)) {
831
            if (isset($obj->NFLAG)) {
832
                $flags .= 'not_null ';
832
                $flags .= 'not_null ';
833
            }
833
            }
834
            if (isset($obj->DSOURCE)) {
834
            if (isset($obj->DSOURCE)) {
835
                $flags .= 'default ';
835
                $flags .= 'default ';
836
            }
836
            }
837
            if (isset($obj->CSOURCE)) {
837
            if (isset($obj->CSOURCE)) {
838
                $flags .= 'computed ';
838
                $flags .= 'computed ';
839
            }
839
            }
840
            if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
840
            if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
841
                $flags .= 'blob ';
841
                $flags .= 'blob ';
842
            }
842
            }
843
        }
843
        }
844
844
845
        return trim($flags);
845
        return trim($flags);
846
    }
846
    }
847
847
848
    // }}}
848
    // }}}
849
    // {{{ ibaseRaiseError()
849
    // {{{ ibaseRaiseError()
850
850
851
    /**
851
    /**
852
     * Produces a DB_Error object regarding the current problem
852
     * Produces a DB_Error object regarding the current problem
853
     *
853
     *
854
     * @param int $errno  if the error is being manually raised pass a
854
     * @param int $errno  if the error is being manually raised pass a
855
     *                     DB_ERROR* constant here.  If this isn't passed
855
     *                     DB_ERROR* constant here.  If this isn't passed
856
     *                     the error information gathered from the DBMS.
856
     *                     the error information gathered from the DBMS.
857
     *
857
     *
858
     * @return object  the DB_Error object
858
     * @return object  the DB_Error object
859
     *
859
     *
860
     * @see DB_common::raiseError(),
860
     * @see DB_common::raiseError(),
861
     *      DB_ibase::errorNative(), DB_ibase::errorCode()
861
     *      DB_ibase::errorNative(), DB_ibase::errorCode()
862
     */
862
     */
863
    function &ibaseRaiseError($errno = null)
863
    function &ibaseRaiseError($errno = null)
864
    {
864
    {
865
        if ($errno === null) {
865
        if ($errno === null) {
866
            $errno = $this->errorCode($this->errorNative());
866
            $errno = $this->errorCode($this->errorNative());
867
        }
867
        }
868
        $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
868
        $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
869
        return $tmp;
869
        return $tmp;
870
    }
870
    }
871
871
872
    // }}}
872
    // }}}
873
    // {{{ errorNative()
873
    // {{{ errorNative()
874
874
875
    /**
875
    /**
876
     * Gets the DBMS' native error code produced by the last query
876
     * Gets the DBMS' native error code produced by the last query
877
     *
877
     *
878
     * @return int  the DBMS' error code.  NULL if there is no error code.
878
     * @return int  the DBMS' error code.  NULL if there is no error code.
879
     *
879
     *
880
     * @since Method available since Release 1.7.0
880
     * @since Method available since Release 1.7.0
881
     */
881
     */
882
    function errorNative()
882
    function errorNative()
883
    {
883
    {
884
        if (function_exists('ibase_errcode')) {
884
        if (function_exists('ibase_errcode')) {
885
            return @ibase_errcode();
885
            return @ibase_errcode();
886
        }
886
        }
887
        if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
887
        if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
888
                       @ibase_errmsg(), $m)) {
888
                       @ibase_errmsg(), $m)) {
889
            return (int)$m[1];
889
            return (int)$m[1];
890
        }
890
        }
891
        return null;
891
        return null;
892
    }
892
    }
893
893
894
    // }}}
894
    // }}}
895
    // {{{ errorCode()
895
    // {{{ errorCode()
896
896
897
    /**
897
    /**
898
     * Maps native error codes to DB's portable ones
898
     * Maps native error codes to DB's portable ones
899
     *
899
     *
900
     * @param int $nativecode  the error code returned by the DBMS
900
     * @param int $nativecode  the error code returned by the DBMS
901
     *
901
     *
902
     * @return int  the portable DB error code.  Return DB_ERROR if the
902
     * @return int  the portable DB error code.  Return DB_ERROR if the
903
     *               current driver doesn't have a mapping for the
903
     *               current driver doesn't have a mapping for the
904
     *               $nativecode submitted.
904
     *               $nativecode submitted.
905
     *
905
     *
906
     * @since Method available since Release 1.7.0
906
     * @since Method available since Release 1.7.0
907
     */
907
     */
908
    function errorCode($nativecode = null)
908
    function errorCode($nativecode = null)
909
    {
909
    {
910
        if (isset($this->errorcode_map[$nativecode])) {
910
        if (isset($this->errorcode_map[$nativecode])) {
911
            return $this->errorcode_map[$nativecode];
911
            return $this->errorcode_map[$nativecode];
912
        }
912
        }
913
913
914
        static $error_regexps;
914
        static $error_regexps;
915
        if (!isset($error_regexps)) {
915
        if (!isset($error_regexps)) {
916
            $error_regexps = array(
916
            $error_regexps = array(
917
                '/generator .* is not defined/'
917
                '/generator .* is not defined/'
918
                    => DB_ERROR_SYNTAX,  // for compat. w ibase_errcode()
918
                    => DB_ERROR_SYNTAX,  // for compat. w ibase_errcode()
919
                '/table.*(not exist|not found|unknown)/i'
919
                '/table.*(not exist|not found|unknown)/i'
920
                    => DB_ERROR_NOSUCHTABLE,
920
                    => DB_ERROR_NOSUCHTABLE,
921
                '/table .* already exists/i'
921
                '/table .* already exists/i'
922
                    => DB_ERROR_ALREADY_EXISTS,
922
                    => DB_ERROR_ALREADY_EXISTS,
923
                '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
923
                '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
924
                    => DB_ERROR_ALREADY_EXISTS,
924
                    => DB_ERROR_ALREADY_EXISTS,
925
                '/unsuccessful metadata update .* not found/i'
925
                '/unsuccessful metadata update .* not found/i'
926
                    => DB_ERROR_NOT_FOUND,
926
                    => DB_ERROR_NOT_FOUND,
927
                '/validation error for column .* value "\*\*\* null/i'
927
                '/validation error for column .* value "\*\*\* null/i'
928
                    => DB_ERROR_CONSTRAINT_NOT_NULL,
928
                    => DB_ERROR_CONSTRAINT_NOT_NULL,
929
                '/violation of [\w ]+ constraint/i'
929
                '/violation of [\w ]+ constraint/i'
930
                    => DB_ERROR_CONSTRAINT,
930
                    => DB_ERROR_CONSTRAINT,
931
                '/conversion error from string/i'
931
                '/conversion error from string/i'
932
                    => DB_ERROR_INVALID_NUMBER,
932
                    => DB_ERROR_INVALID_NUMBER,
933
                '/no permission for/i'
933
                '/no permission for/i'
934
                    => DB_ERROR_ACCESS_VIOLATION,
934
                    => DB_ERROR_ACCESS_VIOLATION,
935
                '/arithmetic exception, numeric overflow, or string truncation/i'
935
                '/arithmetic exception, numeric overflow, or string truncation/i'
936
                    => DB_ERROR_INVALID,
936
                    => DB_ERROR_INVALID,
937
                '/feature is not supported/i'
937
                '/feature is not supported/i'
938
                    => DB_ERROR_NOT_CAPABLE,
938
                    => DB_ERROR_NOT_CAPABLE,
939
            );
939
            );
940
        }
940
        }
941
941
942
        $errormsg = @ibase_errmsg();
942
        $errormsg = @ibase_errmsg();
943
        foreach ($error_regexps as $regexp => $code) {
943
        foreach ($error_regexps as $regexp => $code) {
944
            if (preg_match($regexp, $errormsg)) {
944
            if (preg_match($regexp, $errormsg)) {
945
                return $code;
945
                return $code;
946
            }
946
            }
947
        }
947
        }
948
        return DB_ERROR;
948
        return DB_ERROR;
949
    }
949
    }
950
950
951
    // }}}
951
    // }}}
952
    // {{{ tableInfo()
952
    // {{{ tableInfo()
953
953
954
    /**
954
    /**
955
     * Returns information about a table or a result set
955
     * Returns information about a table or a result set
956
     *
956
     *
957
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
957
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
958
     * is a table name.
958
     * is a table name.
959
     *
959
     *
960
     * @param object|string  $result  DB_result object from a query or a
960
     * @param object|string  $result  DB_result object from a query or a
961
     *                                 string containing the name of a table.
961
     *                                 string containing the name of a table.
962
     *                                 While this also accepts a query result
962
     *                                 While this also accepts a query result
963
     *                                 resource identifier, this behavior is
963
     *                                 resource identifier, this behavior is
964
     *                                 deprecated.
964
     *                                 deprecated.
965
     * @param int            $mode    a valid tableInfo mode
965
     * @param int            $mode    a valid tableInfo mode
966
     *
966
     *
967
     * @return array  an associative array with the information requested.
967
     * @return array  an associative array with the information requested.
968
     *                 A DB_Error object on failure.
968
     *                 A DB_Error object on failure.
969
     *
969
     *
970
     * @see DB_common::tableInfo()
970
     * @see DB_common::tableInfo()
971
     */
971
     */
972
    function tableInfo($result, $mode = null)
972
    function tableInfo($result, $mode = null)
973
    {
973
    {
974
        if (is_string($result)) {
974
        if (is_string($result)) {
975
            /*
975
            /*
976
             * Probably received a table name.
976
             * Probably received a table name.
977
             * Create a result resource identifier.
977
             * Create a result resource identifier.
978
             */
978
             */
979
            $id = @ibase_query($this->connection,
979
            $id = @ibase_query($this->connection,
980
                               "SELECT * FROM $result WHERE 1=0");
980
                               "SELECT * FROM $result WHERE 1=0");
981
            $got_string = true;
981
            $got_string = true;
982
        } elseif (isset($result->result)) {
982
        } elseif (isset($result->result)) {
983
            /*
983
            /*
984
             * Probably received a result object.
984
             * Probably received a result object.
985
             * Extract the result resource identifier.
985
             * Extract the result resource identifier.
986
             */
986
             */
987
            $id = $result->result;
987
            $id = $result->result;
988
            $got_string = false;
988
            $got_string = false;
989
        } else {
989
        } else {
990
            /*
990
            /*
991
             * Probably received a result resource identifier.
991
             * Probably received a result resource identifier.
992
             * Copy it.
992
             * Copy it.
993
             * Deprecated.  Here for compatibility only.
993
             * Deprecated.  Here for compatibility only.
994
             */
994
             */
995
            $id = $result;
995
            $id = $result;
996
            $got_string = false;
996
            $got_string = false;
997
        }
997
        }
998
998
999
        if (!is_resource($id)) {
999
        if (!is_resource($id)) {
1000
            return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
1000
            return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
1001
        }
1001
        }
1002
1002
1003
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
1003
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
1004
            $case_func = 'strtolower';
1004
            $case_func = 'strtolower';
1005
        } else {
1005
        } else {
1006
            $case_func = 'strval';
1006
            $case_func = 'strval';
1007
        }
1007
        }
1008
1008
1009
        $count = @ibase_num_fields($id);
1009
        $count = @ibase_num_fields($id);
1010
        $res   = array();
1010
        $res   = array();
1011
1011
1012
        if ($mode) {
1012
        if ($mode) {
1013
            $res['num_fields'] = $count;
1013
            $res['num_fields'] = $count;
1014
        }
1014
        }
1015
1015
1016
        for ($i = 0; $i < $count; $i++) {
1016
        for ($i = 0; $i < $count; $i++) {
1017
            $info = @ibase_field_info($id, $i);
1017
            $info = @ibase_field_info($id, $i);
1018
            $res[$i] = array(
1018
            $res[$i] = array(
1019
                'table' => $got_string ? $case_func($result) : '',
1019
                'table' => $got_string ? $case_func($result) : '',
1020
                'name'  => $case_func($info['name']),
1020
                'name'  => $case_func($info['name']),
1021
                'type'  => $info['type'],
1021
                'type'  => $info['type'],
1022
                'len'   => $info['length'],
1022
                'len'   => $info['length'],
1023
                'flags' => ($got_string)
1023
                'flags' => ($got_string)
1024
                            ? $this->_ibaseFieldFlags($info['name'], $result)
1024
                            ? $this->_ibaseFieldFlags($info['name'], $result)
1025
                            : '',
1025
                            : '',
1026
            );
1026
            );
1027
            if ($mode & DB_TABLEINFO_ORDER) {
1027
            if ($mode & DB_TABLEINFO_ORDER) {
1028
                $res['order'][$res[$i]['name']] = $i;
1028
                $res['order'][$res[$i]['name']] = $i;
1029
            }
1029
            }
1030
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
1030
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
1031
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1031
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1032
            }
1032
            }
1033
        }
1033
        }
1034
1034
1035
        // free the result only if we were called on a table
1035
        // free the result only if we were called on a table
1036
        if ($got_string) {
1036
        if ($got_string) {
1037
            @ibase_free_result($id);
1037
            @ibase_free_result($id);
1038
        }
1038
        }
1039
        return $res;
1039
        return $res;
1040
    }
1040
    }
1041
1041
1042
    // }}}
1042
    // }}}
1043
    // {{{ getSpecialQuery()
1043
    // {{{ getSpecialQuery()
1044
1044
1045
    /**
1045
    /**
1046
     * Obtains the query string needed for listing a given type of objects
1046
     * Obtains the query string needed for listing a given type of objects
1047
     *
1047
     *
1048
     * @param string $type  the kind of objects you want to retrieve
1048
     * @param string $type  the kind of objects you want to retrieve
1049
     *
1049
     *
1050
     * @return string  the SQL query string or null if the driver doesn't
1050
     * @return string  the SQL query string or null if the driver doesn't
1051
     *                  support the object type requested
1051
     *                  support the object type requested
1052
     *
1052
     *
1053
     * @access protected
1053
     * @access protected
1054
     * @see DB_common::getListOf()
1054
     * @see DB_common::getListOf()
1055
     */
1055
     */
1056
    function getSpecialQuery($type)
1056
    function getSpecialQuery($type)
1057
    {
1057
    {
1058
        switch ($type) {
1058
        switch ($type) {
1059
            case 'tables':
1059
            case 'tables':
1060
                return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
1060
                return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
1061
                       . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
1061
                       . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
1062
            case 'views':
1062
            case 'views':
1063
                return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
1063
                return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
1064
            case 'users':
1064
            case 'users':
1065
                return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
1065
                return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
1066
            default:
1066
            default:
1067
                return null;
1067
                return null;
1068
        }
1068
        }
1069
    }
1069
    }
1070
1070
1071
    // }}}
1071
    // }}}
1072
1072
1073
}
1073
}
1074
1074
1075
/*
1075
/*
1076
 * Local variables:
1076
 * Local variables:
1077
 * tab-width: 4
1077
 * tab-width: 4
1078
 * c-basic-offset: 4
1078
 * c-basic-offset: 4
1079
 * End:
1079
 * End:
1080
 */
1080
 */
1081
1081
1082
?>
1082
?>
1083
 
1083