# Coverage for Doctrine_DataDict_Mysql

Back to coverage report

1 | <?php
2 | /*
3 | * $Id: Mysql.php 3084 2007-11-08 14:01:03Z samw3 $
4 | *
5 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 | *
17 | * This software consists of voluntary contributions made by many individuals
18 | * and is licensed under the LGPL. For more information, see
19 | * <http://www.phpdoctrine.org>.
20 | */
21 | Doctrine::autoload('Doctrine_DataDict');
22 | /**
23 | * @package Doctrine
24 | * @subpackage DataDict
25 | * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
26 | * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
27 | * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
28 | * @version $Revision: 3084 $
29 | * @link www.phpdoctrine.org
30 | * @since 1.0
31 | */
32 | class Doctrine_DataDict_Mysql extends Doctrine_DataDict
33 | {
34 | protected $keywords = array(
35 | 'ADD', 'ALL', 'ALTER',
36 | 'ANALYZE', 'AND', 'AS',
37 | 'ASC', 'ASENSITIVE', 'BEFORE',
38 | 'BETWEEN', 'BIGINT', 'BINARY',
39 | 'BLOB', 'BOTH', 'BY',
40 | 'CALL', 'CASCADE', 'CASE',
41 | 'CHANGE', 'CHAR', 'CHARACTER',
42 | 'CHECK', 'COLLATE', 'COLUMN',
43 | 'CONDITION', 'CONNECTION', 'CONSTRAINT',
44 | 'CONTINUE', 'CONVERT', 'CREATE',
45 | 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME',
46 | 'CURRENT_TIMESTAMP', 'CURRENT_USER', 'CURSOR',
47 | 'DATABASE', 'DATABASES', 'DAY_HOUR',
48 | 'DAY_MICROSECOND', 'DAY_MINUTE', 'DAY_SECOND',
49 | 'DEC', 'DECIMAL', 'DECLARE',
50 | 'DEFAULT', 'DELAYED', 'DELETE',
51 | 'DESC', 'DESCRIBE', 'DETERMINISTIC',
52 | 'DISTINCT', 'DISTINCTROW', 'DIV',
53 | 'DOUBLE', 'DROP', 'DUAL',
54 | 'EACH', 'ELSE', 'ELSEIF',
55 | 'ENCLOSED', 'ESCAPED', 'EXISTS',
56 | 'EXIT', 'EXPLAIN', 'FALSE',
57 | 'FETCH', 'FLOAT', 'FLOAT4',
58 | 'FLOAT8', 'FOR', 'FORCE',
59 | 'FOREIGN', 'FROM', 'FULLTEXT',
60 | 'GRANT', 'GROUP', 'HAVING',
61 | 'HIGH_PRIORITY', 'HOUR_MICROSECOND', 'HOUR_MINUTE',
62 | 'HOUR_SECOND', 'IF', 'IGNORE',
63 | 'IN', 'INDEX', 'INFILE',
64 | 'INNER', 'INOUT', 'INSENSITIVE',
65 | 'INSERT', 'INT', 'INT1',
66 | 'INT2', 'INT3', 'INT4',
67 | 'INT8', 'INTEGER', 'INTERVAL',
68 | 'INTO', 'IS', 'ITERATE',
69 | 'JOIN', 'KEY', 'KEYS',
70 | 'KILL', 'LEADING', 'LEAVE',
71 | 'LEFT', 'LIKE', 'LIMIT',
72 | 'LINES', 'LOAD', 'LOCALTIME',
73 | 'LOCALTIMESTAMP', 'LOCK', 'LONG',
74 | 'LONGBLOB', 'LONGTEXT', 'LOOP',
75 | 'LOW_PRIORITY', 'MATCH', 'MEDIUMBLOB',
76 | 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT',
77 | 'MINUTE_MICROSECOND', 'MINUTE_SECOND', 'MOD',
78 | 'MODIFIES', 'NATURAL', 'NOT',
79 | 'NO_WRITE_TO_BINLOG', 'NULL', 'NUMERIC',
80 | 'ON', 'OPTIMIZE', 'OPTION',
81 | 'OPTIONALLY', 'OR', 'ORDER',
82 | 'OUT', 'OUTER', 'OUTFILE',
83 | 'PRECISION', 'PRIMARY', 'PROCEDURE',
84 | 'PURGE', 'RAID0', 'READ',
85 | 'READS', 'REAL', 'REFERENCES',
86 | 'REGEXP', 'RELEASE', 'RENAME',
87 | 'REPEAT', 'REPLACE', 'REQUIRE',
88 | 'RESTRICT', 'RETURN', 'REVOKE',
89 | 'RIGHT', 'RLIKE', 'SCHEMA',
90 | 'SCHEMAS', 'SECOND_MICROSECOND', 'SELECT',
91 | 'SENSITIVE', 'SEPARATOR', 'SET',
92 | 'SHOW', 'SMALLINT', 'SONAME',
93 | 'SPATIAL', 'SPECIFIC', 'SQL',
94 | 'SQLEXCEPTION', 'SQLSTATE', 'SQLWARNING',
95 | 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT',
96 | 'SSL', 'STARTING', 'STRAIGHT_JOIN',
97 | 'TABLE', 'TERMINATED', 'THEN',
98 | 'TINYBLOB', 'TINYINT', 'TINYTEXT',
99 | 'TO', 'TRAILING', 'TRIGGER',
100 | 'TRUE', 'UNDO', 'UNION',
101 | 'UNIQUE', 'UNLOCK', 'UNSIGNED',
102 | 'UPDATE', 'USAGE', 'USE',
103 | 'USING', 'UTC_DATE', 'UTC_TIME',
104 | 'UTC_TIMESTAMP', 'VALUES', 'VARBINARY',
105 | 'VARCHAR', 'VARCHARACTER', 'VARYING',
106 | 'WHEN', 'WHERE', 'WHILE',
107 | 'WITH', 'WRITE', 'X509',
108 | 'XOR', 'YEAR_MONTH', 'ZEROFILL'
109 | );
110 |
111 | /**
112 | * Obtain DBMS specific SQL code portion needed to declare an text type
113 | * field to be used in statements like CREATE TABLE.
114 | *
115 | * @param array $field associative array with the name of the properties
116 | * of the field being declared as array indexes. Currently, the types
117 | * of supported field properties are as follows:
118 | *
119 | * length
120 | * Integer value that determines the maximum length of the text
121 | * field. If this argument is missing the field should be
122 | * declared to have the longest length allowed by the DBMS.
123 | *
124 | * default
125 | * Text value to be used as default for this field.
126 | *
127 | * notnull
128 | * Boolean flag that indicates whether this field is constrained
129 | * to not be set to null.
130 | *
131 | * @return string DBMS specific SQL code portion that should be used to
132 | * declare the specified field.
133 | */
134 | public function getNativeDeclaration($field)
135 | {
136 | if ( ! isset($field['type'])) {
137 | throw new Doctrine_DataDict_Exception('Missing column type.');
138 | }
139 |
140 | switch ($field['type']) {
141 | case 'char':
142 | $length = ( ! empty($field['length'])) ? $field['length'] : false;
143 |
144 | return $length ? 'CHAR('.$length.')' : 'CHAR(255)';
145 | case 'varchar':
146 | case 'array':
147 | case 'object':
148 | case 'string':
149 | case 'gzip':
150 | if ( ! isset($field['length'])) {
151 | if (array_key_exists('default', $field)) {
152 | $field['length'] = $this->conn->varchar_max_length;
153 | } else {
154 | $field['length'] = false;
155 | }
156 | }
157 |
158 | $length = ($field['length'] <= $this->conn->varchar_max_length) ? $field['length'] : false;
159 | $fixed = (isset($field['fixed'])) ? $field['fixed'] : false;
160 |
161 | return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
162 | : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
163 | case 'clob':
164 | if ( ! empty($field['length'])) {
165 | $length = $field['length'];
166 | if ($length <= 255) {
167 | return 'TINYTEXT';
168 | } elseif ($length <= 65532) {
169 | return 'TEXT';
170 | } elseif ($length <= 16777215) {
171 | return 'MEDIUMTEXT';
172 | }
173 | }
174 | return 'LONGTEXT';
175 | case 'blob':
176 | if ( ! empty($field['length'])) {
177 | $length = $field['length'];
178 | if ($length <= 255) {
179 | return 'TINYBLOB';
180 | } elseif ($length <= 65532) {
181 | return 'BLOB';
182 | } elseif ($length <= 16777215) {
183 | return 'MEDIUMBLOB';
184 | }
185 | }
186 | return 'LONGBLOB';
187 | case 'enum':
188 | if ($this->conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) {
189 | $values = array();
190 | foreach ($field['values'] as $value) {
191 | $values[] = $this->conn->quote($value, 'varchar');
192 | }
193 | return 'ENUM('.implode(', ', $values).')';
194 | }
195 | // fall back to integer
196 | case 'integer':
197 | case 'int':
198 | if ( ! empty($field['length'])) {
199 | $length = $field['length'];
200 | if ($length <= 1) {
201 | return 'TINYINT';
202 | } elseif ($length == 2) {
203 | return 'SMALLINT';
204 | } elseif ($length == 3) {
205 | return 'MEDIUMINT';
206 | } elseif ($length == 4) {
207 | return 'INT';
208 | } elseif ($length > 4) {
209 | return 'BIGINT';
210 | }
211 | }
212 | return 'INT';
213 | case 'boolean':
214 | return 'TINYINT(1)';
215 | case 'date':
216 | return 'DATE';
217 | case 'time':
218 | return 'TIME';
219 | case 'timestamp':
220 | return 'DATETIME';
221 | case 'float':
222 | case 'double':
223 | return 'DOUBLE';
224 | case 'decimal':
225 | $length = !empty($field['length']) ? $field['length'] : 18;
226 | $scale = !empty($field['scale']) ? $field['scale'] : $this->conn->getAttribute(Doctrine::ATTR_DECIMAL_PLACES);
227 | return 'DECIMAL('.$length.','.$scale.')';
228 | }
229 | throw new Doctrine_DataDict_Exception('Unknown field type \'' . $field['type'] . '\'.');
230 | }
231 |
232 | /**
233 | * Maps a native array description of a field to a MDB2 datatype and length
234 | *
235 | * @param array $field native field description
236 | * @return array containing the various possible types, length, sign, fixed
237 | */
238 | public function getPortableDeclaration(array $field)
239 | {
240 | $dbType = strtolower($field['type']);
241 | $dbType = strtok($dbType, '(), ');
242 | if ($dbType == 'national') {
243 | $dbType = strtok('(), ');
244 | }
245 | if (isset($field['length'])) {
246 | $length = $field['length'];
247 | $decimal = '';
248 | } else {
249 | $length = strtok('(), ');
250 | $decimal = strtok('(), ');
251 | }
252 | $type = array();
253 | $unsigned = $fixed = null;
254 |
255 | if ( ! isset($field['name'])) {
256 | $field['name'] = '';
257 | }
258 |
259 | $values = null;
260 |
261 | switch ($dbType) {
262 | case 'tinyint':
263 | $type[] = 'integer';
264 | $type[] = 'boolean';
265 | if (preg_match('/^(is|has)/', $field['name'])) {
266 | $type = array_reverse($type);
267 | }
268 | $unsigned = preg_match('/ unsigned/i', $field['type']);
269 | $length = 1;
270 | break;
271 | case 'smallint':
272 | $type[] = 'integer';
273 | $unsigned = preg_match('/ unsigned/i', $field['type']);
274 | $length = 2;
275 | break;
276 | case 'mediumint':
277 | $type[] = 'integer';
278 | $unsigned = preg_match('/ unsigned/i', $field['type']);
279 | $length = 3;
280 | break;
281 | case 'int':
282 | case 'integer':
283 | $type[] = 'integer';
284 | $unsigned = preg_match('/ unsigned/i', $field['type']);
285 | $length = 4;
286 | break;
287 | case 'bigint':
288 | $type[] = 'integer';
289 | $unsigned = preg_match('/ unsigned/i', $field['type']);
290 | $length = 8;
291 | break;
292 | case 'tinytext':
293 | case 'mediumtext':
294 | case 'longtext':
295 | case 'text':
296 | case 'text':
297 | case 'varchar':
298 | $fixed = false;
299 | case 'string':
300 | case 'char':
301 | $type[] = 'string';
302 | if ($length == '1') {
303 | $type[] = 'boolean';
304 | if (preg_match('/^(is|has)/', $field['name'])) {
305 | $type = array_reverse($type);
306 | }
307 | } elseif (strstr($dbType, 'text')) {
308 | $type[] = 'clob';
309 | if ($decimal == 'binary') {
310 | $type[] = 'blob';
311 | }
312 | }
313 | if ($fixed !== false) {
314 | $fixed = true;
315 | }
316 | break;
317 | case 'enum':
318 | $type[] = 'enum';
319 | preg_match_all('/\'.+\'/U', $field['type'], $matches);
320 | $length = 0;
321 | $fixed = false;
322 | if (is_array($matches)) {
323 | foreach ($matches[0] as $value) {
324 | $length = max($length, strlen($value)-2);
325 | }
326 | if ($length == '1' && count($matches[0]) == 2) {
327 | $type[] = 'boolean';
328 | if (preg_match('/^(is|has)/', $field['name'])) {
329 | $type = array_reverse($type);
330 | }
331 | } else {
332 | $values = $matches[0];
333 | }
334 | }
335 | $type[] = 'integer';
336 | case 'set':
337 | $fixed = false;
338 | $type[] = 'text';
339 | $type[] = 'integer';
340 | break;
341 | case 'date':
342 | $type[] = 'date';
343 | $length = null;
344 | break;
345 | case 'datetime':
346 | case 'timestamp':
347 | $type[] = 'timestamp';
348 | $length = null;
349 | break;
350 | case 'time':
351 | $type[] = 'time';
352 | $length = null;
353 | break;
354 | case 'float':
355 | case 'double':
356 | case 'real':
357 | $type[] = 'float';
358 | $unsigned = preg_match('/ unsigned/i', $field['type']);
359 | break;
360 | case 'unknown':
361 | case 'decimal':
362 | case 'numeric':
363 | $type[] = 'decimal';
364 | $unsigned = preg_match('/ unsigned/i', $field['type']);
365 | break;
366 | case 'tinyblob':
367 | case 'mediumblob':
368 | case 'longblob':
369 | case 'blob':
370 | $type[] = 'blob';
371 | $length = null;
372 | break;
373 | case 'year':
374 | $type[] = 'integer';
375 | $type[] = 'date';
376 | $length = null;
377 | break;
378 | default:
379 | throw new Doctrine_DataDict_Exception('unknown database attribute type: ' . $dbType);
380 | }
381 |
382 | $length = ((int) $length == 0) ? null : (int) $length;
383 |
384 | if ($values === null) {
385 | return array('type' => $type, 'length' => $length, 'unsigned' => $unsigned, 'fixed' => $fixed);
386 | } else {
387 | return array('type' => $type, 'length' => $length, 'unsigned' => $unsigned, 'fixed' => $fixed, 'values' => $values);
388 | }
389 | }
390 |
391 | /**
392 | * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
393 | * of a field declaration to be used in statements like CREATE TABLE.
394 | *
395 | * @param string $charset name of the charset
396 | * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
397 | * of a field declaration.
398 | */
399 | public function getCharsetFieldDeclaration($charset)
400 | {
401 | return 'CHARACTER SET ' . $charset;
402 | }
403 |
404 | /**
405 | * Obtain DBMS specific SQL code portion needed to set the COLLATION
406 | * of a field declaration to be used in statements like CREATE TABLE.
407 | *
408 | * @param string $collation name of the collation
409 | * @return string DBMS specific SQL code portion needed to set the COLLATION
410 | * of a field declaration.
411 | */
412 | public function getCollationFieldDeclaration($collation)
413 | {
414 | return 'COLLATE ' . $collation;
415 | }
416 |
417 | /**
418 | * Obtain DBMS specific SQL code portion needed to declare an integer type
419 | * field to be used in statements like CREATE TABLE.
420 | *
421 | * @param string $name name the field to be declared.
422 | * @param string $field associative array with the name of the properties
423 | * of the field being declared as array indexes.
424 | * Currently, the types of supported field
425 | * properties are as follows:
426 | *
427 | * unsigned
428 | * Boolean flag that indicates whether the field
429 | * should be declared as unsigned integer if
430 | * possible.
431 | *
432 | * default
433 | * Integer value to be used as default for this
434 | * field.
435 | *
436 | * notnull
437 | * Boolean flag that indicates whether this field is
438 | * constrained to not be set to null.
439 | * @return string DBMS specific SQL code portion that should be used to
440 | * declare the specified field.
441 | */
442 | public function getIntegerDeclaration($name, $field)
443 | {
444 | $default = $autoinc = '';
445 | if ( ! empty($field['autoincrement'])) {
446 | $autoinc = ' AUTO_INCREMENT';
447 | } elseif (array_key_exists('default', $field)) {
448 | if ($field['default'] === '') {
449 | $field['default'] = empty($field['notnull']) ? null : 0;
450 | }
451 | if (is_null($field['default'])) {
452 | $default = ' DEFAULT NULL';
453 | } else {
454 | $default = ' DEFAULT '.$this->conn->quote($field['default']);
455 | }
456 | }
457 | /**
458 | elseif (empty($field['notnull'])) {
459 | $default = ' DEFAULT NULL';
460 | }
461 | */
462 |
463 | $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
464 | $unsigned = (isset($field['unsigned']) && $field['unsigned']) ? ' UNSIGNED' : '';
465 |
466 | $name = $this->conn->quoteIdentifier($name, true);
467 |
468 | return $name . ' ' . $this->getNativeDeclaration($field) . $unsigned . $default . $notnull . $autoinc;
469 | }
470 | }