Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * The PEAR DB driver for PHP's sqlite extension
7
 * for interacting with SQLite databases
8
 *
187 mathias 9
 * PHP version 5
94 jpm 10
 *
11
 * LICENSE: This source file is subject to version 3.0 of the PHP license
12
 * that is available through the world-wide-web at the following URI:
13
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14
 * the PHP License and are unable to obtain it through the web, please
15
 * send a note to license@php.net so we can mail you a copy immediately.
16
 *
17
 * @category   Database
18
 * @package    DB
19
 * @author     Urs Gehrig <urs@circle.ch>
20
 * @author     Mika Tuupola <tuupola@appelsiini.net>
21
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 22
 * @copyright  1997-2007 The PHP Group
94 jpm 23
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0 3.0
187 mathias 24
 * @version    CVS: $Id$
94 jpm 25
 * @link       http://pear.php.net/package/DB
26
 */
27
 
28
/**
29
 * Obtain the DB_common class so it can be extended from
30
 */
31
require_once 'DB/common.php';
32
 
33
/**
34
 * The methods PEAR DB uses to interact with PHP's sqlite extension
35
 * for interacting with SQLite databases
36
 *
37
 * These methods overload the ones declared in DB_common.
38
 *
39
 * NOTICE:  This driver needs PHP's track_errors ini setting to be on.
40
 * It is automatically turned on when connecting to the database.
41
 * Make sure your scripts don't turn it off.
42
 *
43
 * @category   Database
44
 * @package    DB
45
 * @author     Urs Gehrig <urs@circle.ch>
46
 * @author     Mika Tuupola <tuupola@appelsiini.net>
47
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 48
 * @copyright  1997-2007 The PHP Group
94 jpm 49
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0 3.0
187 mathias 50
 * @version    Release: 1.9.2
94 jpm 51
 * @link       http://pear.php.net/package/DB
52
 */
53
class DB_sqlite extends DB_common
54
{
55
    // {{{ properties
56
 
57
    /**
58
     * The DB driver type (mysql, oci8, odbc, etc.)
59
     * @var string
60
     */
61
    var $phptype = 'sqlite';
62
 
63
    /**
64
     * The database syntax variant to be used (db2, access, etc.), if any
65
     * @var string
66
     */
67
    var $dbsyntax = 'sqlite';
68
 
69
    /**
70
     * The capabilities of this DB implementation
71
     *
72
     * The 'new_link' element contains the PHP version that first provided
73
     * new_link support for this DBMS.  Contains false if it's unsupported.
74
     *
75
     * Meaning of the 'limit' element:
76
     *   + 'emulate' = emulate with fetch row by number
77
     *   + 'alter'   = alter the query
78
     *   + false     = skip rows
79
     *
80
     * @var array
81
     */
82
    var $features = array(
83
        'limit'         => 'alter',
84
        'new_link'      => false,
85
        'numrows'       => true,
86
        'pconnect'      => true,
87
        'prepare'       => false,
88
        'ssl'           => false,
89
        'transactions'  => false,
90
    );
91
 
92
    /**
93
     * A mapping of native error codes to DB error codes
94
     *
95
     * {@internal  Error codes according to sqlite_exec.  See the online
96
     * manual at http://sqlite.org/c_interface.html for info.
97
     * This error handling based on sqlite_exec is not yet implemented.}}
98
     *
99
     * @var array
100
     */
101
    var $errorcode_map = array(
102
    );
103
 
104
    /**
105
     * The raw database connection created by PHP
106
     * @var resource
107
     */
108
    var $connection;
109
 
110
    /**
111
     * The DSN information for connecting to a database
112
     * @var array
113
     */
114
    var $dsn = array();
115
 
116
 
117
    /**
118
     * SQLite data types
119
     *
120
     * @link http://www.sqlite.org/datatypes.html
121
     *
122
     * @var array
123
     */
124
    var $keywords = array (
125
        'BLOB'      => '',
126
        'BOOLEAN'   => '',
127
        'CHARACTER' => '',
128
        'CLOB'      => '',
129
        'FLOAT'     => '',
130
        'INTEGER'   => '',
131
        'KEY'       => '',
132
        'NATIONAL'  => '',
133
        'NUMERIC'   => '',
134
        'NVARCHAR'  => '',
135
        'PRIMARY'   => '',
136
        'TEXT'      => '',
137
        'TIMESTAMP' => '',
138
        'UNIQUE'    => '',
139
        'VARCHAR'   => '',
140
        'VARYING'   => '',
141
    );
142
 
143
    /**
144
     * The most recent error message from $php_errormsg
145
     * @var string
146
     * @access private
147
     */
148
    var $_lasterror = '';
149
 
150
 
151
    // }}}
152
    // {{{ constructor
153
 
154
    /**
187 mathias 155
     * This constructor calls <kbd>parent::__construct()</kbd>
94 jpm 156
     *
157
     * @return void
158
     */
187 mathias 159
    function __construct()
94 jpm 160
    {
187 mathias 161
        parent::__construct();
94 jpm 162
    }
163
 
164
    // }}}
165
    // {{{ connect()
166
 
167
    /**
168
     * Connect to the database server, log in and open the database
169
     *
170
     * Don't call this method directly.  Use DB::connect() instead.
171
     *
172
     * PEAR DB's sqlite driver supports the following extra DSN options:
173
     *   + mode  The permissions for the database file, in four digit
174
     *            chmod octal format (eg "0600").
175
     *
176
     * Example of connecting to a database in read-only mode:
177
     * <code>
178
     * require_once 'DB.php';
179
     *
180
     * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400';
181
     * $options = array(
182
     *     'portability' => DB_PORTABILITY_ALL,
183
     * );
184
     *
187 mathias 185
     * $db = DB::connect($dsn, $options);
94 jpm 186
     * if (PEAR::isError($db)) {
187
     *     die($db->getMessage());
188
     * }
189
     * </code>
190
     *
191
     * @param array $dsn         the data source name
192
     * @param bool  $persistent  should the connection be persistent?
193
     *
194
     * @return int  DB_OK on success. A DB_Error object on failure.
195
     */
196
    function connect($dsn, $persistent = false)
197
    {
198
        if (!PEAR::loadExtension('sqlite')) {
199
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
200
        }
201
 
202
        $this->dsn = $dsn;
203
        if ($dsn['dbsyntax']) {
204
            $this->dbsyntax = $dsn['dbsyntax'];
205
        }
206
 
187 mathias 207
        if (!$dsn['database']) {
208
            return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
209
        }
210
 
211
        if ($dsn['database'] !== ':memory:') {
94 jpm 212
            if (!file_exists($dsn['database'])) {
213
                if (!touch($dsn['database'])) {
214
                    return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
215
                }
216
                if (!isset($dsn['mode']) ||
217
                    !is_numeric($dsn['mode']))
218
                {
219
                    $mode = 0644;
220
                } else {
221
                    $mode = octdec($dsn['mode']);
222
                }
223
                if (!chmod($dsn['database'], $mode)) {
224
                    return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
225
                }
226
                if (!file_exists($dsn['database'])) {
227
                    return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND);
228
                }
229
            }
230
            if (!is_file($dsn['database'])) {
231
                return $this->sqliteRaiseError(DB_ERROR_INVALID);
232
            }
233
            if (!is_readable($dsn['database'])) {
234
                return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION);
235
            }
236
        }
237
 
238
        $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open';
239
 
240
        // track_errors must remain on for simpleQuery()
187 mathias 241
        @ini_set('track_errors', 1);
94 jpm 242
        $php_errormsg = '';
243
 
244
        if (!$this->connection = @$connect_function($dsn['database'])) {
245
            return $this->raiseError(DB_ERROR_NODBSELECTED,
246
                                     null, null, null,
247
                                     $php_errormsg);
248
        }
249
        return DB_OK;
250
    }
251
 
252
    // }}}
253
    // {{{ disconnect()
254
 
255
    /**
256
     * Disconnects from the database server
257
     *
258
     * @return bool  TRUE on success, FALSE on failure
259
     */
260
    function disconnect()
261
    {
262
        $ret = @sqlite_close($this->connection);
263
        $this->connection = null;
264
        return $ret;
265
    }
266
 
267
    // }}}
268
    // {{{ simpleQuery()
269
 
270
    /**
271
     * Sends a query to the database server
272
     *
273
     * NOTICE:  This method needs PHP's track_errors ini setting to be on.
274
     * It is automatically turned on when connecting to the database.
275
     * Make sure your scripts don't turn it off.
276
     *
277
     * @param string  the SQL query string
278
     *
279
     * @return mixed  + a PHP result resrouce for successful SELECT queries
280
     *                + the DB_OK constant for other successful queries
281
     *                + a DB_Error object on failure
282
     */
283
    function simpleQuery($query)
284
    {
187 mathias 285
        $ismanip = $this->_checkManip($query);
94 jpm 286
        $this->last_query = $query;
287
        $query = $this->modifyQuery($query);
288
 
289
        $php_errormsg = '';
290
 
291
        $result = @sqlite_query($query, $this->connection);
292
        $this->_lasterror = $php_errormsg ? $php_errormsg : '';
293
 
294
        $this->result = $result;
295
        if (!$this->result) {
296
            return $this->sqliteRaiseError(null);
297
        }
298
 
299
        // sqlite_query() seems to allways return a resource
300
        // so cant use that. Using $ismanip instead
301
        if (!$ismanip) {
302
            $numRows = $this->numRows($result);
303
            if (is_object($numRows)) {
304
                // we've got PEAR_Error
305
                return $numRows;
306
            }
307
            return $result;
308
        }
309
        return DB_OK;
310
    }
311
 
312
    // }}}
313
    // {{{ nextResult()
314
 
315
    /**
316
     * Move the internal sqlite result pointer to the next available result
317
     *
318
     * @param resource $result  the valid sqlite result resource
319
     *
320
     * @return bool  true if a result is available otherwise return false
321
     */
322
    function nextResult($result)
323
    {
324
        return false;
325
    }
326
 
327
    // }}}
328
    // {{{ fetchInto()
329
 
330
    /**
331
     * Places a row from the result set into the given array
332
     *
333
     * Formating of the array and the data therein are configurable.
334
     * See DB_result::fetchInto() for more information.
335
     *
336
     * This method is not meant to be called directly.  Use
337
     * DB_result::fetchInto() instead.  It can't be declared "protected"
338
     * because DB_result is a separate object.
339
     *
340
     * @param resource $result    the query result resource
341
     * @param array    $arr       the referenced array to put the data in
342
     * @param int      $fetchmode how the resulting array should be indexed
343
     * @param int      $rownum    the row number to fetch (0 = first row)
344
     *
345
     * @return mixed  DB_OK on success, NULL when the end of a result set is
346
     *                 reached or on failure
347
     *
348
     * @see DB_result::fetchInto()
349
     */
350
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
351
    {
352
        if ($rownum !== null) {
353
            if (!@sqlite_seek($this->result, $rownum)) {
354
                return null;
355
            }
356
        }
357
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
358
            $arr = @sqlite_fetch_array($result, SQLITE_ASSOC);
359
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
360
                $arr = array_change_key_case($arr, CASE_LOWER);
361
            }
187 mathias 362
 
363
            /* Remove extraneous " characters from the fields in the result.
364
             * Fixes bug #11716. */
365
            if (is_array($arr) && count($arr) > 0) {
366
                $strippedArr = array();
367
                foreach ($arr as $field => $value) {
368
                    $strippedArr[trim($field, '"')] = $value;
369
                }
370
                $arr = $strippedArr;
371
            }
94 jpm 372
        } else {
373
            $arr = @sqlite_fetch_array($result, SQLITE_NUM);
374
        }
375
        if (!$arr) {
376
            return null;
377
        }
378
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
379
            /*
380
             * Even though this DBMS already trims output, we do this because
381
             * a field might have intentional whitespace at the end that
382
             * gets removed by DB_PORTABILITY_RTRIM under another driver.
383
             */
384
            $this->_rtrimArrayValues($arr);
385
        }
386
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
387
            $this->_convertNullArrayValuesToEmpty($arr);
388
        }
389
        return DB_OK;
390
    }
391
 
392
    // }}}
393
    // {{{ freeResult()
394
 
395
    /**
396
     * Deletes the result set and frees the memory occupied by the result set
397
     *
398
     * This method is not meant to be called directly.  Use
399
     * DB_result::free() instead.  It can't be declared "protected"
400
     * because DB_result is a separate object.
401
     *
402
     * @param resource $result  PHP's query result resource
403
     *
404
     * @return bool  TRUE on success, FALSE if $result is invalid
405
     *
406
     * @see DB_result::free()
407
     */
408
    function freeResult(&$result)
409
    {
410
        // XXX No native free?
411
        if (!is_resource($result)) {
412
            return false;
413
        }
414
        $result = null;
415
        return true;
416
    }
417
 
418
    // }}}
419
    // {{{ numCols()
420
 
421
    /**
422
     * Gets the number of columns in a result set
423
     *
424
     * This method is not meant to be called directly.  Use
425
     * DB_result::numCols() instead.  It can't be declared "protected"
426
     * because DB_result is a separate object.
427
     *
428
     * @param resource $result  PHP's query result resource
429
     *
430
     * @return int  the number of columns.  A DB_Error object on failure.
431
     *
432
     * @see DB_result::numCols()
433
     */
434
    function numCols($result)
435
    {
436
        $cols = @sqlite_num_fields($result);
437
        if (!$cols) {
438
            return $this->sqliteRaiseError();
439
        }
440
        return $cols;
441
    }
442
 
443
    // }}}
444
    // {{{ numRows()
445
 
446
    /**
447
     * Gets the number of rows in a result set
448
     *
449
     * This method is not meant to be called directly.  Use
450
     * DB_result::numRows() instead.  It can't be declared "protected"
451
     * because DB_result is a separate object.
452
     *
453
     * @param resource $result  PHP's query result resource
454
     *
455
     * @return int  the number of rows.  A DB_Error object on failure.
456
     *
457
     * @see DB_result::numRows()
458
     */
459
    function numRows($result)
460
    {
461
        $rows = @sqlite_num_rows($result);
462
        if ($rows === null) {
463
            return $this->sqliteRaiseError();
464
        }
465
        return $rows;
466
    }
467
 
468
    // }}}
469
    // {{{ affected()
470
 
471
    /**
472
     * Determines the number of rows affected by a data maniuplation query
473
     *
474
     * 0 is returned for queries that don't manipulate data.
475
     *
476
     * @return int  the number of rows.  A DB_Error object on failure.
477
     */
478
    function affectedRows()
479
    {
480
        return @sqlite_changes($this->connection);
481
    }
482
 
483
    // }}}
484
    // {{{ dropSequence()
485
 
486
    /**
487
     * Deletes a sequence
488
     *
489
     * @param string $seq_name  name of the sequence to be deleted
490
     *
491
     * @return int  DB_OK on success.  A DB_Error object on failure.
492
     *
493
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
494
     *      DB_sqlite::nextID(), DB_sqlite::createSequence()
495
     */
496
    function dropSequence($seq_name)
497
    {
498
        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
499
    }
500
 
501
    /**
502
     * Creates a new sequence
503
     *
504
     * @param string $seq_name  name of the new sequence
505
     *
506
     * @return int  DB_OK on success.  A DB_Error object on failure.
507
     *
508
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
509
     *      DB_sqlite::nextID(), DB_sqlite::dropSequence()
510
     */
511
    function createSequence($seq_name)
512
    {
513
        $seqname = $this->getSequenceName($seq_name);
514
        $query   = 'CREATE TABLE ' . $seqname .
515
                   ' (id INTEGER UNSIGNED PRIMARY KEY) ';
516
        $result  = $this->query($query);
517
        if (DB::isError($result)) {
518
            return($result);
519
        }
520
        $query   = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname
521
                    BEGIN
522
                        DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID();
523
                    END ";
524
        $result  = $this->query($query);
525
        if (DB::isError($result)) {
526
            return($result);
527
        }
528
    }
529
 
530
    // }}}
531
    // {{{ nextId()
532
 
533
    /**
534
     * Returns the next free id in a sequence
535
     *
536
     * @param string  $seq_name  name of the sequence
537
     * @param boolean $ondemand  when true, the seqence is automatically
538
     *                            created if it does not exist
539
     *
540
     * @return int  the next id number in the sequence.
541
     *               A DB_Error object on failure.
542
     *
543
     * @see DB_common::nextID(), DB_common::getSequenceName(),
544
     *      DB_sqlite::createSequence(), DB_sqlite::dropSequence()
545
     */
546
    function nextId($seq_name, $ondemand = true)
547
    {
548
        $seqname = $this->getSequenceName($seq_name);
549
 
550
        do {
551
            $repeat = 0;
552
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
553
            $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)");
554
            $this->popErrorHandling();
555
            if ($result === DB_OK) {
556
                $id = @sqlite_last_insert_rowid($this->connection);
557
                if ($id != 0) {
558
                    return $id;
559
                }
560
            } elseif ($ondemand && DB::isError($result) &&
561
                      $result->getCode() == DB_ERROR_NOSUCHTABLE)
562
            {
563
                $result = $this->createSequence($seq_name);
564
                if (DB::isError($result)) {
565
                    return $this->raiseError($result);
566
                } else {
567
                    $repeat = 1;
568
                }
569
            }
570
        } while ($repeat);
571
 
572
        return $this->raiseError($result);
573
    }
574
 
575
    // }}}
576
    // {{{ getDbFileStats()
577
 
578
    /**
579
     * Get the file stats for the current database
580
     *
581
     * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size,
582
     * atime, mtime, ctime, blksize, blocks or a numeric key between
583
     * 0 and 12.
584
     *
585
     * @param string $arg  the array key for stats()
586
     *
587
     * @return mixed  an array on an unspecified key, integer on a passed
588
     *                arg and false at a stats error
589
     */
590
    function getDbFileStats($arg = '')
591
    {
592
        $stats = stat($this->dsn['database']);
593
        if ($stats == false) {
594
            return false;
595
        }
596
        if (is_array($stats)) {
597
            if (is_numeric($arg)) {
598
                if (((int)$arg <= 12) & ((int)$arg >= 0)) {
599
                    return false;
600
                }
601
                return $stats[$arg ];
602
            }
603
            if (array_key_exists(trim($arg), $stats)) {
604
                return $stats[$arg ];
605
            }
606
        }
607
        return $stats;
608
    }
609
 
610
    // }}}
611
    // {{{ escapeSimple()
612
 
613
    /**
614
     * Escapes a string according to the current DBMS's standards
615
     *
616
     * In SQLite, this makes things safe for inserts/updates, but may
617
     * cause problems when performing text comparisons against columns
618
     * containing binary data. See the
619
     * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
620
     *
621
     * @param string $str  the string to be escaped
622
     *
623
     * @return string  the escaped string
624
     *
625
     * @since Method available since Release 1.6.1
626
     * @see DB_common::escapeSimple()
627
     */
628
    function escapeSimple($str)
629
    {
630
        return @sqlite_escape_string($str);
631
    }
632
 
633
    // }}}
634
    // {{{ modifyLimitQuery()
635
 
636
    /**
637
     * Adds LIMIT clauses to a query string according to current DBMS standards
638
     *
639
     * @param string $query   the query to modify
640
     * @param int    $from    the row to start to fetching (0 = the first row)
641
     * @param int    $count   the numbers of rows to fetch
642
     * @param mixed  $params  array, string or numeric data to be used in
643
     *                         execution of the statement.  Quantity of items
644
     *                         passed must match quantity of placeholders in
645
     *                         query:  meaning 1 placeholder for non-array
646
     *                         parameters or 1 placeholder per array element.
647
     *
648
     * @return string  the query string with LIMIT clauses added
649
     *
650
     * @access protected
651
     */
652
    function modifyLimitQuery($query, $from, $count, $params = array())
653
    {
654
        return "$query LIMIT $count OFFSET $from";
655
    }
656
 
657
    // }}}
658
    // {{{ modifyQuery()
659
 
660
    /**
661
     * Changes a query string for various DBMS specific reasons
662
     *
663
     * This little hack lets you know how many rows were deleted
664
     * when running a "DELETE FROM table" query.  Only implemented
665
     * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
666
     *
667
     * @param string $query  the query string to modify
668
     *
669
     * @return string  the modified query string
670
     *
671
     * @access protected
672
     * @see DB_common::setOption()
673
     */
674
    function modifyQuery($query)
675
    {
676
        if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
677
            if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
678
                $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
679
                                      'DELETE FROM \1 WHERE 1=1', $query);
680
            }
681
        }
682
        return $query;
683
    }
684
 
685
    // }}}
686
    // {{{ sqliteRaiseError()
687
 
688
    /**
689
     * Produces a DB_Error object regarding the current problem
690
     *
691
     * @param int $errno  if the error is being manually raised pass a
692
     *                     DB_ERROR* constant here.  If this isn't passed
693
     *                     the error information gathered from the DBMS.
694
     *
695
     * @return object  the DB_Error object
696
     *
697
     * @see DB_common::raiseError(),
698
     *      DB_sqlite::errorNative(), DB_sqlite::errorCode()
699
     */
700
    function sqliteRaiseError($errno = null)
701
    {
702
        $native = $this->errorNative();
703
        if ($errno === null) {
704
            $errno = $this->errorCode($native);
705
        }
706
 
707
        $errorcode = @sqlite_last_error($this->connection);
708
        $userinfo = "$errorcode ** $this->last_query";
709
 
710
        return $this->raiseError($errno, null, null, $userinfo, $native);
711
    }
712
 
713
    // }}}
714
    // {{{ errorNative()
715
 
716
    /**
717
     * Gets the DBMS' native error message produced by the last query
718
     *
719
     * {@internal This is used to retrieve more meaningfull error messages
720
     * because sqlite_last_error() does not provide adequate info.}}
721
     *
722
     * @return string  the DBMS' error message
723
     */
724
    function errorNative()
725
    {
726
        return $this->_lasterror;
727
    }
728
 
729
    // }}}
730
    // {{{ errorCode()
731
 
732
    /**
733
     * Determines PEAR::DB error code from the database's text error message
734
     *
735
     * @param string $errormsg  the error message returned from the database
736
     *
737
     * @return integer  the DB error number
738
     */
739
    function errorCode($errormsg)
740
    {
741
        static $error_regexps;
187 mathias 742
 
743
        // PHP 5.2+ prepends the function name to $php_errormsg, so we need
744
        // this hack to work around it, per bug #9599.
745
        $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg);
746
 
94 jpm 747
        if (!isset($error_regexps)) {
748
            $error_regexps = array(
749
                '/^no such table:/' => DB_ERROR_NOSUCHTABLE,
750
                '/^no such index:/' => DB_ERROR_NOT_FOUND,
751
                '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS,
752
                '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT,
753
                '/is not unique/' => DB_ERROR_CONSTRAINT,
754
                '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT,
755
                '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT,
756
                '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL,
757
                '/^no such column:/' => DB_ERROR_NOSUCHFIELD,
187 mathias 758
                '/no column named/' => DB_ERROR_NOSUCHFIELD,
94 jpm 759
                '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD,
760
                '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX,
761
                '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW,
762
            );
763
        }
764
        foreach ($error_regexps as $regexp => $code) {
765
            if (preg_match($regexp, $errormsg)) {
766
                return $code;
767
            }
768
        }
769
        // Fall back to DB_ERROR if there was no mapping.
770
        return DB_ERROR;
771
    }
772
 
773
    // }}}
774
    // {{{ tableInfo()
775
 
776
    /**
777
     * Returns information about a table
778
     *
779
     * @param string         $result  a string containing the name of a table
780
     * @param int            $mode    a valid tableInfo mode
781
     *
782
     * @return array  an associative array with the information requested.
783
     *                 A DB_Error object on failure.
784
     *
785
     * @see DB_common::tableInfo()
786
     * @since Method available since Release 1.7.0
787
     */
788
    function tableInfo($result, $mode = null)
789
    {
790
        if (is_string($result)) {
791
            /*
792
             * Probably received a table name.
793
             * Create a result resource identifier.
794
             */
795
            $id = @sqlite_array_query($this->connection,
796
                                      "PRAGMA table_info('$result');",
797
                                      SQLITE_ASSOC);
798
            $got_string = true;
799
        } else {
800
            $this->last_query = '';
801
            return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null,
802
                                     'This DBMS can not obtain tableInfo' .
803
                                     ' from result sets');
804
        }
805
 
806
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
807
            $case_func = 'strtolower';
808
        } else {
809
            $case_func = 'strval';
810
        }
811
 
812
        $count = count($id);
813
        $res   = array();
814
 
815
        if ($mode) {
816
            $res['num_fields'] = $count;
817
        }
818
 
819
        for ($i = 0; $i < $count; $i++) {
820
            if (strpos($id[$i]['type'], '(') !== false) {
821
                $bits = explode('(', $id[$i]['type']);
822
                $type = $bits[0];
823
                $len  = rtrim($bits[1],')');
824
            } else {
825
                $type = $id[$i]['type'];
826
                $len  = 0;
827
            }
828
 
829
            $flags = '';
830
            if ($id[$i]['pk']) {
831
                $flags .= 'primary_key ';
187 mathias 832
                if (strtoupper($type) == 'INTEGER') {
833
                    $flags .= 'auto_increment ';
834
                }
94 jpm 835
            }
836
            if ($id[$i]['notnull']) {
837
                $flags .= 'not_null ';
838
            }
839
            if ($id[$i]['dflt_value'] !== null) {
840
                $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']);
841
            }
842
            $flags = trim($flags);
843
 
844
            $res[$i] = array(
845
                'table' => $case_func($result),
846
                'name'  => $case_func($id[$i]['name']),
847
                'type'  => $type,
848
                'len'   => $len,
849
                'flags' => $flags,
850
            );
851
 
852
            if ($mode & DB_TABLEINFO_ORDER) {
853
                $res['order'][$res[$i]['name']] = $i;
854
            }
855
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
856
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
857
            }
858
        }
859
 
860
        return $res;
861
    }
862
 
863
    // }}}
864
    // {{{ getSpecialQuery()
865
 
866
    /**
867
     * Obtains the query string needed for listing a given type of objects
868
     *
869
     * @param string $type  the kind of objects you want to retrieve
870
     * @param array  $args  SQLITE DRIVER ONLY: a private array of arguments
871
     *                       used by the getSpecialQuery().  Do not use
872
     *                       this directly.
873
     *
874
     * @return string  the SQL query string or null if the driver doesn't
875
     *                  support the object type requested
876
     *
877
     * @access protected
878
     * @see DB_common::getListOf()
879
     */
880
    function getSpecialQuery($type, $args = array())
881
    {
882
        if (!is_array($args)) {
883
            return $this->raiseError('no key specified', null, null, null,
884
                                     'Argument has to be an array.');
885
        }
886
 
887
        switch ($type) {
888
            case 'master':
889
                return 'SELECT * FROM sqlite_master;';
890
            case 'tables':
891
                return "SELECT name FROM sqlite_master WHERE type='table' "
892
                       . 'UNION ALL SELECT name FROM sqlite_temp_master '
893
                       . "WHERE type='table' ORDER BY name;";
894
            case 'schema':
895
                return 'SELECT sql FROM (SELECT * FROM sqlite_master '
896
                       . 'UNION ALL SELECT * FROM sqlite_temp_master) '
897
                       . "WHERE type!='meta' "
898
                       . 'ORDER BY tbl_name, type DESC, name;';
899
            case 'schemax':
900
            case 'schema_x':
901
                /*
902
                 * Use like:
903
                 * $res = $db->query($db->getSpecialQuery('schema_x',
904
                 *                   array('table' => 'table3')));
905
                 */
906
                return 'SELECT sql FROM (SELECT * FROM sqlite_master '
907
                       . 'UNION ALL SELECT * FROM sqlite_temp_master) '
908
                       . "WHERE tbl_name LIKE '{$args['table']}' "
909
                       . "AND type!='meta' "
910
                       . 'ORDER BY type DESC, name;';
911
            case 'alter':
912
                /*
913
                 * SQLite does not support ALTER TABLE; this is a helper query
914
                 * to handle this. 'table' represents the table name, 'rows'
915
                 * the news rows to create, 'save' the row(s) to keep _with_
916
                 * the data.
917
                 *
918
                 * Use like:
919
                 * $args = array(
920
                 *     'table' => $table,
921
                 *     'rows'  => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT",
922
                 *     'save'  => "NULL, titel, content, datetime"
923
                 * );
924
                 * $res = $db->query( $db->getSpecialQuery('alter', $args));
925
                 */
926
                $rows = strtr($args['rows'], $this->keywords);
927
 
928
                $q = array(
929
                    'BEGIN TRANSACTION',
930
                    "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})",
931
                    "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}",
932
                    "DROP TABLE {$args['table']}",
933
                    "CREATE TABLE {$args['table']} ({$args['rows']})",
934
                    "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup",
935
                    "DROP TABLE {$args['table']}_backup",
936
                    'COMMIT',
937
                );
938
 
939
                /*
940
                 * This is a dirty hack, since the above query will not get
941
                 * executed with a single query call so here the query method
942
                 * will be called directly and return a select instead.
943
                 */
944
                foreach ($q as $query) {
945
                    $this->query($query);
946
                }
947
                return "SELECT * FROM {$args['table']};";
948
            default:
949
                return null;
950
        }
951
    }
952
 
953
    // }}}
954
}
955
 
956
/*
957
 * Local variables:
958
 * tab-width: 4
959
 * c-basic-offset: 4
960
 * End:
961
 */
962
 
963
?>