1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Database\Drivers;
9:
10: use Nette;
11:
12:
13: 14: 15: 16: 17:
18: class SqliteDriver extends Nette\Object implements Nette\Database\ISupplementalDriver
19: {
20:
21: private $connection;
22:
23:
24: private $fmtDateTime;
25:
26:
27: public function __construct(Nette\Database\Connection $connection, array $options)
28: {
29: $this->connection = $connection;
30: $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U';
31: }
32:
33:
34:
35:
36:
37: 38: 39:
40: public function delimite($name)
41: {
42: return '[' . strtr($name, '[]', ' ') . ']';
43: }
44:
45:
46: 47: 48:
49: public function formatBool($value)
50: {
51: return $value ? '1' : '0';
52: }
53:
54:
55: 56: 57:
58: public function formatDateTime( $value)
59: {
60: return $value->format($this->fmtDateTime);
61: }
62:
63:
64: 65: 66:
67: public function formatLike($value, $pos)
68: {
69: $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\');
70: return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
71: }
72:
73:
74: 75: 76:
77: public function applyLimit(& $sql, $limit, $offset)
78: {
79: if ($limit >= 0 || $offset > 0) {
80: $sql .= ' LIMIT ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
81: }
82: }
83:
84:
85: 86: 87:
88: public function normalizeRow($row)
89: {
90: foreach ($row as $key => $value) {
91: unset($row[$key]);
92: if ($key[0] === '[' || $key[0] === '"') {
93: $key = substr($key, 1, -1);
94: }
95: $row[$key] = $value;
96: }
97: return $row;
98: }
99:
100:
101:
102:
103:
104: 105: 106:
107: public function getTables()
108: {
109: $tables = array();
110: foreach ($this->connection->query("
111: SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
112: UNION ALL
113: SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
114: ORDER BY name
115: ") as $row) {
116: $tables[] = array(
117: 'name' => $row->name,
118: 'view' => (bool) $row->view,
119: );
120: }
121:
122: return $tables;
123: }
124:
125:
126: 127: 128:
129: public function getColumns($table)
130: {
131: $meta = $this->connection->query("
132: SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->connection->quote($table)}
133: UNION ALL
134: SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->connection->quote($table)}
135: ")->fetch();
136:
137: $columns = array();
138: foreach ($this->connection->query("PRAGMA table_info({$this->delimite($table)})") as $row) {
139: $column = $row['name'];
140: $pattern = "/(\"$column\"|\[$column\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui";
141: $type = explode('(', $row['type']);
142: $columns[] = array(
143: 'name' => $column,
144: 'table' => $table,
145: 'nativetype' => strtoupper($type[0]),
146: 'size' => isset($type[1]) ? (int) $type[1] : NULL,
147: 'unsigned' => FALSE,
148: 'nullable' => $row['notnull'] == '0',
149: 'default' => $row['dflt_value'],
150: 'autoincrement' => (bool) preg_match($pattern, $meta['sql']),
151: 'primary' => $row['pk'] > 0,
152: 'vendor' => (array) $row,
153: );
154: }
155: return $columns;
156: }
157:
158:
159: 160: 161:
162: public function getIndexes($table)
163: {
164: $indexes = array();
165: foreach ($this->connection->query("PRAGMA index_list({$this->delimite($table)})") as $row) {
166: $indexes[$row['name']]['name'] = $row['name'];
167: $indexes[$row['name']]['unique'] = (bool) $row['unique'];
168: $indexes[$row['name']]['primary'] = FALSE;
169: }
170:
171: foreach ($indexes as $index => $values) {
172: $res = $this->connection->query("PRAGMA index_info({$this->delimite($index)})");
173: while ($row = $res->fetch(TRUE)) {
174: $indexes[$index]['columns'][$row['seqno']] = $row['name'];
175: }
176: }
177:
178: $columns = $this->getColumns($table);
179: foreach ($indexes as $index => $values) {
180: $column = $indexes[$index]['columns'][0];
181: foreach ($columns as $info) {
182: if ($column == $info['name']) {
183: $indexes[$index]['primary'] = (bool) $info['primary'];
184: break;
185: }
186: }
187: }
188: if (!$indexes) {
189: foreach ($columns as $column) {
190: if ($column['vendor']['pk']) {
191: $indexes[] = array(
192: 'name' => 'ROWID',
193: 'unique' => TRUE,
194: 'primary' => TRUE,
195: 'columns' => array($column['name']),
196: );
197: break;
198: }
199: }
200: }
201:
202: return array_values($indexes);
203: }
204:
205:
206: 207: 208:
209: public function getForeignKeys($table)
210: {
211: $keys = array();
212: foreach ($this->connection->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) {
213: $keys[$row['id']]['name'] = $row['id'];
214: $keys[$row['id']]['local'] = $row['from'];
215: $keys[$row['id']]['table'] = $row['table'];
216: $keys[$row['id']]['foreign'] = $row['to'];
217: $keys[$row['id']]['onDelete'] = $row['on_delete'];
218: $keys[$row['id']]['onUpdate'] = $row['on_update'];
219:
220: if ($keys[$row['id']]['foreign'][0] == NULL) {
221: $keys[$row['id']]['foreign'] = NULL;
222: }
223: }
224: return array_values($keys);
225: }
226:
227:
228: 229: 230:
231: public function getColumnTypes(\PDOStatement $statement)
232: {
233: $types = array();
234: $count = $statement->columnCount();
235: for ($col = 0; $col < $count; $col++) {
236: $meta = $statement->getColumnMeta($col);
237: if (isset($meta['sqlite:decl_type'])) {
238: if ($meta['sqlite:decl_type'] === 'DATE') {
239: $types[$meta['name']] = Nette\Database\IReflection::FIELD_UNIX_TIMESTAMP;
240: } else {
241: $types[$meta['name']] = Nette\Database\Helpers::detectType($meta['sqlite:decl_type']);
242: }
243: } elseif (isset($meta['native_type'])) {
244: $types[$meta['name']] = Nette\Database\Helpers::detectType($meta['native_type']);
245: }
246: }
247: return $types;
248: }
249:
250:
251: 252: 253: 254:
255: public function isSupported($item)
256: {
257: return $item === self::SUPPORT_MULTI_INSERT_AS_SELECT || $item === self::SUPPORT_SUBSELECT || $item === self::SUPPORT_MULTI_COLUMN_AS_OR_COND;
258: }
259:
260: }
261: