Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: dbo_mysql.php 7945 2008-12-19 02:16:01Z gwoo $ */
3
/**
4
 * MySQL layer for DBO
5
 *
6
 * Long description for file
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
11
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
12
 *
13
 * Licensed under The MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 *
16
 * @filesource
17
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
18
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
19
 * @package       cake
20
 * @subpackage    cake.cake.libs.model.datasources.dbo
21
 * @since         CakePHP(tm) v 0.10.5.1790
22
 * @version       $Revision: 7945 $
23
 * @modifiedby    $LastChangedBy: gwoo $
24
 * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
25
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
26
 */
27
/**
28
 * Provides common base for MySQL & MySQLi connections
29
 *
30
 * @package       cake
31
 * @subpackage    cake.cake.libs.model.datasources.dbo
32
 */
33
class DboMysqlBase extends DboSource {
34
/**
35
 * Description property.
36
 *
37
 * @var string
38
 */
39
	var $description = "MySQL DBO Base Driver";
40
/**
41
 * Start quote
42
 *
43
 * @var string
44
 */
45
	var $startQuote = "`";
46
/**
47
 * End quote
48
 *
49
 * @var string
50
 */
51
	var $endQuote = "`";
52
/**
53
 * use alias for update and delete. Set to true if version >= 4.1
54
 *
55
 * @var boolean
56
 * @access protected
57
 */
58
	var $_useAlias = true;
59
/**
60
 * Index of basic SQL commands
61
 *
62
 * @var array
63
 * @access protected
64
 */
65
	var $_commands = array(
66
		'begin'    => 'START TRANSACTION',
67
		'commit'   => 'COMMIT',
68
		'rollback' => 'ROLLBACK'
69
	);
70
/**
71
 * MySQL column definition
72
 *
73
 * @var array
74
 */
75
	var $columns = array(
76
		'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
77
		'string' => array('name' => 'varchar', 'limit' => '255'),
78
		'text' => array('name' => 'text'),
79
		'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
80
		'float' => array('name' => 'float', 'formatter' => 'floatval'),
81
		'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
82
		'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
83
		'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
84
		'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
85
		'binary' => array('name' => 'blob'),
86
		'boolean' => array('name' => 'tinyint', 'limit' => '1')
87
	);
88
/**
89
 * Generates and executes an SQL UPDATE statement for given model, fields, and values.
90
 *
91
 * @param Model $model
92
 * @param array $fields
93
 * @param array $values
94
 * @param mixed $conditions
95
 * @return array
96
 */
97
	function update(&$model, $fields = array(), $values = null, $conditions = null) {
98
		if (!$this->_useAlias) {
99
			return parent::update($model, $fields, $values, $conditions);
100
		}
101
 
102
		if ($values == null) {
103
			$combined = $fields;
104
		} else {
105
			$combined = array_combine($fields, $values);
106
		}
107
 
108
		$alias = $joins = false;
109
		$fields = $this->_prepareUpdateFields($model, $combined, empty($conditions), !empty($conditions));
110
		$fields = join(', ', $fields);
111
		$table = $this->fullTableName($model);
112
 
113
		if (!empty($conditions)) {
114
			$alias = $this->name($model->alias);
115
			if ($model->name == $model->alias) {
116
				$joins = implode(' ', $this->_getJoins($model));
117
			}
118
		}
119
		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
120
 
121
		if ($conditions === false) {
122
			return false;
123
		}
124
 
125
		if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) {
126
			$model->onError();
127
			return false;
128
		}
129
		return true;
130
	}
131
/**
132
 * Generates and executes an SQL DELETE statement for given id/conditions on given model.
133
 *
134
 * @param Model $model
135
 * @param mixed $conditions
136
 * @return boolean Success
137
 */
138
	function delete(&$model, $conditions = null) {
139
		if (!$this->_useAlias) {
140
			return parent::delete($model, $conditions);
141
		}
142
		$alias = $this->name($model->alias);
143
		$table = $this->fullTableName($model);
144
		$joins = implode(' ', $this->_getJoins($model));
145
 
146
		if (empty($conditions)) {
147
			$alias = $joins = false;
148
		}
149
		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
150
 
151
		if ($conditions === false) {
152
			return false;
153
		}
154
 
155
		if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
156
			$model->onError();
157
			return false;
158
		}
159
		return true;
160
	}
161
/**
162
 * Sets the database encoding
163
 *
164
 * @param string $enc Database encoding
165
 */
166
	function setEncoding($enc) {
167
		return $this->_execute('SET NAMES ' . $enc) != false;
168
	}
169
/**
170
 * Returns an array of the indexes in given datasource name.
171
 *
172
 * @param string $model Name of model to inspect
173
 * @return array Fields in table. Keys are column and unique
174
 */
175
	function index($model) {
176
		$index = array();
177
		$table = $this->fullTableName($model);
178
		if ($table) {
179
			$indexes = $this->query('SHOW INDEX FROM ' . $table);
180
			$keys = Set::extract($indexes, '{n}.STATISTICS');
181
			foreach ($keys as $i => $key) {
182
				if (!isset($index[$key['Key_name']])) {
183
					$col = array();
184
					$index[$key['Key_name']]['column'] = $key['Column_name'];
185
					$index[$key['Key_name']]['unique'] = intval($key['Non_unique'] == 0);
186
				} else {
187
					if (!is_array($index[$key['Key_name']]['column'])) {
188
						$col[] = $index[$key['Key_name']]['column'];
189
					}
190
					$col[] = $key['Column_name'];
191
					$index[$key['Key_name']]['column'] = $col;
192
				}
193
			}
194
		}
195
		return $index;
196
	}
197
/**
198
 * Generate a MySQL Alter Table syntax for the given Schema comparison
199
 *
200
 * @param array $compare Result of a CakeSchema::compare()
201
 * @return array Array of alter statements to make.
202
 */
203
	function alterSchema($compare, $table = null) {
204
		if (!is_array($compare)) {
205
			return false;
206
		}
207
		$out = '';
208
		$colList = array();
209
		foreach ($compare as $curTable => $types) {
210
			$indexes = array();
211
			if (!$table || $table == $curTable) {
212
				$out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
213
				foreach ($types as $type => $column) {
214
					if (isset($column['indexes'])) {
215
						$indexes[$type] = $column['indexes'];
216
						unset($column['indexes']);
217
					}
218
					switch ($type) {
219
						case 'add':
220
							foreach ($column as $field => $col) {
221
								$col['name'] = $field;
222
								$alter = 'ADD '.$this->buildColumn($col);
223
								if (isset($col['after'])) {
224
									$alter .= ' AFTER '. $this->name($col['after']);
225
								}
226
								$colList[] = $alter;
227
							}
228
						break;
229
						case 'drop':
230
							foreach ($column as $field => $col) {
231
								$col['name'] = $field;
232
								$colList[] = 'DROP '.$this->name($field);
233
							}
234
						break;
235
						case 'change':
236
							foreach ($column as $field => $col) {
237
								if (!isset($col['name'])) {
238
									$col['name'] = $field;
239
								}
240
								$colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
241
							}
242
						break;
243
					}
244
				}
245
				$colList = array_merge($colList, $this->_alterIndexes($curTable, $indexes));
246
				$out .= "\t" . join(",\n\t", $colList) . ";\n\n";
247
			}
248
		}
249
		return $out;
250
	}
251
/**
252
 * Generate a MySQL "drop table" statement for the given Schema object
253
 *
254
 * @param object $schema An instance of a subclass of CakeSchema
255
 * @param string $table Optional.  If specified only the table name given will be generated.
256
 *                      Otherwise, all tables defined in the schema are generated.
257
 * @return string
258
 */
259
	function dropSchema($schema, $table = null) {
260
		if (!is_a($schema, 'CakeSchema')) {
261
			trigger_error(__('Invalid schema object', true), E_USER_WARNING);
262
			return null;
263
		}
264
		$out = '';
265
		foreach ($schema->tables as $curTable => $columns) {
266
			if (!$table || $table == $curTable) {
267
				$out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n";
268
			}
269
		}
270
		return $out;
271
	}
272
/**
273
 * Generate MySQL index alteration statements for a table.
274
 *
275
 * @param string $table Table to alter indexes for
276
 * @param array $new Indexes to add and drop
277
 * @return array Index alteration statements
278
 */
279
	function _alterIndexes($table, $indexes) {
280
		$alter = array();
281
		if (isset($indexes['drop'])) {
282
			foreach($indexes['drop'] as $name => $value) {
283
				$out = 'DROP ';
284
				if ($name == 'PRIMARY') {
285
					$out .= 'PRIMARY KEY';
286
				} else {
287
					$out .= 'KEY ' . $name;
288
				}
289
				$alter[] = $out;
290
			}
291
		}
292
		if (isset($indexes['add'])) {
293
			foreach ($indexes['add'] as $name => $value) {
294
				$out = 'ADD ';
295
				if ($name == 'PRIMARY') {
296
					$out .= 'PRIMARY ';
297
					$name = null;
298
				} else {
299
					if (!empty($value['unique'])) {
300
						$out .= 'UNIQUE ';
301
					}
302
				}
303
				if (is_array($value['column'])) {
304
					$out .= 'KEY '. $name .' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
305
				} else {
306
					$out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')';
307
				}
308
				$alter[] = $out;
309
			}
310
		}
311
		return $alter;
312
	}
313
/**
314
 * Inserts multiple values into a table
315
 *
316
 * @param string $table
317
 * @param string $fields
318
 * @param array $values
319
 */
320
	function insertMulti($table, $fields, $values) {
321
		$table = $this->fullTableName($table);
322
		if (is_array($fields)) {
323
			$fields = join(', ', array_map(array(&$this, 'name'), $fields));
324
		}
325
		$values = implode(', ', $values);
326
		$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
327
	}
328
}
329
 
330
/**
331
 * MySQL DBO driver object
332
 *
333
 * Provides connection and SQL generation for MySQL RDMS
334
 *
335
 * @package       cake
336
 * @subpackage    cake.cake.libs.model.datasources.dbo
337
 */
338
class DboMysql extends DboMysqlBase {
339
/**
340
 * Enter description here...
341
 *
342
 * @var unknown_type
343
 */
344
	var $description = "MySQL DBO Driver";
345
/**
346
 * Base configuration settings for MySQL driver
347
 *
348
 * @var array
349
 */
350
	var $_baseConfig = array(
351
		'persistent' => true,
352
		'host' => 'localhost',
353
		'login' => 'root',
354
		'password' => '',
355
		'database' => 'cake',
356
		'port' => '3306',
357
		'connect' => 'mysql_pconnect'
358
	);
359
/**
360
 * Connects to the database using options in the given configuration array.
361
 *
362
 * @return boolean True if the database could be connected, else false
363
 */
364
	function connect() {
365
		$config = $this->config;
366
		$connect = $config['connect'];
367
		$this->connected = false;
368
 
369
		if (!$config['persistent']) {
370
			$this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
371
		} else {
372
			$this->connection = $connect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
373
		}
374
 
375
		if (mysql_select_db($config['database'], $this->connection)) {
376
			$this->connected = true;
377
		}
378
 
379
		if (isset($config['encoding']) && !empty($config['encoding'])) {
380
			$this->setEncoding($config['encoding']);
381
		}
382
 
383
		$this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
384
 
385
		return $this->connected;
386
	}
387
/**
388
 * Disconnects from database.
389
 *
390
 * @return boolean True if the database could be disconnected, else false
391
 */
392
	function disconnect() {
393
		if (isset($this->results) && is_resource($this->results)) {
394
			mysql_free_result($this->results);
395
		}
396
		$this->connected = !@mysql_close($this->connection);
397
		return !$this->connected;
398
	}
399
/**
400
 * Executes given SQL statement.
401
 *
402
 * @param string $sql SQL statement
403
 * @return resource Result resource identifier
404
 * @access protected
405
 */
406
	function _execute($sql) {
407
		return mysql_query($sql, $this->connection);
408
	}
409
/**
410
 * Returns an array of sources (tables) in the database.
411
 *
412
 * @return array Array of tablenames in the database
413
 */
414
	function listSources() {
415
		$cache = parent::listSources();
416
		if ($cache != null) {
417
			return $cache;
418
		}
419
		$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
420
 
421
		if (!$result) {
422
			return array();
423
		} else {
424
			$tables = array();
425
 
426
			while ($line = mysql_fetch_array($result)) {
427
				$tables[] = $line[0];
428
			}
429
			parent::listSources($tables);
430
			return $tables;
431
		}
432
	}
433
/**
434
 * Returns an array of the fields in given table name.
435
 *
436
 * @param string $tableName Name of database table to inspect
437
 * @return array Fields in table. Keys are name and type
438
 */
439
	function describe(&$model) {
440
		$cache = parent::describe($model);
441
		if ($cache != null) {
442
			return $cache;
443
		}
444
		$fields = false;
445
		$cols = $this->query('DESCRIBE ' . $this->fullTableName($model));
446
 
447
		foreach ($cols as $column) {
448
			$colKey = array_keys($column);
449
			if (isset($column[$colKey[0]]) && !isset($column[0])) {
450
				$column[0] = $column[$colKey[0]];
451
			}
452
			if (isset($column[0])) {
453
				$fields[$column[0]['Field']] = array(
454
					'type'		=> $this->column($column[0]['Type']),
455
					'null'		=> ($column[0]['Null'] == 'YES' ? true : false),
456
					'default'	=> $column[0]['Default'],
457
					'length'	=> $this->length($column[0]['Type']),
458
				);
459
				if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
460
					$fields[$column[0]['Field']]['key']	= $this->index[$column[0]['Key']];
461
				}
462
			}
463
		}
464
		$this->__cacheDescription($this->fullTableName($model, false), $fields);
465
		return $fields;
466
	}
467
/**
468
 * Returns a quoted and escaped string of $data for use in an SQL statement.
469
 *
470
 * @param string $data String to be prepared for use in an SQL statement
471
 * @param string $column The column into which this data will be inserted
472
 * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
473
 * @return string Quoted and escaped data
474
 */
475
	function value($data, $column = null, $safe = false) {
476
		$parent = parent::value($data, $column, $safe);
477
 
478
		if ($parent != null) {
479
			return $parent;
480
		} elseif ($data === null || (is_array($data) && empty($data))) {
481
			return 'NULL';
482
		} elseif ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
483
			return  "''";
484
		}
485
		if (empty($column)) {
486
			$column = $this->introspectType($data);
487
		}
488
 
489
		switch ($column) {
490
			case 'boolean':
491
				return $this->boolean((bool)$data);
492
			break;
493
			case 'integer':
494
			case 'float':
495
				if ($data === '') {
496
					return 'NULL';
497
				}
498
				if ((is_int($data) || is_float($data) || $data === '0') || (
499
					is_numeric($data) && strpos($data, ',') === false &&
500
					$data[0] != '0' && strpos($data, 'e') === false)) {
501
						return $data;
502
					}
503
			default:
504
				$data = "'" . mysql_real_escape_string($data, $this->connection) . "'";
505
			break;
506
		}
507
		return $data;
508
	}
509
/**
510
 * Returns a formatted error message from previous database operation.
511
 *
512
 * @return string Error message with error number
513
 */
514
	function lastError() {
515
		if (mysql_errno($this->connection)) {
516
			return mysql_errno($this->connection).': '.mysql_error($this->connection);
517
		}
518
		return null;
519
	}
520
/**
521
 * Returns number of affected rows in previous database operation. If no previous operation exists,
522
 * this returns false.
523
 *
524
 * @return integer Number of affected rows
525
 */
526
	function lastAffected() {
527
		if ($this->_result) {
528
			return mysql_affected_rows($this->connection);
529
		}
530
		return null;
531
	}
532
/**
533
 * Returns number of rows in previous resultset. If no previous resultset exists,
534
 * this returns false.
535
 *
536
 * @return integer Number of rows in resultset
537
 */
538
	function lastNumRows() {
539
		if ($this->hasResult()) {
540
			return mysql_num_rows($this->_result);
541
		}
542
		return null;
543
	}
544
/**
545
 * Returns the ID generated from the previous INSERT operation.
546
 *
547
 * @param unknown_type $source
548
 * @return in
549
 */
550
	function lastInsertId($source = null) {
551
		$id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
552
		if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
553
			return $id[0]['insertID'];
554
		}
555
 
556
		return null;
557
	}
558
/**
559
 * Converts database-layer column types to basic types
560
 *
561
 * @param string $real Real database-layer column type (i.e. "varchar(255)")
562
 * @return string Abstract column type (i.e. "string")
563
 */
564
	function column($real) {
565
		if (is_array($real)) {
566
			$col = $real['name'];
567
			if (isset($real['limit'])) {
568
				$col .= '('.$real['limit'].')';
569
			}
570
			return $col;
571
		}
572
 
573
		$col = str_replace(')', '', $real);
574
		$limit = $this->length($real);
575
		if (strpos($col, '(') !== false) {
576
			list($col, $vals) = explode('(', $col);
577
		}
578
 
579
		if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
580
			return $col;
581
		}
582
		if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
583
			return 'boolean';
584
		}
585
		if (strpos($col, 'int') !== false) {
586
			return 'integer';
587
		}
588
		if (strpos($col, 'char') !== false || $col == 'tinytext') {
589
			return 'string';
590
		}
591
		if (strpos($col, 'text') !== false) {
592
			return 'text';
593
		}
594
		if (strpos($col, 'blob') !== false || $col == 'binary') {
595
			return 'binary';
596
		}
597
		if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) {
598
			return 'float';
599
		}
600
		if (strpos($col, 'enum') !== false) {
601
			return "enum($vals)";
602
		}
603
		return 'text';
604
	}
605
/**
606
 * Enter description here...
607
 *
608
 * @param unknown_type $results
609
 */
610
	function resultSet(&$results) {
611
		if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
612
			mysql_free_result($this->results);
613
		}
614
		$this->results =& $results;
615
		$this->map = array();
616
		$numFields = mysql_num_fields($results);
617
		$index = 0;
618
		$j = 0;
619
 
620
		while ($j < $numFields) {
621
 
622
			$column = mysql_fetch_field($results,$j);
623
			if (!empty($column->table)) {
624
				$this->map[$index++] = array($column->table, $column->name);
625
			} else {
626
				$this->map[$index++] = array(0, $column->name);
627
			}
628
			$j++;
629
		}
630
	}
631
/**
632
 * Fetches the next row from the current result set
633
 *
634
 * @return unknown
635
 */
636
	function fetchResult() {
637
		if ($row = mysql_fetch_row($this->results)) {
638
			$resultRow = array();
639
			$i = 0;
640
			foreach ($row as $index => $field) {
641
				list($table, $column) = $this->map[$index];
642
				$resultRow[$table][$column] = $row[$index];
643
				$i++;
644
			}
645
			return $resultRow;
646
		} else {
647
			return false;
648
		}
649
	}
650
/**
651
 * Gets the database encoding
652
 *
653
 * @return string The database encoding
654
 */
655
	function getEncoding() {
656
		return mysql_client_encoding($this->connection);
657
	}
658
}
659
?>