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: class DboSqlite extends DboSource {
30:
31: 32: 33: 34: 35:
36: var $description = "SQLite DBO Driver";
37:
38: 39: 40: 41: 42:
43: var $startQuote = '"';
44:
45: 46: 47: 48: 49:
50: var $endQuote = '"';
51:
52: 53: 54: 55: 56: 57:
58: var $_queryStats = array();
59:
60: 61: 62: 63: 64:
65: var $_baseConfig = array(
66: 'persistent' => true,
67: 'database' => null
68: );
69:
70: 71: 72: 73: 74: 75:
76: var $_commands = array(
77: 'begin' => 'BEGIN TRANSACTION',
78: 'commit' => 'COMMIT TRANSACTION',
79: 'rollback' => 'ROLLBACK TRANSACTION'
80: );
81:
82: 83: 84: 85: 86:
87: var $columns = array(
88: 'primary_key' => array('name' => 'integer primary key'),
89: 'string' => array('name' => 'varchar', 'limit' => '255'),
90: 'text' => array('name' => 'text'),
91: 'integer' => array('name' => 'integer', 'limit' => 11, 'formatter' => 'intval'),
92: 'float' => array('name' => 'float', 'formatter' => 'floatval'),
93: 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
94: 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
95: 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
96: 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
97: 'binary' => array('name' => 'blob'),
98: 'boolean' => array('name' => 'boolean')
99: );
100:
101: 102: 103: 104: 105: 106:
107: var $fieldParameters = array(
108: 'collate' => array(
109: 'value' => 'COLLATE',
110: 'quote' => false,
111: 'join' => ' ',
112: 'column' => 'Collate',
113: 'position' => 'afterDefault',
114: 'options' => array(
115: 'BINARY', 'NOCASE', 'RTRIM'
116: )
117: ),
118: );
119:
120: 121: 122: 123: 124: 125:
126: function connect() {
127: $config = $this->config;
128:
129: if (!$config['persistent']) {
130: $this->connection = sqlite_open($config['database']);
131: } else {
132: $this->connection = sqlite_popen($config['database']);
133: }
134: $this->connected = is_resource($this->connection);
135:
136: if ($this->connected) {
137: $this->_execute('PRAGMA count_changes = 1;');
138: }
139: return $this->connected;
140: }
141:
142: 143: 144: 145: 146:
147: function enabled() {
148: return extension_loaded('sqlite');
149: }
150: 151: 152: 153: 154:
155: function disconnect() {
156: @sqlite_close($this->connection);
157: $this->connected = false;
158: return $this->connected;
159: }
160:
161: 162: 163: 164: 165: 166:
167: function _execute($sql) {
168: $result = sqlite_query($this->connection, $sql);
169:
170: if (preg_match('/^(INSERT|UPDATE|DELETE)/', $sql)) {
171: $this->resultSet($result);
172: list($this->_queryStats) = $this->fetchResult();
173: }
174: return $result;
175: }
176:
177: 178: 179: 180: 181: 182:
183: function execute($sql) {
184: $result = parent::execute($sql);
185: $this->_queryStats = array();
186: return $result;
187: }
188:
189: 190: 191: 192: 193:
194: function listSources() {
195: $cache = parent::listSources();
196:
197: if ($cache != null) {
198: return $cache;
199: }
200: $result = $this->fetchAll("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", false);
201:
202: if (empty($result)) {
203: return array();
204: } else {
205: $tables = array();
206: foreach ($result as $table) {
207: $tables[] = $table[0]['name'];
208: }
209: parent::listSources($tables);
210: return $tables;
211: }
212: return array();
213: }
214:
215: 216: 217: 218: 219: 220:
221: function describe(&$model) {
222: $cache = parent::describe($model);
223: if ($cache != null) {
224: return $cache;
225: }
226: $fields = array();
227: $result = $this->fetchAll('PRAGMA table_info(' . $this->fullTableName($model) . ')');
228:
229: foreach ($result as $column) {
230: $fields[$column[0]['name']] = array(
231: 'type' => $this->column($column[0]['type']),
232: 'null' => !$column[0]['notnull'],
233: 'default' => $column[0]['dflt_value'],
234: 'length' => $this->length($column[0]['type'])
235: );
236: if ($column[0]['pk'] == 1) {
237: $colLength = $this->length($column[0]['type']);
238: $fields[$column[0]['name']] = array(
239: 'type' => $fields[$column[0]['name']]['type'],
240: 'null' => false,
241: 'default' => $column[0]['dflt_value'],
242: 'key' => $this->index['PRI'],
243: 'length'=> ($colLength != null) ? $colLength : 11
244: );
245: }
246: }
247:
248: $this->__cacheDescription($model->tablePrefix . $model->table, $fields);
249: return $fields;
250: }
251:
252: 253: 254: 255: 256: 257:
258: function value($data, $column = null, $safe = false) {
259: $parent = parent::value($data, $column, $safe);
260:
261: if ($parent != null) {
262: return $parent;
263: }
264: if ($data === null) {
265: return 'NULL';
266: }
267: if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
268: return "''";
269: }
270: switch ($column) {
271: case 'boolean':
272: $data = $this->boolean((bool)$data);
273: break;
274: case 'integer':
275: case 'float':
276: if ($data === '') {
277: return 'NULL';
278: }
279: default:
280: $data = sqlite_escape_string($data);
281: break;
282: }
283: return "'" . $data . "'";
284: }
285:
286: 287: 288: 289: 290: 291: 292: 293: 294:
295: function update(&$model, $fields = array(), $values = null, $conditions = null) {
296: if (empty($values) && !empty($fields)) {
297: foreach ($fields as $field => $value) {
298: if (strpos($field, $model->alias . '.') !== false) {
299: unset($fields[$field]);
300: $field = str_replace($model->alias . '.', "", $field);
301: $field = str_replace($model->alias . '.', "", $field);
302: $fields[$field] = $value;
303: }
304: }
305: }
306: $result = parent::update($model, $fields, $values, $conditions);
307: return $result;
308: }
309:
310: 311: 312: 313: 314: 315: 316: 317:
318: function truncate($table) {
319: return $this->execute('DELETE From ' . $this->fullTableName($table));
320: }
321:
322: 323: 324: 325: 326:
327: function lastError() {
328: $error = sqlite_last_error($this->connection);
329: if ($error) {
330: return $error.': '.sqlite_error_string($error);
331: }
332: return null;
333: }
334:
335: 336: 337: 338: 339:
340: function lastAffected() {
341: if (!empty($this->_queryStats)) {
342: foreach (array('rows inserted', 'rows updated', 'rows deleted') as $key) {
343: if (array_key_exists($key, $this->_queryStats)) {
344: return $this->_queryStats[$key];
345: }
346: }
347: }
348: return false;
349: }
350:
351: 352: 353: 354: 355: 356:
357: function lastNumRows() {
358: if ($this->hasResult()) {
359: sqlite_num_rows($this->_result);
360: }
361: return false;
362: }
363:
364: 365: 366: 367: 368:
369: function lastInsertId() {
370: return sqlite_last_insert_rowid($this->connection);
371: }
372:
373: 374: 375: 376: 377: 378:
379: function column($real) {
380: if (is_array($real)) {
381: $col = $real['name'];
382: if (isset($real['limit'])) {
383: $col .= '('.$real['limit'].')';
384: }
385: return $col;
386: }
387:
388: $col = strtolower(str_replace(')', '', $real));
389: $limit = null;
390: if (strpos($col, '(') !== false) {
391: list($col, $limit) = explode('(', $col);
392: }
393:
394: if (in_array($col, array('text', 'integer', 'float', 'boolean', 'timestamp', 'date', 'datetime', 'time'))) {
395: return $col;
396: }
397: if (strpos($col, 'varchar') !== false) {
398: return 'string';
399: }
400: if (in_array($col, array('blob', 'clob'))) {
401: return 'binary';
402: }
403: if (strpos($col, 'numeric') !== false) {
404: return 'float';
405: }
406: return 'text';
407: }
408:
409: 410: 411: 412: 413:
414: function resultSet(&$results) {
415: $this->results =& $results;
416: $this->map = array();
417: $fieldCount = sqlite_num_fields($results);
418: $index = $j = 0;
419:
420: while ($j < $fieldCount) {
421: $columnName = str_replace('"', '', sqlite_field_name($results, $j));
422:
423: if (strpos($columnName, '.')) {
424: $parts = explode('.', $columnName);
425: $this->map[$index++] = array($parts[0], $parts[1]);
426: } else {
427: $this->map[$index++] = array(0, $columnName);
428: }
429: $j++;
430: }
431: }
432:
433: 434: 435: 436: 437:
438: function fetchResult() {
439: if ($row = sqlite_fetch_array($this->results, SQLITE_ASSOC)) {
440: $resultRow = array();
441: $i = 0;
442:
443: foreach ($row as $index => $field) {
444: if (strpos($index, '.')) {
445: list($table, $column) = explode('.', str_replace('"', '', $index));
446: $resultRow[$table][$column] = $row[$index];
447: } else {
448: $resultRow[0][str_replace('"', '', $index)] = $row[$index];
449: }
450: $i++;
451: }
452: return $resultRow;
453: } else {
454: return false;
455: }
456: }
457:
458: 459: 460: 461: 462: 463: 464:
465: function limit($limit, $offset = null) {
466: if ($limit) {
467: $rt = '';
468: if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
469: $rt = ' LIMIT';
470: }
471: $rt .= ' ' . $limit;
472: if ($offset) {
473: $rt .= ' OFFSET ' . $offset;
474: }
475: return $rt;
476: }
477: return null;
478: }
479:
480: 481: 482: 483: 484: 485: 486:
487: function buildColumn($column) {
488: $name = $type = null;
489: $column = array_merge(array('null' => true), $column);
490: extract($column);
491:
492: if (empty($name) || empty($type)) {
493: trigger_error(__('Column name or type not defined in schema', true), E_USER_WARNING);
494: return null;
495: }
496:
497: if (!isset($this->columns[$type])) {
498: trigger_error(sprintf(__('Column type %s does not exist', true), $type), E_USER_WARNING);
499: return null;
500: }
501:
502: $real = $this->columns[$type];
503: $out = $this->name($name) . ' ' . $real['name'];
504: if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
505: return $this->name($name) . ' ' . $this->columns['primary_key']['name'];
506: }
507: return parent::buildColumn($column);
508: }
509:
510: 511: 512: 513: 514:
515: function setEncoding($enc) {
516: if (!in_array($enc, array("UTF-8", "UTF-16", "UTF-16le", "UTF-16be"))) {
517: return false;
518: }
519: return $this->_execute("PRAGMA encoding = \"{$enc}\"") !== false;
520: }
521:
522: 523: 524: 525: 526:
527: function getEncoding() {
528: return $this->fetchRow('PRAGMA encoding');
529: }
530:
531: 532: 533: 534: 535: 536: 537:
538: function buildIndex($indexes, $table = null) {
539: $join = array();
540:
541: foreach ($indexes as $name => $value) {
542:
543: if ($name == 'PRIMARY') {
544: continue;
545: }
546: $out = 'CREATE ';
547:
548: if (!empty($value['unique'])) {
549: $out .= 'UNIQUE ';
550: }
551: if (is_array($value['column'])) {
552: $value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
553: } else {
554: $value['column'] = $this->name($value['column']);
555: }
556: $out .= "INDEX {$name} ON {$table}({$value['column']});";
557: $join[] = $out;
558: }
559: return $join;
560: }
561:
562: 563: 564: 565: 566: 567: 568:
569: function index(&$model) {
570: $index = array();
571: $table = $this->fullTableName($model);
572: if ($table) {
573: $indexes = $this->query('PRAGMA index_list(' . $table . ')');
574: $tableInfo = $this->query('PRAGMA table_info(' . $table . ')');
575: foreach ($indexes as $i => $info) {
576: $key = array_pop($info);
577: $keyInfo = $this->query('PRAGMA index_info("' . $key['name'] . '")');
578: foreach ($keyInfo as $keyCol) {
579: if (!isset($index[$key['name']])) {
580: $col = array();
581: if (preg_match('/autoindex/', $key['name'])) {
582: $key['name'] = 'PRIMARY';
583: }
584: $index[$key['name']]['column'] = $keyCol[0]['name'];
585: $index[$key['name']]['unique'] = intval($key['unique'] == 1);
586: } else {
587: if (!is_array($index[$key['name']]['column'])) {
588: $col[] = $index[$key['name']]['column'];
589: }
590: $col[] = $keyCol[0]['name'];
591: $index[$key['name']]['column'] = $col;
592: }
593: }
594: }
595: }
596: return $index;
597: }
598:
599: 600: 601: 602: 603: 604: 605:
606: function renderStatement($type, $data) {
607: switch (strtolower($type)) {
608: case 'schema':
609: extract($data);
610:
611: foreach (array('columns', 'indexes') as $var) {
612: if (is_array(${$var})) {
613: ${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
614: }
615: }
616: return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
617: break;
618: default:
619: return parent::renderStatement($type, $data);
620: break;
621: }
622: }
623: }
624: