1<?php
2/**
3 * Implements specific functions of the PHPs Sqlite Extension
4 *
5 * This adapter give sqlite2 support, and is based on code of previous
6 * versions of the sqlite plugin
7 */
8class helper_plugin_sqlite_adapter_sqlite2 extends helper_plugin_sqlite_adapter {
9    protected $fileextension = '.sqlite';
10    protected $db;
11
12    /**
13     * return name of adapter
14     *
15     * @return string adapter name
16     */
17    public function getName() {
18        return DOKU_EXT_SQLITE;
19    }
20
21    /**
22     * open db
23     *
24     * @param bool $init          true if this is a new database to initialize
25     * @param bool $sqliteupgrade when connecting to a new database:
26     *                              false stops connecting to an .sqlite3 db when an .sqlite2 db already exist and warns instead,
27     *                              true let connecting so upgrading is possible
28     * @return bool true if connecting to sqlite3 db succeed
29     */
30    public function opendb($init, $sqliteupgrade = false) {
31        if($this->isSqlite3db($this->dbfile)) {
32            msg("SQLite: failed to open SQLite '".$this->dbname."' database (DB has a sqlite3 format instead of sqlite2 format.)", -1);
33            return false;
34        }
35
36        $error    = '';
37        $this->db = sqlite_open($this->dbfile, 0666, $error);
38        if(!$this->db) {
39            msg("SQLite: failed to open SQLite '".$this->dbname."' database ($error)", -1);
40            return false;
41        }
42
43        // register our custom aggregate function
44        sqlite_create_aggregate(
45            $this->db, 'group_concat',
46            array($this, '_sqlite_group_concat_step'),
47            array($this, '_sqlite_group_concat_finalize'), 2
48        );
49        return true;
50    }
51
52    /**
53     * close current db
54     */
55    public function closedb() {
56        sqlite_close($this->db);
57    }
58
59    /**
60     * Registers a User Defined Function for use in SQL statements
61     */
62    public function create_function($function_name, $callback, $num_args) {
63        sqlite_create_function($this->db, $function_name, $callback, $num_args);
64    }
65
66    /**
67     * Execute a query.
68     *
69     * @param string $sql query
70     * @return bool|\SQLiteResult
71     */
72    public function executeQuery($sql) {
73        $err = '';
74        $res = @sqlite_query($this->db, $sql, SQLITE_ASSOC, $err);
75        if($err) {
76            msg($err.':<br /><pre>'.hsc($sql).'</pre>', -1);
77            return false;
78        } elseif(!$res) {
79            msg(
80                sqlite_error_string(sqlite_last_error($this->db)).
81                    ':<br /><pre>'.hsc($sql).'</pre>', -1
82            );
83            return false;
84        }
85
86        return $res;
87    }
88
89    /**
90     * Close the result set and it's cursors
91     *
92     * @param bool|\SQLiteResult $res
93     * @return bool
94     */
95    public function res_close($res) {
96        return true; //seems not to be needed in sqlite2?
97    }
98
99    /**
100     * Returns a complete result set as array
101     *
102     * @param bool|\SQLiteResult $res
103     * @param bool $assoc
104     * @return array of arrays of the rows
105     */
106    public function res2arr($res, $assoc = true) {
107        $data = array();
108
109        if(!$res) return $data;
110        if(!sqlite_num_rows($res)) return $data;
111
112        sqlite_rewind($res);
113        $mode = $assoc ? SQLITE_ASSOC : SQLITE_NUM;
114        while(($row = sqlite_fetch_array($res, $mode)) !== false) {
115            $data[] = $row;
116        }
117        return $data;
118    }
119
120    /**
121     * Return the next row of the given result set as associative array
122     *
123     * @param bool|\SQLiteResult $res
124     * @return array|bool next row or false
125     */
126    public function res2row($res) {
127        if(!$res) return false;
128        return sqlite_fetch_array($res, SQLITE_ASSOC);
129    }
130
131    /**
132     * Return the first value from the next row.
133     *
134     * @param bool|\SQLiteResult $res
135     * @return bool|string
136     */
137    public function res2single($res) {
138        if(!$res) return false;
139
140        return sqlite_fetch_single($res);
141    }
142
143    /**
144     * Run sqlite_escape_string() on the given string and surround it
145     * with quotes
146     *
147     * @param string $string
148     * @return string escaped and quoted string
149     */
150    public function quote_string($string) {
151        return "'".sqlite_escape_string($string)."'";
152    }
153
154    /**
155     * Escape string for sql
156     *
157     * @param string $str
158     * @return string escaped
159     */
160    public function escape_string($str) {
161        return sqlite_escape_string($str);
162    }
163
164    /**
165     * Aggregation function for SQLite
166     * Callback function called for each row of the result set.
167     *
168     * @link http://devzone.zend.com/article/863-SQLite-Lean-Mean-DB-Machine
169     *
170     * @param array &$context   (reference) argument where processed data can be stored
171     * @param string $string    column value
172     * @param string $separator separator between the values
173     */
174    public function _sqlite_group_concat_step(&$context, $string, $separator = ',') {
175        $context['sep']    = $separator;
176        $context['data'][] = $string;
177    }
178
179    /**
180     * Aggregation function for SQLite
181     * Callback function to aggregate the "stepped" data from each row and return it.
182     *
183     * @link http://devzone.zend.com/article/863-SQLite-Lean-Mean-DB-Machine
184     *
185     * @param array &$context (reference) data as collected in step callback
186     * @return string final result of aggregation
187     */
188    public function _sqlite_group_concat_finalize(&$context) {
189        $context['data'] = array_unique($context['data']);
190        return join($context['sep'], $context['data']);
191    }
192
193    /**
194     * fetch the next row as zero indexed array
195     *
196     * @param bool|\SQLiteResult $res
197     * @return array|bool
198     */
199    public function res_fetch_array($res) {
200        if(!$res) return false;
201
202        return sqlite_fetch_array($res, SQLITE_NUM);
203    }
204
205    /**
206     * fetch the next row as assocative array
207     *
208     * @param bool|\SQLiteResult $res
209     * @return array|bool
210     */
211    public function res_fetch_assoc($res) {
212        if(!$res) return false;
213
214        return sqlite_fetch_array($res, SQLITE_ASSOC);
215    }
216
217    /**
218     * Count the number of records in result
219     *
220     * This function is really inperformant in PDO and should be avoided!
221     *
222     * @param bool|\SQLiteResult $res
223     * @return int
224     */
225    public function res2count($res) {
226        if(!$res) return 0;
227
228        return sqlite_num_rows($res);
229    }
230
231    /**
232     * Count the number of records changed last time
233     *
234     * Don't work after a SELECT statement in PDO
235     *
236     * @param bool|\SQLiteResult $res
237     * @return int
238     */
239    public function countChanges($res) {
240        if(!$res) return 0;
241        return sqlite_changes($this->db);
242    }
243}
244
245// vim:ts=4:sw=4:et:enc=utf-8:
246