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: class DboMysqlBase extends DboSource {
28:
29: 30: 31: 32: 33:
34: var $description = "MySQL DBO Base Driver";
35:
36: 37: 38: 39: 40:
41: var $startQuote = "`";
42:
43: 44: 45: 46: 47:
48: var $endQuote = "`";
49:
50: 51: 52: 53: 54: 55:
56: var $_useAlias = true;
57:
58: 59: 60: 61: 62: 63:
64: var $_commands = array(
65: 'begin' => 'START TRANSACTION',
66: 'commit' => 'COMMIT',
67: 'rollback' => 'ROLLBACK'
68: );
69:
70: 71: 72: 73: 74: 75:
76: var $fieldParameters = array(
77: 'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'),
78: 'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'),
79: 'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault')
80: );
81:
82: 83: 84: 85: 86: 87:
88: var $tableParameters = array(
89: 'charset' => array('value' => 'DEFAULT CHARSET', 'quote' => false, 'join' => '=', 'column' => 'charset'),
90: 'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => '=', 'column' => 'Collation'),
91: 'engine' => array('value' => 'ENGINE', 'quote' => false, 'join' => '=', 'column' => 'Engine')
92: );
93:
94: 95: 96: 97: 98:
99: var $columns = array(
100: 'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
101: 'string' => array('name' => 'varchar', 'limit' => '255'),
102: 'text' => array('name' => 'text'),
103: 'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
104: 'float' => array('name' => 'float', 'formatter' => 'floatval'),
105: 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
106: 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
107: 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
108: 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
109: 'binary' => array('name' => 'blob'),
110: 'boolean' => array('name' => 'tinyint', 'limit' => '1')
111: );
112:
113: 114: 115: 116: 117: 118:
119: function describe(&$model) {
120: $cache = parent::describe($model);
121: if ($cache != null) {
122: return $cache;
123: }
124: $fields = false;
125: $cols = $this->query('SHOW FULL COLUMNS FROM ' . $this->fullTableName($model));
126:
127: foreach ($cols as $column) {
128: $colKey = array_keys($column);
129: if (isset($column[$colKey[0]]) && !isset($column[0])) {
130: $column[0] = $column[$colKey[0]];
131: }
132: if (isset($column[0])) {
133: $fields[$column[0]['Field']] = array(
134: 'type' => $this->column($column[0]['Type']),
135: 'null' => ($column[0]['Null'] == 'YES' ? true : false),
136: 'default' => $column[0]['Default'],
137: 'length' => $this->length($column[0]['Type']),
138: );
139: if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
140: $fields[$column[0]['Field']]['key'] = $this->index[$column[0]['Key']];
141: }
142: foreach ($this->fieldParameters as $name => $value) {
143: if (!empty($column[0][$value['column']])) {
144: $fields[$column[0]['Field']][$name] = $column[0][$value['column']];
145: }
146: }
147: if (isset($fields[$column[0]['Field']]['collate'])) {
148: $charset = $this->getCharsetName($fields[$column[0]['Field']]['collate']);
149: if ($charset) {
150: $fields[$column[0]['Field']]['charset'] = $charset;
151: }
152: }
153: }
154: }
155: $this->__cacheDescription($this->fullTableName($model, false), $fields);
156: return $fields;
157: }
158:
159: 160: 161: 162: 163: 164: 165: 166: 167:
168: function update(&$model, $fields = array(), $values = null, $conditions = null) {
169: if (!$this->_useAlias) {
170: return parent::update($model, $fields, $values, $conditions);
171: }
172:
173: if ($values == null) {
174: $combined = $fields;
175: } else {
176: $combined = array_combine($fields, $values);
177: }
178:
179: $alias = $joins = false;
180: $fields = $this->_prepareUpdateFields($model, $combined, empty($conditions), !empty($conditions));
181: $fields = implode(', ', $fields);
182: $table = $this->fullTableName($model);
183:
184: if (!empty($conditions)) {
185: $alias = $this->name($model->alias);
186: if ($model->name == $model->alias) {
187: $joins = implode(' ', $this->_getJoins($model));
188: }
189: }
190: $conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
191:
192: if ($conditions === false) {
193: return false;
194: }
195:
196: if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) {
197: $model->onError();
198: return false;
199: }
200: return true;
201: }
202:
203: 204: 205: 206: 207: 208: 209:
210: function delete(&$model, $conditions = null) {
211: if (!$this->_useAlias) {
212: return parent::delete($model, $conditions);
213: }
214: $alias = $this->name($model->alias);
215: $table = $this->fullTableName($model);
216: $joins = implode(' ', $this->_getJoins($model));
217:
218: if (empty($conditions)) {
219: $alias = $joins = false;
220: }
221: $complexConditions = false;
222: foreach ((array)$conditions as $key => $value) {
223: if (strpos($key, $model->alias) === false) {
224: $complexConditions = true;
225: break;
226: }
227: }
228: if (!$complexConditions) {
229: $joins = false;
230: }
231:
232: $conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
233: if ($conditions === false) {
234: return false;
235: }
236: if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
237: $model->onError();
238: return false;
239: }
240: return true;
241: }
242:
243: 244: 245: 246: 247:
248: function setEncoding($enc) {
249: return $this->_execute('SET NAMES ' . $enc) != false;
250: }
251:
252: 253: 254: 255: 256: 257:
258: function index($model) {
259: $index = array();
260: $table = $this->fullTableName($model);
261: if ($table) {
262: $indexes = $this->query('SHOW INDEX FROM ' . $table);
263: if (isset($indexes[0]['STATISTICS'])) {
264: $keys = Set::extract($indexes, '{n}.STATISTICS');
265: } else {
266: $keys = Set::extract($indexes, '{n}.0');
267: }
268: foreach ($keys as $i => $key) {
269: if (!isset($index[$key['Key_name']])) {
270: $col = array();
271: $index[$key['Key_name']]['column'] = $key['Column_name'];
272: $index[$key['Key_name']]['unique'] = intval($key['Non_unique'] == 0);
273: } else {
274: if (!is_array($index[$key['Key_name']]['column'])) {
275: $col[] = $index[$key['Key_name']]['column'];
276: }
277: $col[] = $key['Column_name'];
278: $index[$key['Key_name']]['column'] = $col;
279: }
280: }
281: }
282: return $index;
283: }
284:
285: 286: 287: 288: 289: 290:
291: function alterSchema($compare, $table = null) {
292: if (!is_array($compare)) {
293: return false;
294: }
295: $out = '';
296: $colList = array();
297: foreach ($compare as $curTable => $types) {
298: $indexes = $tableParameters = $colList = array();
299: if (!$table || $table == $curTable) {
300: $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
301: foreach ($types as $type => $column) {
302: if (isset($column['indexes'])) {
303: $indexes[$type] = $column['indexes'];
304: unset($column['indexes']);
305: }
306: if (isset($column['tableParameters'])) {
307: $tableParameters[$type] = $column['tableParameters'];
308: unset($column['tableParameters']);
309: }
310: switch ($type) {
311: case 'add':
312: foreach ($column as $field => $col) {
313: $col['name'] = $field;
314: $alter = 'ADD ' . $this->buildColumn($col);
315: if (isset($col['after'])) {
316: $alter .= ' AFTER ' . $this->name($col['after']);
317: }
318: $colList[] = $alter;
319: }
320: break;
321: case 'drop':
322: foreach ($column as $field => $col) {
323: $col['name'] = $field;
324: $colList[] = 'DROP ' . $this->name($field);
325: }
326: break;
327: case 'change':
328: foreach ($column as $field => $col) {
329: if (!isset($col['name'])) {
330: $col['name'] = $field;
331: }
332: $colList[] = 'CHANGE ' . $this->name($field) . ' ' . $this->buildColumn($col);
333: }
334: break;
335: }
336: }
337: $colList = array_merge($colList, $this->_alterIndexes($curTable, $indexes));
338: $colList = array_merge($colList, $this->_alterTableParameters($curTable, $tableParameters));
339: $out .= "\t" . join(",\n\t", $colList) . ";\n\n";
340: }
341: }
342: return $out;
343: }
344:
345: 346: 347: 348: 349: 350: 351: 352:
353: function dropSchema($schema, $table = null) {
354: if (!is_a($schema, 'CakeSchema')) {
355: trigger_error(__('Invalid schema object', true), E_USER_WARNING);
356: return null;
357: }
358: $out = '';
359: foreach ($schema->tables as $curTable => $columns) {
360: if (!$table || $table == $curTable) {
361: $out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n";
362: }
363: }
364: return $out;
365: }
366:
367: 368: 369: 370: 371: 372: 373: 374:
375: function _alterTableParameters($table, $parameters) {
376: if (isset($parameters['change'])) {
377: return $this->buildTableParameters($parameters['change']);
378: }
379: return array();
380: }
381:
382: 383: 384: 385: 386: 387: 388:
389: function _alterIndexes($table, $indexes) {
390: $alter = array();
391: if (isset($indexes['drop'])) {
392: foreach($indexes['drop'] as $name => $value) {
393: $out = 'DROP ';
394: if ($name == 'PRIMARY') {
395: $out .= 'PRIMARY KEY';
396: } else {
397: $out .= 'KEY ' . $name;
398: }
399: $alter[] = $out;
400: }
401: }
402: if (isset($indexes['add'])) {
403: foreach ($indexes['add'] as $name => $value) {
404: $out = 'ADD ';
405: if ($name == 'PRIMARY') {
406: $out .= 'PRIMARY ';
407: $name = null;
408: } else {
409: if (!empty($value['unique'])) {
410: $out .= 'UNIQUE ';
411: }
412: }
413: if (is_array($value['column'])) {
414: $out .= 'KEY '. $name .' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
415: } else {
416: $out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')';
417: }
418: $alter[] = $out;
419: }
420: }
421: return $alter;
422: }
423:
424: 425: 426: 427: 428: 429: 430:
431: function insertMulti($table, $fields, $values) {
432: $table = $this->fullTableName($table);
433: if (is_array($fields)) {
434: $fields = implode(', ', array_map(array(&$this, 'name'), $fields));
435: }
436: $values = implode(', ', $values);
437: $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
438: }
439: 440: 441: 442: 443: 444:
445: function listDetailedSources($name = null) {
446: $condition = '';
447: if (is_string($name)) {
448: $condition = ' LIKE ' . $this->value($name);
449: }
450: $result = $this->query('SHOW TABLE STATUS FROM ' . $this->name($this->config['database']) . $condition . ';');
451: if (!$result) {
452: return array();
453: } else {
454: $tables = array();
455: foreach ($result as $row) {
456: $tables[$row['TABLES']['Name']] = $row['TABLES'];
457: if (!empty($row['TABLES']['Collation'])) {
458: $charset = $this->getCharsetName($row['TABLES']['Collation']);
459: if ($charset) {
460: $tables[$row['TABLES']['Name']]['charset'] = $charset;
461: }
462: }
463: }
464: if (is_string($name)) {
465: return $tables[$name];
466: }
467: return $tables;
468: }
469: }
470:
471: 472: 473: 474: 475: 476:
477: function column($real) {
478: if (is_array($real)) {
479: $col = $real['name'];
480: if (isset($real['limit'])) {
481: $col .= '('.$real['limit'].')';
482: }
483: return $col;
484: }
485:
486: $col = str_replace(')', '', $real);
487: $limit = $this->length($real);
488: if (strpos($col, '(') !== false) {
489: list($col, $vals) = explode('(', $col);
490: }
491:
492: if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
493: return $col;
494: }
495: if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
496: return 'boolean';
497: }
498: if (strpos($col, 'int') !== false) {
499: return 'integer';
500: }
501: if (strpos($col, 'char') !== false || $col == 'tinytext') {
502: return 'string';
503: }
504: if (strpos($col, 'text') !== false) {
505: return 'text';
506: }
507: if (strpos($col, 'blob') !== false || $col == 'binary') {
508: return 'binary';
509: }
510: if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) {
511: return 'float';
512: }
513: if (strpos($col, 'enum') !== false) {
514: return "enum($vals)";
515: }
516: return 'text';
517: }
518: }
519:
520: 521: 522: 523: 524: 525: 526: 527:
528: class DboMysql extends DboMysqlBase {
529:
530: 531: 532: 533: 534:
535: var $description = "MySQL DBO Driver";
536:
537: 538: 539: 540: 541:
542: var $_baseConfig = array(
543: 'persistent' => true,
544: 'host' => 'localhost',
545: 'login' => 'root',
546: 'password' => '',
547: 'database' => 'cake',
548: 'port' => '3306'
549: );
550:
551: 552: 553: 554: 555:
556: function connect() {
557: $config = $this->config;
558: $this->connected = false;
559:
560: if (!$config['persistent']) {
561: $this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
562: $config['connect'] = 'mysql_connect';
563: } else {
564: $this->connection = mysql_pconnect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
565: }
566:
567: if (!$this->connection) {
568: return false;
569: }
570:
571: if (mysql_select_db($config['database'], $this->connection)) {
572: $this->connected = true;
573: }
574:
575: if (!empty($config['encoding'])) {
576: $this->setEncoding($config['encoding']);
577: }
578:
579: $this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
580:
581: return $this->connected;
582: }
583:
584: 585: 586: 587: 588:
589: function enabled() {
590: return extension_loaded('mysql');
591: }
592: 593: 594: 595: 596:
597: function disconnect() {
598: if (isset($this->results) && is_resource($this->results)) {
599: mysql_free_result($this->results);
600: }
601: $this->connected = !@mysql_close($this->connection);
602: return !$this->connected;
603: }
604:
605: 606: 607: 608: 609: 610: 611:
612: function _execute($sql) {
613: return mysql_query($sql, $this->connection);
614: }
615:
616: 617: 618: 619: 620:
621: function listSources() {
622: $cache = parent::listSources();
623: if ($cache != null) {
624: return $cache;
625: }
626: $result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
627:
628: if (!$result) {
629: return array();
630: } else {
631: $tables = array();
632:
633: while ($line = mysql_fetch_row($result)) {
634: $tables[] = $line[0];
635: }
636: parent::listSources($tables);
637: return $tables;
638: }
639: }
640:
641: 642: 643: 644: 645: 646: 647: 648:
649: function value($data, $column = null, $safe = false) {
650: $parent = parent::value($data, $column, $safe);
651:
652: if ($parent != null) {
653: return $parent;
654: }
655: if ($data === null || (is_array($data) && empty($data))) {
656: return 'NULL';
657: }
658: if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
659: return "''";
660: }
661: if (empty($column)) {
662: $column = $this->introspectType($data);
663: }
664:
665: switch ($column) {
666: case 'boolean':
667: return $this->boolean((bool)$data);
668: break;
669: case 'integer':
670: case 'float':
671: if ($data === '') {
672: return 'NULL';
673: }
674: if (is_float($data)) {
675: return str_replace(',', '.', strval($data));
676: }
677: if ((is_int($data) || $data === '0') || (
678: is_numeric($data) && strpos($data, ',') === false &&
679: $data[0] != '0' && strpos($data, 'e') === false)
680: ) {
681: return $data;
682: }
683: default:
684: return "'" . mysql_real_escape_string($data, $this->connection) . "'";
685: break;
686: }
687: }
688:
689: 690: 691: 692: 693:
694: function lastError() {
695: if (mysql_errno($this->connection)) {
696: return mysql_errno($this->connection).': '.mysql_error($this->connection);
697: }
698: return null;
699: }
700:
701: 702: 703: 704: 705: 706:
707: function lastAffected() {
708: if ($this->_result) {
709: return mysql_affected_rows($this->connection);
710: }
711: return null;
712: }
713:
714: 715: 716: 717: 718: 719:
720: function lastNumRows() {
721: if ($this->hasResult()) {
722: return mysql_num_rows($this->_result);
723: }
724: return null;
725: }
726:
727: 728: 729: 730: 731: 732:
733: function lastInsertId($source = null) {
734: $id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
735: if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
736: return $id[0]['insertID'];
737: }
738:
739: return null;
740: }
741:
742: 743: 744: 745: 746:
747: function resultSet(&$results) {
748: if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
749: mysql_free_result($this->results);
750: }
751: $this->results =& $results;
752: $this->map = array();
753: $numFields = mysql_num_fields($results);
754: $index = 0;
755: $j = 0;
756:
757: while ($j < $numFields) {
758: $column = mysql_fetch_field($results, $j);
759: if (!empty($column->table) && strpos($column->name, $this->virtualFieldSeparator) === false) {
760: $this->map[$index++] = array($column->table, $column->name);
761: } else {
762: $this->map[$index++] = array(0, $column->name);
763: }
764: $j++;
765: }
766: }
767:
768: 769: 770: 771: 772:
773: function fetchResult() {
774: if ($row = mysql_fetch_row($this->results)) {
775: $resultRow = array();
776: $i = 0;
777: foreach ($row as $index => $field) {
778: list($table, $column) = $this->map[$index];
779: $resultRow[$table][$column] = $row[$index];
780: $i++;
781: }
782: return $resultRow;
783: } else {
784: return false;
785: }
786: }
787:
788: 789: 790: 791: 792:
793: function getEncoding() {
794: return mysql_client_encoding($this->connection);
795: }
796:
797: 798: 799: 800: 801: 802:
803: function getCharsetName($name) {
804: if ((bool)version_compare(mysql_get_server_info($this->connection), "5", ">=")) {
805: $cols = $this->query('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
806: if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
807: return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
808: }
809: }
810: return false;
811: }
812: }
813: