Initial repo created
[timetracker.git] / WEB-INF / lib / pear / MDB2 / Driver / Datatype / mysql.php
1 <?php
2 // vim: set et ts=4 sw=4 fdm=marker:
3 // +----------------------------------------------------------------------+
4 // | PHP versions 4 and 5                                                 |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
7 // | Stig. S. Bakken, Lukas Smith                                         |
8 // | All rights reserved.                                                 |
9 // +----------------------------------------------------------------------+
10 // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
11 // | API as well as database abstraction for PHP applications.            |
12 // | This LICENSE is in the BSD license style.                            |
13 // |                                                                      |
14 // | Redistribution and use in source and binary forms, with or without   |
15 // | modification, are permitted provided that the following conditions   |
16 // | are met:                                                             |
17 // |                                                                      |
18 // | Redistributions of source code must retain the above copyright       |
19 // | notice, this list of conditions and the following disclaimer.        |
20 // |                                                                      |
21 // | Redistributions in binary form must reproduce the above copyright    |
22 // | notice, this list of conditions and the following disclaimer in the  |
23 // | documentation and/or other materials provided with the distribution. |
24 // |                                                                      |
25 // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
26 // | Lukas Smith nor the names of his contributors may be used to endorse |
27 // | or promote products derived from this software without specific prior|
28 // | written permission.                                                  |
29 // |                                                                      |
30 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
31 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
32 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
33 // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
34 // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
35 // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
36 // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
37 // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
38 // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
39 // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
40 // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
41 // | POSSIBILITY OF SUCH DAMAGE.                                          |
42 // +----------------------------------------------------------------------+
43 // | Author: Lukas Smith <smith@pooteeweet.org>                           |
44 // +----------------------------------------------------------------------+
45 //
46 // $Id: mysql.php 327310 2012-08-27 15:16:18Z danielc $
47 //
48
49 require_once 'MDB2/Driver/Datatype/Common.php';
50
51 /**
52  * MDB2 MySQL driver
53  *
54  * @package MDB2
55  * @category Database
56  * @author  Lukas Smith <smith@pooteeweet.org>
57  */
58 class MDB2_Driver_Datatype_mysql extends MDB2_Driver_Datatype_Common
59 {
60     // {{{ _getCharsetFieldDeclaration()
61
62     /**
63      * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
64      * of a field declaration to be used in statements like CREATE TABLE.
65      *
66      * @param string $charset   name of the charset
67      * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
68      *                 of a field declaration.
69      */
70     function _getCharsetFieldDeclaration($charset)
71     {
72         return 'CHARACTER SET '.$charset;
73     }
74
75     // }}}
76     // {{{ _getCollationFieldDeclaration()
77
78     /**
79      * Obtain DBMS specific SQL code portion needed to set the COLLATION
80      * of a field declaration to be used in statements like CREATE TABLE.
81      *
82      * @param string $collation   name of the collation
83      * @return string  DBMS specific SQL code portion needed to set the COLLATION
84      *                 of a field declaration.
85      */
86     function _getCollationFieldDeclaration($collation)
87     {
88         return 'COLLATE '.$collation;
89     }
90
91     // }}}
92     // {{{ getDeclaration()
93
94     /**
95      * Obtain DBMS specific SQL code portion needed to declare
96      * of the given type
97      *
98      * @param string $type  type to which the value should be converted to
99      * @param string $name  name the field to be declared.
100      * @param string $field definition of the field
101      *
102      * @return string DBMS-specific SQL code portion that should be used to
103      *                declare the specified field.
104      * @access public
105      */
106     function getDeclaration($type, $name, $field)
107     {
108         // MySQL DDL syntax forbids combining NOT NULL with DEFAULT NULL.
109         // To get a default of NULL for NOT NULL columns, omit it.
110         if (   isset($field['notnull'])
111             && !empty($field['notnull'])
112             && array_key_exists('default', $field) // do not use isset() here!
113             && null === $field['default']
114         ) {
115             unset($field['default']);
116         }
117         return parent::getDeclaration($type, $name, $field);
118     }
119
120     // }}}
121     // {{{ getTypeDeclaration()
122
123     /**
124      * Obtain DBMS specific SQL code portion needed to declare an text type
125      * field to be used in statements like CREATE TABLE.
126      *
127      * @param array $field  associative array with the name of the properties
128      *      of the field being declared as array indexes. Currently, the types
129      *      of supported field properties are as follows:
130      *
131      *      length
132      *          Integer value that determines the maximum length of the text
133      *          field. If this argument is missing the field should be
134      *          declared to have the longest length allowed by the DBMS.
135      *
136      *      default
137      *          Text value to be used as default for this field.
138      *
139      *      notnull
140      *          Boolean flag that indicates whether this field is constrained
141      *          to not be set to null.
142      * @return string  DBMS specific SQL code portion that should be used to
143      *      declare the specified field.
144      * @access public
145      */
146     function getTypeDeclaration($field)
147     {
148         $db = $this->getDBInstance();
149         if (MDB2::isError($db)) {
150             return $db;
151         }
152
153         switch ($field['type']) {
154         case 'text':
155             if (empty($field['length']) && array_key_exists('default', $field)) {
156                 $field['length'] = $db->varchar_max_length;
157             }
158             $length = !empty($field['length']) ? $field['length'] : false;
159             $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
160             return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR(255)')
161                 : ($length ? 'VARCHAR('.$length.')' : 'TEXT');
162         case 'clob':
163             if (!empty($field['length'])) {
164                 $length = $field['length'];
165                 if ($length <= 255) {
166                     return 'TINYTEXT';
167                 } elseif ($length <= 65532) {
168                     return 'TEXT';
169                 } elseif ($length <= 16777215) {
170                     return 'MEDIUMTEXT';
171                 }
172             }
173             return 'LONGTEXT';
174         case 'blob':
175             if (!empty($field['length'])) {
176                 $length = $field['length'];
177                 if ($length <= 255) {
178                     return 'TINYBLOB';
179                 } elseif ($length <= 65532) {
180                     return 'BLOB';
181                 } elseif ($length <= 16777215) {
182                     return 'MEDIUMBLOB';
183                 }
184             }
185             return 'LONGBLOB';
186         case 'integer':
187             if (!empty($field['length'])) {
188                 $length = $field['length'];
189                 if ($length <= 1) {
190                     return 'TINYINT';
191                 } elseif ($length == 2) {
192                     return 'SMALLINT';
193                 } elseif ($length == 3) {
194                     return 'MEDIUMINT';
195                 } elseif ($length == 4) {
196                     return 'INT';
197                 } elseif ($length > 4) {
198                     return 'BIGINT';
199                 }
200             }
201             return 'INT';
202         case 'boolean':
203             return 'TINYINT(1)';
204         case 'date':
205             return 'DATE';
206         case 'time':
207             return 'TIME';
208         case 'timestamp':
209             return 'DATETIME';
210         case 'float':
211             $l = '';
212             if (!empty($field['length'])) {
213                 $l = '(' . $field['length'];
214                 if (!empty($field['scale'])) {
215                     $l .= ',' . $field['scale'];
216                 }
217                 $l .= ')';
218             }
219             return 'DOUBLE' . $l;
220         case 'decimal':
221             $length = !empty($field['length']) ? $field['length'] : 18;
222             $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
223             return 'DECIMAL('.$length.','.$scale.')';
224         }
225         return '';
226     }
227
228     // }}}
229     // {{{ _getIntegerDeclaration()
230
231     /**
232      * Obtain DBMS specific SQL code portion needed to declare an integer type
233      * field to be used in statements like CREATE TABLE.
234      *
235      * @param string  $name   name the field to be declared.
236      * @param string  $field  associative array with the name of the properties
237      *                        of the field being declared as array indexes.
238      *                        Currently, the types of supported field
239      *                        properties are as follows:
240      *
241      *                       unsigned
242      *                        Boolean flag that indicates whether the field
243      *                        should be declared as unsigned integer if
244      *                        possible.
245      *
246      *                       default
247      *                        Integer value to be used as default for this
248      *                        field.
249      *
250      *                       notnull
251      *                        Boolean flag that indicates whether this field is
252      *                        constrained to not be set to null.
253      * @return string  DBMS specific SQL code portion that should be used to
254      *                 declare the specified field.
255      * @access protected
256      */
257     function _getIntegerDeclaration($name, $field)
258     {
259         $db = $this->getDBInstance();
260         if (MDB2::isError($db)) {
261             return $db;
262         }
263
264         $default = $autoinc = '';
265         if (!empty($field['autoincrement'])) {
266             $autoinc = ' AUTO_INCREMENT PRIMARY KEY';
267         } elseif (array_key_exists('default', $field)) {
268             if ($field['default'] === '') {
269                 $field['default'] = empty($field['notnull']) ? null : 0;
270             }
271             $default = ' DEFAULT '.$this->quote($field['default'], 'integer');
272         }
273
274         $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
275         $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
276         if (empty($default) && empty($notnull)) {
277             $default = ' DEFAULT NULL';
278         }
279         $name = $db->quoteIdentifier($name, true);
280         return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
281     }
282
283     // }}}
284     // {{{ _getFloatDeclaration()
285
286     /**
287      * Obtain DBMS specific SQL code portion needed to declare an float type
288      * field to be used in statements like CREATE TABLE.
289      *
290      * @param string  $name   name the field to be declared.
291      * @param string  $field  associative array with the name of the properties
292      *                        of the field being declared as array indexes.
293      *                        Currently, the types of supported field
294      *                        properties are as follows:
295      *
296      *                       unsigned
297      *                        Boolean flag that indicates whether the field
298      *                        should be declared as unsigned float if
299      *                        possible.
300      *
301      *                       default
302      *                        float value to be used as default for this
303      *                        field.
304      *
305      *                       notnull
306      *                        Boolean flag that indicates whether this field is
307      *                        constrained to not be set to null.
308      * @return string  DBMS specific SQL code portion that should be used to
309      *                 declare the specified field.
310      * @access protected
311      */
312     function _getFloatDeclaration($name, $field)
313     {
314         // Since AUTO_INCREMENT can be used for integer or floating-point types,
315         // reuse the INTEGER declaration
316         // @see http://bugs.mysql.com/bug.php?id=31032
317         return $this->_getIntegerDeclaration($name, $field);
318     }
319
320     // }}}
321     // {{{ _getDecimalDeclaration()
322
323     /**
324      * Obtain DBMS specific SQL code portion needed to declare an decimal type
325      * field to be used in statements like CREATE TABLE.
326      *
327      * @param string  $name   name the field to be declared.
328      * @param string  $field  associative array with the name of the properties
329      *                        of the field being declared as array indexes.
330      *                        Currently, the types of supported field
331      *                        properties are as follows:
332      *
333      *                       unsigned
334      *                        Boolean flag that indicates whether the field
335      *                        should be declared as unsigned integer if
336      *                        possible.
337      *
338      *                       default
339      *                        Decimal value to be used as default for this
340      *                        field.
341      *
342      *                       notnull
343      *                        Boolean flag that indicates whether this field is
344      *                        constrained to not be set to null.
345      * @return string  DBMS specific SQL code portion that should be used to
346      *                 declare the specified field.
347      * @access protected
348      */
349     function _getDecimalDeclaration($name, $field)
350     {
351         $db = $this->getDBInstance();
352         if (MDB2::isError($db)) {
353             return $db;
354         }
355
356         $default = '';
357         if (array_key_exists('default', $field)) {
358             if ($field['default'] === '') {
359                 $field['default'] = empty($field['notnull']) ? null : 0;
360             }
361             $default = ' DEFAULT '.$this->quote($field['default'], 'integer');
362         } elseif (empty($field['notnull'])) {
363             $default = ' DEFAULT NULL';
364         }
365
366         $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
367         $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
368         $name = $db->quoteIdentifier($name, true);
369         return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull;
370     }
371
372     // }}}
373     // {{{ matchPattern()
374
375     /**
376      * build a pattern matching string
377      *
378      * @access public
379      *
380      * @param array $pattern even keys are strings, odd are patterns (% and _)
381      * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
382      * @param string $field optional field name that is being matched against
383      *                  (might be required when emulating ILIKE)
384      *
385      * @return string SQL pattern
386      */
387     function matchPattern($pattern, $operator = null, $field = null)
388     {
389         $db = $this->getDBInstance();
390         if (MDB2::isError($db)) {
391             return $db;
392         }
393
394         $match = '';
395         if (null !== $operator) {
396             $field = (null === $field) ? '' : $field.' ';
397             $operator = strtoupper($operator);
398             switch ($operator) {
399             // case insensitive
400             case 'ILIKE':
401                 $match = $field.'LIKE ';
402                 break;
403             case 'NOT ILIKE':
404                 $match = $field.'NOT LIKE ';
405                 break;
406             // case sensitive
407             case 'LIKE':
408                 $match = $field.'LIKE BINARY ';
409                 break;
410             case 'NOT LIKE':
411                 $match = $field.'NOT LIKE BINARY ';
412                 break;
413             default:
414                 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
415                     'not a supported operator type:'. $operator, __FUNCTION__);
416             }
417         }
418         $match.= "'";
419         foreach ($pattern as $key => $value) {
420             if ($key % 2) {
421                 $match.= $value;
422             } else {
423                 $match.= $db->escapePattern($db->escape($value));
424             }
425         }
426         $match.= "'";
427         $match.= $this->patternEscapeString();
428         return $match;
429     }
430
431     // }}}
432     // {{{ _mapNativeDatatype()
433
434     /**
435      * Maps a native array description of a field to a MDB2 datatype and length
436      *
437      * @param array  $field native field description
438      * @return array containing the various possible types, length, sign, fixed
439      * @access public
440      */
441     function _mapNativeDatatype($field)
442     {
443         $db_type = strtolower($field['type']);
444         $db_type = strtok($db_type, '(), ');
445         if ($db_type == 'national') {
446             $db_type = strtok('(), ');
447         }
448         if (!empty($field['length'])) {
449             $length = strtok($field['length'], ', ');
450             $decimal = strtok(', ');
451         } else {
452             $length = strtok('(), ');
453             $decimal = strtok('(), ');
454         }
455         $type = array();
456         $unsigned = $fixed = null;
457         switch ($db_type) {
458         case 'tinyint':
459             $type[] = 'integer';
460             $type[] = 'boolean';
461             if (preg_match('/^(is|has)/', $field['name'])) {
462                 $type = array_reverse($type);
463             }
464             $unsigned = preg_match('/ unsigned/i', $field['type']);
465             $length = 1;
466             break;
467         case 'smallint':
468             $type[] = 'integer';
469             $unsigned = preg_match('/ unsigned/i', $field['type']);
470             $length = 2;
471             break;
472         case 'mediumint':
473             $type[] = 'integer';
474             $unsigned = preg_match('/ unsigned/i', $field['type']);
475             $length = 3;
476             break;
477         case 'int':
478         case 'integer':
479             $type[] = 'integer';
480             $unsigned = preg_match('/ unsigned/i', $field['type']);
481             $length = 4;
482             break;
483         case 'bigint':
484             $type[] = 'integer';
485             $unsigned = preg_match('/ unsigned/i', $field['type']);
486             $length = 8;
487             break;
488         case 'tinytext':
489         case 'mediumtext':
490         case 'longtext':
491         case 'text':
492         case 'varchar':
493             $fixed = false;
494         case 'string':
495         case 'char':
496             $type[] = 'text';
497             if ($length == '1') {
498                 $type[] = 'boolean';
499                 if (preg_match('/^(is|has)/', $field['name'])) {
500                     $type = array_reverse($type);
501                 }
502             } elseif (strstr($db_type, 'text')) {
503                 $type[] = 'clob';
504                 if ($decimal == 'binary') {
505                     $type[] = 'blob';
506                 }
507                 $type = array_reverse($type);
508             }
509             if ($fixed !== false) {
510                 $fixed = true;
511             }
512             break;
513         case 'enum':
514             $type[] = 'text';
515             preg_match_all('/\'.+\'/U', $field['type'], $matches);
516             $length = 0;
517             $fixed = false;
518             if (is_array($matches)) {
519                 foreach ($matches[0] as $value) {
520                     $length = max($length, strlen($value)-2);
521                 }
522                 if ($length == '1' && count($matches[0]) == 2) {
523                     $type[] = 'boolean';
524                     if (preg_match('/^(is|has)/', $field['name'])) {
525                         $type = array_reverse($type);
526                     }
527                 }
528             }
529             $type[] = 'integer';
530         case 'set':
531             $fixed = false;
532             $type[] = 'text';
533             $type[] = 'integer';
534             break;
535         case 'date':
536             $type[] = 'date';
537             $length = null;
538             break;
539         case 'datetime':
540         case 'timestamp':
541             $type[] = 'timestamp';
542             $length = null;
543             break;
544         case 'time':
545             $type[] = 'time';
546             $length = null;
547             break;
548         case 'float':
549         case 'double':
550         case 'real':
551             $type[] = 'float';
552             $unsigned = preg_match('/ unsigned/i', $field['type']);
553             if ($decimal !== false) {
554                 $length = $length.','.$decimal;
555             }
556             break;
557         case 'unknown':
558         case 'decimal':
559         case 'numeric':
560             $type[] = 'decimal';
561             $unsigned = preg_match('/ unsigned/i', $field['type']);
562             if ($decimal !== false) {
563                 $length = $length.','.$decimal;
564             }
565             break;
566         case 'tinyblob':
567         case 'mediumblob':
568         case 'longblob':
569         case 'blob':
570             $type[] = 'blob';
571             $length = null;
572             break;
573         case 'binary':
574         case 'varbinary':
575             $type[] = 'blob';
576             break;
577         case 'year':
578             $type[] = 'integer';
579             $type[] = 'date';
580             $length = null;
581             break;
582         default:
583             $db = $this->getDBInstance();
584             if (MDB2::isError($db)) {
585                 return $db;
586             }
587
588             return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
589                 'unknown database attribute type: '.$db_type, __FUNCTION__);
590         }
591
592         if ((int)$length <= 0) {
593             $length = null;
594         }
595
596         return array($type, $length, $unsigned, $fixed);
597     }
598
599     // }}}
600 }
601
602 ?>