1 <?php
2 3 4 5 6 7 8 9 10 11 12 13 14
15
16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
35 class NeevoDriverSQLite2 extends NeevoParser implements INeevoDriver {
36
37
38
39 private $charset;
40
41
42 private $dbCharset;
43
44
45 private $updateLimit;
46
47
48 private $resource;
49
50
51 private $unbuffered;
52
53
54 private $persistent;
55
56
57 private $affectedRows;
58
59
60 private $tblData = array();
61
62
63 64 65 66 67
68 public function __construct(NeevoBaseStmt $statement = null){
69 if(!extension_loaded("sqlite"))
70 throw new NeevoDriverException("Cannot instantiate Neevo SQLite driver - PHP extension 'sqlite' not loaded.");
71 if($statement instanceof NeevoBaseStmt)
72 parent::__construct($statement);
73 }
74
75
76 77 78 79 80 81
82 public function connect(array $config){
83 NeevoConnection::alias($config, 'database', 'file');
84 NeevoConnection::alias($config, 'updateLimit', 'update_limit');
85
86 $defaults = array(
87 'memory' => false,
88 'resource' => null,
89 'updateLimit' => false,
90 'charset' => 'UTF-8',
91 'dbcharset' => 'UTF-8',
92 'persistent' => false,
93 'unbuffered' => false
94 );
95
96 $config += $defaults;
97
98 if($config['memory'])
99 $config['database'] = ':memory:';
100
101
102 if(is_resource($config['resource']))
103 $connection = $config['resource'];
104 elseif($config['persistent'])
105 $connection = @sqlite_popen($config['database'], 0666, $error);
106 else
107 $connection = @sqlite_open($config['database'], 0666, $error);
108
109 if(!is_resource($connection))
110 throw new NeevoException("Opening database file '$config[database]' failed.");
111
112 $this->resource = $connection;
113 $this->updateLimit = (bool) $config['updateLimit'];
114
115
116 $this->dbCharset = $config['dbcharset'];
117 $this->charset = $config['charset'];
118 if(strcasecmp($this->dbCharset, $this->charset) === 0)
119 $this->dbCharset = $this->charset = null;
120
121 $this->unbuffered = $config['unbuffered'];
122 $this->persistent = $config['persistent'];
123 }
124
125
126 127 128 129
130 public function closeConnection(){
131 if(!$this->persistent && $this->resource !== null)
132 @sqlite_close($this->resource);
133 }
134
135
136 137 138 139 140
141 public function freeResultSet($resultSet){
142 return true;
143 }
144
145
146 147 148 149 150 151
152 public function runQuery($queryString){
153
154 $this->affectedRows = false;
155 if($this->dbCharset !== null)
156 $queryString = iconv($this->charset, $this->dbCharset . '//IGNORE', $queryString);
157
158 if($this->unbuffered)
159 $result = @sqlite_unbuffered_query($this->resource, $queryString, null, $error);
160 else
161 $result = @sqlite_query($this->resource, $queryString, null, $error);
162
163 if($error && $result === false)
164 throw new NeevoException("Query failed. $error", sqlite_last_error($this->resource), $queryString);
165
166 $this->affectedRows = @sqlite_changes($this->resource);
167 return $result;
168 }
169
170
171 172 173 174 175
176 public function beginTransaction($savepoint = null){
177 $this->runQuery('BEGIN');
178 }
179
180
181 182 183 184 185
186 public function commit($savepoint = null){
187 $this->runQuery('COMMIT');
188 }
189
190
191 192 193 194 195
196 public function rollback($savepoint = null){
197 $this->runQuery('ROLLBACK');
198 }
199
200
201 202 203 204 205
206 public function fetch($resultSet){
207 $row = @sqlite_fetch_array($resultSet, SQLITE_ASSOC);
208 if($row){
209 $charset = $this->charset === null ? null : $this->charset.'//TRANSLIT';
210
211 $fields = array();
212 foreach($row as $key => $val){
213 if($charset !== null && is_string($val))
214 $val = iconv($this->dbcharset, $charset, $val);
215 $key = str_replace(array('[', ']'), '', $key);
216 $pos = strpos($key, '.');
217 if($pos !== false)
218 $key = substr($key, $pos + 1);
219 $fields[$key] = $val;
220 }
221 $row = $fields;
222 }
223 return $row;
224 }
225
226
227 228 229 230 231 232 233
234 public function seek($resultSet, $offset){
235 if($this->unbuffered)
236 throw new NeevoDriverException('Cannot seek on unbuffered result.');
237 return @sqlite_seek($resultSet, $offset);
238 }
239
240
241 242 243 244
245 public function getInsertId(){
246 return @sqlite_last_insert_rowid($this->resource);
247 }
248
249
250 251 252 253 254
255 public function randomizeOrder(NeevoBaseStmt $statement){
256 $statement->order('RANDOM()');
257 }
258
259
260 261 262 263 264 265
266 public function getNumRows($resultSet){
267 if($this->unbuffered)
268 throw new NeevoDriverException('Cannot count rows on unbuffered result.');
269 return @sqlite_num_rows($resultSet);
270 }
271
272
273 274 275 276
277 public function getAffectedRows(){
278 return $this->affectedRows;
279 }
280
281
282 283 284 285 286 287 288
289 public function escape($value, $type){
290 switch($type){
291 case Neevo::BOOL:
292 return $value ? 1 :0;
293
294 case Neevo::TEXT:
295 case Neevo::BINARY:
296 return "'". sqlite_escape_string($value) ."'";
297
298 case Neevo::IDENTIFIER:
299 return str_replace('[*]', '*', '[' . str_replace('.', '].[', $value) . ']');
300
301 case Neevo::DATETIME:
302 return ($value instanceof DateTime) ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
303
304 default:
305 throw new InvalidArgumentException('Unsupported data type.');
306 break;
307 }
308 }
309
310
311 312 313 314 315 316 317
318 public function unescape($value, $type){
319 if($type === Neevo::BINARY)
320 return $value;
321 throw new InvalidArgumentException('Unsupported data type.');
322 }
323
324
325 326 327 328 329
330 public function getPrimaryKey($table){
331 $key = '';
332 $pos = strpos($table, '.');
333 if($pos !== false)
334 $table = substr($table, $pos + 1);
335 if(isset($this->tblData[$table]))
336 $sql = $this->tblData[$table];
337 else{
338 $q = $this->runQuery("SELECT sql FROM sqlite_master WHERE tbl_name='$table'");
339 $r = $this->fetch($q);
340 if($r === false)
341 return '';
342 $this->tblData[$table] = $sql = $r['sql'];
343 }
344
345 $sql = explode("\n", $sql);
346 foreach($sql as $field){
347 $field = trim($field);
348 if(stripos($field, 'PRIMARY KEY') !== false && $key === '')
349 $key = preg_replace('~^"(\w+)".*$~i', '$1', $field);
350 }
351 return $key;
352 }
353
354
355 356 357 358 359 360
361 public function getColumnTypes($resultSet, $table){
362 if($table === null)
363 return array();
364 if(isset($this->tblData[$table]))
365 $sql = $this->tblData[$table];
366 else
367 $q = $this->runQuery("SELECT sql FROM sqlite_master WHERE tbl_name='$table'");
368 $r = $this->fetch($q);
369 if($r === false)
370 return array();
371 $this->tblData[$table] = $sql = $r['sql'];
372 $sql = explode("\n", $sql);
373
374 $cols = array();
375 foreach($sql as $field){
376 $field = trim($field);
377 preg_match('~^"(\w+)"\s+(integer|real|numeric|text|blob).+$~i', $field, $m);
378 if(isset($m[1], $m[2]))
379 $cols[$m[1]] = $m[2];
380 }
381 return $cols;
382 }
383
384
385
386
387
388 389 390 391
392 protected function parseUpdateStmt(){
393 $sql = parent::parseUpdateStmt();
394 return $this->updateLimit ? $this->applyLimit($sql . $this->clauses[3]) : $sql;
395 }
396
397
398 399 400 401
402 protected function parseDeleteStmt(){
403 $sql = parent::parseDeleteStmt();
404 return $this->updateLimit ? $this->applyLimit($sql . $this->clauses[3]) : $sql;
405 }
406
407
408 }
409