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: class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
29: {
30:
31: private $connection;
32:
33:
34: private $resultSet;
35:
36:
37: private $affectedRows = FALSE;
38:
39:
40: private $driverName;
41:
42:
43:
44: 45: 46:
47: public function __construct()
48: {
49: if (!extension_loaded('pdo')) {
50: throw new DibiDriverException("PHP extension 'pdo' is not loaded.");
51: }
52: }
53:
54:
55:
56: 57: 58: 59: 60:
61: public function connect(array &$config)
62: {
63: $foo = & $config['dsn'];
64: $foo = & $config['options'];
65: DibiConnection::alias($config, 'resource', 'pdo');
66:
67: if ($config['resource'] instanceof PDO) {
68: $this->connection = $config['resource'];
69:
70: } else try {
71: $this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
72:
73: } catch (PDOException $e) {
74: throw new DibiDriverException($e->getMessage(), $e->getCode());
75: }
76:
77: if (!$this->connection) {
78: throw new DibiDriverException('Connecting error.');
79: }
80:
81: $this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
82: }
83:
84:
85:
86: 87: 88: 89:
90: public function disconnect()
91: {
92: $this->connection = NULL;
93: }
94:
95:
96:
97: 98: 99: 100: 101: 102:
103: public function query($sql)
104: {
105: 106: $cmd = strtoupper(substr(ltrim($sql), 0, 6));
107: static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
108:
109: if (isset($list[$cmd])) {
110: $this->resultSet = NULL;
111: $this->affectedRows = $this->connection->exec($sql);
112:
113: if ($this->affectedRows === FALSE) {
114: $err = $this->connection->errorInfo();
115: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
116: }
117:
118: return NULL;
119:
120: } else {
121: $this->resultSet = $this->connection->query($sql);
122: $this->affectedRows = FALSE;
123:
124: if ($this->resultSet === FALSE) {
125: $err = $this->connection->errorInfo();
126: throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
127: }
128:
129: return clone $this;
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: throw new NotSupportedException;
223: }
224:
225:
226:
227:
228:
229:
230:
231: 232: 233: 234: 235: 236: 237:
238: public function escape($value, $type)
239: {
240: switch ($type) {
241: case dibi::TEXT:
242: return $this->connection->quote($value, PDO::PARAM_STR);
243:
244: case dibi::BINARY:
245: return $this->connection->quote($value, PDO::PARAM_LOB);
246:
247: case dibi::IDENTIFIER:
248: switch ($this->driverName) {
249: case 'mysql':
250: return '`' . str_replace('`', '``', $value) . '`';
251:
252: case 'pgsql':
253: return '"' . str_replace('"', '""', $value) . '"';
254:
255: case 'sqlite':
256: case 'sqlite2':
257: return '[' . strtr($value, '[]', ' ') . ']';
258:
259: case 'odbc':
260: case 'oci': 261: case 'mssql':
262: return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
263:
264: default:
265: return $value;
266: }
267:
268: case dibi::BOOL:
269: return $this->connection->quote($value, PDO::PARAM_BOOL);
270:
271: case dibi::DATE:
272: return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
273:
274: case dibi::DATETIME:
275: return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
276:
277: default:
278: throw new InvalidArgumentException('Unsupported type.');
279: }
280: }
281:
282:
283:
284: 285: 286: 287: 288: 289:
290: public function escapeLike($value, $pos)
291: {
292: throw new NotImplementedException;
293: }
294:
295:
296:
297: 298: 299: 300: 301: 302: 303:
304: public function unescape($value, $type)
305: {
306: if ($type === dibi::BINARY) {
307: return $value;
308: }
309: throw new InvalidArgumentException('Unsupported type.');
310: }
311:
312:
313:
314: 315: 316: 317: 318: 319: 320:
321: public function applyLimit(&$sql, $limit, $offset)
322: {
323: if ($limit < 0 && $offset < 1) return;
324:
325: switch ($this->driverName) {
326: case 'mysql':
327: $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
328: . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
329: break;
330:
331: case 'pgsql':
332: if ($limit >= 0) $sql .= ' LIMIT ' . (int) $limit;
333: if ($offset > 0) $sql .= ' OFFSET ' . (int) $offset;
334: break;
335:
336: case 'sqlite':
337: case 'sqlite2':
338: $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
339: break;
340:
341: case 'oci':
342: if ($offset > 0) {
343: $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ') WHERE "__rnum" > '. (int) $offset;
344: } elseif ($limit >= 0) {
345: $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
346: }
347: break;
348:
349: case 'odbc':
350: case 'mssql':
351: if ($offset < 1) {
352: $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
353: break;
354: }
355: 356:
357: default:
358: throw new NotSupportedException('PDO or driver does not support applying limit or offset.');
359: }
360: }
361:
362:
363:
364:
365:
366:
367:
368: 369: 370: 371:
372: public function getRowCount()
373: {
374: return $this->resultSet->rowCount();
375: }
376:
377:
378:
379: 380: 381: 382: 383:
384: public function fetch($assoc)
385: {
386: return $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
387: }
388:
389:
390:
391: 392: 393: 394: 395:
396: public function seek($row)
397: {
398: throw new NotSupportedException('Cannot seek an unbuffered result set.');
399: }
400:
401:
402:
403: 404: 405: 406:
407: public function free()
408: {
409: $this->resultSet = NULL;
410: }
411:
412:
413:
414: 415: 416: 417: 418:
419: public function getResultColumns()
420: {
421: $count = $this->resultSet->columnCount();
422: $columns = array();
423: for ($i = 0; $i < $count; $i++) {
424: $row = @$this->resultSet->getColumnMeta($i); 425: if ($row === FALSE) {
426: throw new DibiDriverException('Driver does not support meta data.');
427: }
428: 429: 430: $row['table'] = isset($row['table']) ? $row['table'] : NULL;
431:
432: $columns[] = array(
433: 'name' => $row['name'],
434: 'table' => $row['table'],
435: 'nativetype' => $row['native_type'],
436: 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
437: 'vendor' => $row,
438: );
439: }
440: return $columns;
441: }
442:
443:
444:
445: 446: 447: 448:
449: public function getResultResource()
450: {
451: return $this->resultSet;
452: }
453:
454: }
455: