1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12:
13: require_once dirname(__FILE__) . '/mysql.reflector.php';
14: require_once dirname(__FILE__) . '/sqlite.reflector.php';
15:
16:
17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
31: class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
32: {
33:
34: private $connection;
35:
36:
37: private $resultSet;
38:
39:
40: private $affectedRows = FALSE;
41:
42:
43: private $driverName;
44:
45:
46:
47: 48: 49:
50: public function __construct()
51: {
52: if (!extension_loaded('pdo')) {
53: throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
54: }
55: }
56:
57:
58:
59: 60: 61: 62: 63:
64: public function connect(array &$config)
65: {
66: $foo = & $config['dsn'];
67: $foo = & $config['options'];
68: DibiConnection::alias($config, 'resource', 'pdo');
69:
70: if ($config['resource'] instanceof PDO) {
71: $this->connection = $config['resource'];
72:
73: } else try {
74: $this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
75:
76: } catch (PDOException $e) {
77: throw new DibiDriverException($e->getMessage(), $e->getCode());
78: }
79:
80: if (!$this->connection) {
81: throw new DibiDriverException('Connecting error.');
82: }
83:
84: $this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
85: }
86:
87:
88:
89: 90: 91: 92:
93: public function disconnect()
94: {
95: $this->connection = NULL;
96: }
97:
98:
99:
100: 101: 102: 103: 104: 105:
106: public function query($sql)
107: {
108:
109: $cmd = strtoupper(substr(ltrim($sql), 0, 6));
110: static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
111: $this->affectedRows = FALSE;
112:
113: if (isset($list[$cmd])) {
114: $this->affectedRows = $this->connection->exec($sql);
115:
116: if ($this->affectedRows === FALSE) {
117: $err = $this->connection->errorInfo();
118: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
119: }
120:
121: } else {
122: $res = $this->connection->query($sql);
123:
124: if ($res === FALSE) {
125: $err = $this->connection->errorInfo();
126: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
127: } else {
128: return $this->createResultDriver($res);
129: }
130: }
131: }
132:
133:
134:
135: 136: 137: 138:
139: public function getAffectedRows()
140: {
141: return $this->affectedRows;
142: }
143:
144:
145:
146: 147: 148: 149:
150: public function getInsertId($sequence)
151: {
152: return $this->connection->lastInsertId();
153: }
154:
155:
156:
157: 158: 159: 160: 161: 162:
163: public function begin($savepoint = NULL)
164: {
165: if (!$this->connection->beginTransaction()) {
166: $err = $this->connection->errorInfo();
167: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
168: }
169: }
170:
171:
172:
173: 174: 175: 176: 177: 178:
179: public function commit($savepoint = NULL)
180: {
181: if (!$this->connection->commit()) {
182: $err = $this->connection->errorInfo();
183: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
184: }
185: }
186:
187:
188:
189: 190: 191: 192: 193: 194:
195: public function rollback($savepoint = NULL)
196: {
197: if (!$this->connection->rollBack()) {
198: $err = $this->connection->errorInfo();
199: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
200: }
201: }
202:
203:
204:
205: 206: 207: 208:
209: public function getResource()
210: {
211: return $this->connection;
212: }
213:
214:
215:
216: 217: 218: 219:
220: public function getReflector()
221: {
222: switch ($this->driverName) {
223: case 'mysql':
224: return new DibiMySqlReflector($this);
225:
226: case 'sqlite':
227: case 'sqlite2':
228: return new DibiSqliteReflector($this);
229:
230: default:
231: throw new DibiNotSupportedException;
232: }
233: }
234:
235:
236:
237: 238: 239: 240: 241:
242: public function createResultDriver(PDOStatement $resource)
243: {
244: $res = clone $this;
245: $res->resultSet = $resource;
246: return $res;
247: }
248:
249:
250:
251:
252:
253:
254:
255: 256: 257: 258: 259: 260: 261:
262: public function escape($value, $type)
263: {
264: switch ($type) {
265: case dibi::TEXT:
266: return $this->connection->quote($value, PDO::PARAM_STR);
267:
268: case dibi::BINARY:
269: return $this->connection->quote($value, PDO::PARAM_LOB);
270:
271: case dibi::IDENTIFIER:
272: switch ($this->driverName) {
273: case 'mysql':
274: return '`' . str_replace('`', '``', $value) . '`';
275:
276: case 'pgsql':
277: return '"' . str_replace('"', '""', $value) . '"';
278:
279: case 'sqlite':
280: case 'sqlite2':
281: return '[' . strtr($value, '[]', ' ') . ']';
282:
283: case 'odbc':
284: case 'oci':
285: case 'mssql':
286: return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
287:
288: default:
289: return $value;
290: }
291:
292: case dibi::BOOL:
293: return $this->connection->quote($value, PDO::PARAM_BOOL);
294:
295: case dibi::DATE:
296: return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
297:
298: case dibi::DATETIME:
299: return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
300:
301: default:
302: throw new InvalidArgumentException('Unsupported type.');
303: }
304: }
305:
306:
307:
308: 309: 310: 311: 312: 313:
314: public function escapeLike($value, $pos)
315: {
316: throw new DibiNotImplementedException;
317: }
318:
319:
320:
321: 322: 323: 324: 325: 326: 327:
328: public function unescape($value, $type)
329: {
330: if ($type === dibi::BINARY) {
331: return $value;
332: }
333: throw new InvalidArgumentException('Unsupported type.');
334: }
335:
336:
337:
338: 339: 340: 341: 342: 343: 344:
345: public function applyLimit(&$sql, $limit, $offset)
346: {
347: if ($limit < 0 && $offset < 1) return;
348:
349: switch ($this->driverName) {
350: case 'mysql':
351: $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
352: . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
353: break;
354:
355: case 'pgsql':
356: if ($limit >= 0) $sql .= ' LIMIT ' . (int) $limit;
357: if ($offset > 0) $sql .= ' OFFSET ' . (int) $offset;
358: break;
359:
360: case 'sqlite':
361: case 'sqlite2':
362: $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
363: break;
364:
365: case 'oci':
366: if ($offset > 0) {
367: $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ') WHERE "__rnum" > '. (int) $offset;
368: } elseif ($limit >= 0) {
369: $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
370: }
371: break;
372:
373: case 'odbc':
374: case 'mssql':
375: if ($offset < 1) {
376: $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
377: break;
378: }
379:
380:
381: default:
382: throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
383: }
384: }
385:
386:
387:
388:
389:
390:
391:
392: 393: 394: 395:
396: public function getRowCount()
397: {
398: return $this->resultSet->rowCount();
399: }
400:
401:
402:
403: 404: 405: 406: 407:
408: public function fetch($assoc)
409: {
410: return $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
411: }
412:
413:
414:
415: 416: 417: 418: 419:
420: public function seek($row)
421: {
422: throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
423: }
424:
425:
426:
427: 428: 429: 430:
431: public function free()
432: {
433: $this->resultSet = NULL;
434: }
435:
436:
437:
438: 439: 440: 441: 442:
443: public function getResultColumns()
444: {
445: $count = $this->resultSet->columnCount();
446: $columns = array();
447: for ($i = 0; $i < $count; $i++) {
448: $row = @$this->resultSet->getColumnMeta($i);
449: if ($row === FALSE) {
450: throw new DibiNotSupportedException('Driver does not support meta data.');
451: }
452:
453:
454: $row['table'] = isset($row['table']) ? $row['table'] : NULL;
455:
456: $columns[] = array(
457: 'name' => $row['name'],
458: 'table' => $row['table'],
459: 'nativetype' => $row['native_type'],
460: 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
461: 'vendor' => $row,
462: );
463: }
464: return $columns;
465: }
466:
467:
468:
469: 470: 471: 472:
473: public function getResultResource()
474: {
475: return $this->resultSet;
476: }
477:
478: }
479: