2 // +----------------------------------------------------------------------+
3 // | PHP versions 4 and 5 |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, |
6 // | Stig. S. Bakken, Lukas Smith |
7 // | All rights reserved. |
8 // +----------------------------------------------------------------------+
9 // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
10 // | API as well as database abstraction for PHP applications. |
11 // | This LICENSE is in the BSD license style. |
13 // | Redistribution and use in source and binary forms, with or without |
14 // | modification, are permitted provided that the following conditions |
17 // | Redistributions of source code must retain the above copyright |
18 // | notice, this list of conditions and the following disclaimer. |
20 // | Redistributions in binary form must reproduce the above copyright |
21 // | notice, this list of conditions and the following disclaimer in the |
22 // | documentation and/or other materials provided with the distribution. |
24 // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
25 // | Lukas Smith nor the names of his contributors may be used to endorse |
26 // | or promote products derived from this software without specific prior|
27 // | written permission. |
29 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
32 // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
33 // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
34 // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
35 // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
36 // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
37 // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
38 // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
39 // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
40 // | POSSIBILITY OF SUCH DAMAGE. |
41 // +----------------------------------------------------------------------+
42 // | Author: Lukas Smith <smith@pooteeweet.org> |
43 // +----------------------------------------------------------------------+
45 // $Id: Common.php 328137 2012-10-25 02:26:35Z danielc $
47 require_once 'MDB2/LOB.php';
52 * @author Lukas Smith <smith@pooteeweet.org>
56 * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
58 * To load this module in the MDB2 object:
59 * $mdb->loadModule('Datatype');
63 * @author Lukas Smith <smith@pooteeweet.org>
65 class MDB2_Driver_Datatype_Common extends MDB2_Module_Common
67 var $valid_default_values = array(
73 'timestamp' => '1970-01-01 00:00:00',
75 'date' => '1970-01-01',
81 * contains all LOB objects created with this MDB2 instance
88 // {{{ getValidTypes()
91 * Get the list of valid types
93 * This function returns an array of valid types as keys with the values
94 * being possible default values for all native datatypes and mapped types
95 * for custom datatypes.
97 * @return mixed array on success, a MDB2 error on failure
100 function getValidTypes()
102 $types = $this->valid_default_values;
103 $db = $this->getDBInstance();
104 if (MDB2::isError($db)) {
107 if (!empty($db->options['datatype_map'])) {
108 foreach ($db->options['datatype_map'] as $type => $mapped_type) {
109 if (array_key_exists($mapped_type, $types)) {
110 $types[$type] = $types[$mapped_type];
111 } elseif (!empty($db->options['datatype_map_callback'][$type])) {
112 $parameter = array('type' => $type, 'mapped_type' => $mapped_type);
113 $default = call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
114 $types[$type] = $default;
122 // {{{ checkResultTypes()
125 * Define the list of types to be associated with the columns of a given
128 * This function may be called before invoking fetchRow(), fetchOne()
129 * fetchCole() and fetchAll() so that the necessary data type
130 * conversions are performed on the data to be retrieved by them. If this
131 * function is not called, the type of all result set columns is assumed
132 * to be text, thus leading to not perform any conversions.
134 * @param array $types array variable that lists the
135 * data types to be expected in the result set columns. If this array
136 * contains less types than the number of columns that are returned
137 * in the result set, the remaining columns are assumed to be of the
138 * type text. Currently, the types clob and blob are not fully
140 * @return mixed MDB2_OK on success, a MDB2 error on failure
143 function checkResultTypes($types)
145 $types = is_array($types) ? $types : array($types);
146 foreach ($types as $key => $type) {
147 if (!isset($this->valid_default_values[$type])) {
148 $db = $this->getDBInstance();
149 if (MDB2::isError($db)) {
152 if (empty($db->options['datatype_map'][$type])) {
153 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
154 $type.' for '.$key.' is not a supported column type', __FUNCTION__);
162 // {{{ _baseConvertResult()
165 * General type conversion method
167 * @param mixed $value reference to a value to be converted
168 * @param string $type specifies which type to convert to
169 * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
170 * @return object an MDB2 error on failure
173 function _baseConvertResult($value, $type, $rtrim = true)
178 $value = rtrim($value);
182 return intval($value);
184 return !empty($value);
188 return doubleval($value);
197 $this->lobs[] = array(
202 'resource' => $value,
207 $lob_index = key($this->lobs);
208 $this->lobs[$lob_index]['lob_index'] = $lob_index;
209 return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+');
212 $db = $this->getDBInstance();
213 if (MDB2::isError($db)) {
217 return $db->raiseError(MDB2_ERROR_INVALID, null, null,
218 'attempt to convert result value to an unknown type :' . $type, __FUNCTION__);
222 // {{{ convertResult()
225 * Convert a value to a RDBMS indipendent MDB2 type
227 * @param mixed $value value to be converted
228 * @param string $type specifies which type to convert to
229 * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
230 * @return mixed converted value
233 function convertResult($value, $type, $rtrim = true)
235 if (null === $value) {
238 $db = $this->getDBInstance();
239 if (MDB2::isError($db)) {
242 if (!empty($db->options['datatype_map'][$type])) {
243 $type = $db->options['datatype_map'][$type];
244 if (!empty($db->options['datatype_map_callback'][$type])) {
245 $parameter = array('type' => $type, 'value' => $value, 'rtrim' => $rtrim);
246 return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
249 return $this->_baseConvertResult($value, $type, $rtrim);
253 // {{{ convertResultRow()
256 * Convert a result row
258 * @param array $types
259 * @param array $row specifies the types to convert to
260 * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
261 * @return mixed MDB2_OK on success, an MDB2 error on failure
264 function convertResultRow($types, $row, $rtrim = true)
266 //$types = $this->_sortResultFieldTypes(array_keys($row), $types);
267 $keys = array_keys($row);
268 if (is_int($keys[0])) {
269 $types = $this->_sortResultFieldTypes($keys, $types);
271 foreach ($row as $key => $value) {
272 if (empty($types[$key])) {
275 $value = $this->convertResult($row[$key], $types[$key], $rtrim);
276 if (MDB2::isError($value)) {
285 // {{{ _sortResultFieldTypes()
288 * convert a result row
290 * @param array $types
291 * @param array $row specifies the types to convert to
292 * @param bool $rtrim if to rtrim text values or not
293 * @return mixed MDB2_OK on success, a MDB2 error on failure
296 function _sortResultFieldTypes($columns, $types)
298 $n_cols = count($columns);
299 $n_types = count($types);
300 if ($n_cols > $n_types) {
301 for ($i= $n_cols - $n_types; $i >= 0; $i--) {
305 $sorted_types = array();
306 foreach ($columns as $col) {
307 $sorted_types[$col] = null;
309 foreach ($types as $name => $type) {
310 if (array_key_exists($name, $sorted_types)) {
311 $sorted_types[$name] = $type;
312 unset($types[$name]);
315 // if there are left types in the array, fill the null values of the
316 // sorted array with them, in order.
319 foreach (array_keys($sorted_types) as $k) {
320 if (null === $sorted_types[$k]) {
321 $sorted_types[$k] = current($types);
326 return $sorted_types;
330 // {{{ getDeclaration()
333 * Obtain DBMS specific SQL code portion needed to declare
336 * @param string $type type to which the value should be converted to
337 * @param string $name name the field to be declared.
338 * @param string $field definition of the field
339 * @return string DBMS specific SQL code portion that should be used to
340 * declare the specified field.
343 function getDeclaration($type, $name, $field)
345 $db = $this->getDBInstance();
346 if (MDB2::isError($db)) {
350 if (!empty($db->options['datatype_map'][$type])) {
351 $type = $db->options['datatype_map'][$type];
352 if (!empty($db->options['datatype_map_callback'][$type])) {
353 $parameter = array('type' => $type, 'name' => $name, 'field' => $field);
354 return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
356 $field['type'] = $type;
359 if (!method_exists($this, "_get{$type}Declaration")) {
360 return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
361 'type not defined: '.$type, __FUNCTION__);
363 return $this->{"_get{$type}Declaration"}($name, $field);
367 // {{{ getTypeDeclaration()
370 * Obtain DBMS specific SQL code portion needed to declare an text type
371 * field to be used in statements like CREATE TABLE.
373 * @param array $field associative array with the name of the properties
374 * of the field being declared as array indexes. Currently, the types
375 * of supported field properties are as follows:
378 * Integer value that determines the maximum length of the text
379 * field. If this argument is missing the field should be
380 * declared to have the longest length allowed by the DBMS.
383 * Text value to be used as default for this field.
386 * Boolean flag that indicates whether this field is constrained
387 * to not be set to null.
388 * @return string DBMS specific SQL code portion that should be used to
389 * declare the specified field.
392 function getTypeDeclaration($field)
394 $db = $this->getDBInstance();
395 if (MDB2::isError($db)) {
399 switch ($field['type']) {
401 $length = !empty($field['length']) ? $field['length'] : $db->options['default_text_field_length'];
402 $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
403 return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
404 : ($length ? 'VARCHAR('.$length.')' : 'TEXT');
414 return 'CHAR ('.strlen('YYYY-MM-DD').')';
416 return 'CHAR ('.strlen('HH:MM:SS').')';
418 return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
428 // {{{ _getDeclaration()
431 * Obtain DBMS specific SQL code portion needed to declare a generic type
432 * field to be used in statements like CREATE TABLE.
434 * @param string $name name the field to be declared.
435 * @param array $field associative array with the name of the properties
436 * of the field being declared as array indexes. Currently, the types
437 * of supported field properties are as follows:
440 * Integer value that determines the maximum length of the text
441 * field. If this argument is missing the field should be
442 * declared to have the longest length allowed by the DBMS.
445 * Text value to be used as default for this field.
448 * Boolean flag that indicates whether this field is constrained
449 * to not be set to null.
451 * Text value with the default CHARACTER SET for this field.
453 * Text value with the default COLLATION for this field.
454 * @return string DBMS specific SQL code portion that should be used to
455 * declare the specified field, or a MDB2_Error on failure
458 function _getDeclaration($name, $field)
460 $db = $this->getDBInstance();
461 if (MDB2::isError($db)) {
465 $name = $db->quoteIdentifier($name, true);
466 $declaration_options = $db->datatype->_getDeclarationOptions($field);
467 if (MDB2::isError($declaration_options)) {
468 return $declaration_options;
470 return $name.' '.$this->getTypeDeclaration($field).$declaration_options;
474 // {{{ _getDeclarationOptions()
477 * Obtain DBMS specific SQL code portion needed to declare a generic type
478 * field to be used in statement like CREATE TABLE, without the field name
479 * and type values (ie. just the character set, default value, if the
480 * field is permitted to be NULL or not, and the collation options).
482 * @param array $field associative array with the name of the properties
483 * of the field being declared as array indexes. Currently, the types
484 * of supported field properties are as follows:
487 * Text value to be used as default for this field.
489 * Boolean flag that indicates whether this field is constrained
490 * to not be set to null.
492 * Text value with the default CHARACTER SET for this field.
494 * Text value with the default COLLATION for this field.
495 * @return string DBMS specific SQL code portion that should be used to
496 * declare the specified field's options.
499 function _getDeclarationOptions($field)
501 $charset = empty($field['charset']) ? '' :
502 ' '.$this->_getCharsetFieldDeclaration($field['charset']);
504 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
506 if (array_key_exists('default', $field)) {
507 if ($field['default'] === '') {
508 $db = $this->getDBInstance();
509 if (MDB2::isError($db)) {
512 $valid_default_values = $this->getValidTypes();
513 $field['default'] = $valid_default_values[$field['type']];
514 if ($field['default'] === '' && ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)) {
515 $field['default'] = ' ';
518 if (null !== $field['default']) {
519 $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
523 $collation = empty($field['collation']) ? '' :
524 ' '.$this->_getCollationFieldDeclaration($field['collation']);
526 return $charset.$default.$notnull.$collation;
530 // {{{ _getCharsetFieldDeclaration()
533 * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
534 * of a field declaration to be used in statements like CREATE TABLE.
536 * @param string $charset name of the charset
537 * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
538 * of a field declaration.
540 function _getCharsetFieldDeclaration($charset)
546 // {{{ _getCollationFieldDeclaration()
549 * Obtain DBMS specific SQL code portion needed to set the COLLATION
550 * of a field declaration to be used in statements like CREATE TABLE.
552 * @param string $collation name of the collation
553 * @return string DBMS specific SQL code portion needed to set the COLLATION
554 * of a field declaration.
556 function _getCollationFieldDeclaration($collation)
562 // {{{ _getIntegerDeclaration()
565 * Obtain DBMS specific SQL code portion needed to declare an integer type
566 * field to be used in statements like CREATE TABLE.
568 * @param string $name name the field to be declared.
569 * @param array $field associative array with the name of the properties
570 * of the field being declared as array indexes. Currently, the types
571 * of supported field properties are as follows:
574 * Boolean flag that indicates whether the field should be
575 * declared as unsigned integer if possible.
578 * Integer value to be used as default for this field.
581 * Boolean flag that indicates whether this field is constrained
582 * to not be set to null.
583 * @return string DBMS specific SQL code portion that should be used to
584 * declare the specified field.
587 function _getIntegerDeclaration($name, $field)
589 if (!empty($field['unsigned'])) {
590 $db = $this->getDBInstance();
591 if (MDB2::isError($db)) {
595 $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
597 return $this->_getDeclaration($name, $field);
601 // {{{ _getTextDeclaration()
604 * Obtain DBMS specific SQL code portion needed to declare an text type
605 * field to be used in statements like CREATE TABLE.
607 * @param string $name name the field to be declared.
608 * @param array $field associative array with the name of the properties
609 * of the field being declared as array indexes. Currently, the types
610 * of supported field properties are as follows:
613 * Integer value that determines the maximum length of the text
614 * field. If this argument is missing the field should be
615 * declared to have the longest length allowed by the DBMS.
618 * Text value to be used as default for this field.
621 * Boolean flag that indicates whether this field is constrained
622 * to not be set to null.
623 * @return string DBMS specific SQL code portion that should be used to
624 * declare the specified field.
627 function _getTextDeclaration($name, $field)
629 return $this->_getDeclaration($name, $field);
633 // {{{ _getCLOBDeclaration()
636 * Obtain DBMS specific SQL code portion needed to declare an character
637 * large object type field to be used in statements like CREATE TABLE.
639 * @param string $name name the field to be declared.
640 * @param array $field associative array with the name of the properties
641 * of the field being declared as array indexes. Currently, the types
642 * of supported field properties are as follows:
645 * Integer value that determines the maximum length of the large
646 * object field. If this argument is missing the field should be
647 * declared to have the longest length allowed by the DBMS.
650 * Boolean flag that indicates whether this field is constrained
651 * to not be set to null.
652 * @return string DBMS specific SQL code portion that should be used to
653 * declare the specified field.
656 function _getCLOBDeclaration($name, $field)
658 $db = $this->getDBInstance();
659 if (MDB2::isError($db)) {
663 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
664 $name = $db->quoteIdentifier($name, true);
665 return $name.' '.$this->getTypeDeclaration($field).$notnull;
669 // {{{ _getBLOBDeclaration()
672 * Obtain DBMS specific SQL code portion needed to declare an binary large
673 * object type field to be used in statements like CREATE TABLE.
675 * @param string $name name the field to be declared.
676 * @param array $field associative array with the name of the properties
677 * of the field being declared as array indexes. Currently, the types
678 * of supported field properties are as follows:
681 * Integer value that determines the maximum length of the large
682 * object field. If this argument is missing the field should be
683 * declared to have the longest length allowed by the DBMS.
686 * Boolean flag that indicates whether this field is constrained
687 * to not be set to null.
688 * @return string DBMS specific SQL code portion that should be used to
689 * declare the specified field.
692 function _getBLOBDeclaration($name, $field)
694 $db = $this->getDBInstance();
695 if (MDB2::isError($db)) {
699 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
700 $name = $db->quoteIdentifier($name, true);
701 return $name.' '.$this->getTypeDeclaration($field).$notnull;
705 // {{{ _getBooleanDeclaration()
708 * Obtain DBMS specific SQL code portion needed to declare a boolean type
709 * field to be used in statements like CREATE TABLE.
711 * @param string $name name the field to be declared.
712 * @param array $field associative array with the name of the properties
713 * of the field being declared as array indexes. Currently, the types
714 * of supported field properties are as follows:
717 * Boolean value to be used as default for this field.
720 * Boolean flag that indicates whether this field is constrained
721 * to not be set to null.
722 * @return string DBMS specific SQL code portion that should be used to
723 * declare the specified field.
726 function _getBooleanDeclaration($name, $field)
728 return $this->_getDeclaration($name, $field);
732 // {{{ _getDateDeclaration()
735 * Obtain DBMS specific SQL code portion needed to declare a date type
736 * field to be used in statements like CREATE TABLE.
738 * @param string $name name the field to be declared.
739 * @param array $field associative array with the name of the properties
740 * of the field being declared as array indexes. Currently, the types
741 * of supported field properties are as follows:
744 * Date value to be used as default for this field.
747 * Boolean flag that indicates whether this field is constrained
748 * to not be set to null.
749 * @return string DBMS specific SQL code portion that should be used to
750 * declare the specified field.
753 function _getDateDeclaration($name, $field)
755 return $this->_getDeclaration($name, $field);
759 // {{{ _getTimestampDeclaration()
762 * Obtain DBMS specific SQL code portion needed to declare a timestamp
763 * field to be used in statements like CREATE TABLE.
765 * @param string $name name the field to be declared.
766 * @param array $field associative array with the name of the properties
767 * of the field being declared as array indexes. Currently, the types
768 * of supported field properties are as follows:
771 * Timestamp value to be used as default for this field.
774 * Boolean flag that indicates whether this field is constrained
775 * to not be set to null.
776 * @return string DBMS specific SQL code portion that should be used to
777 * declare the specified field.
780 function _getTimestampDeclaration($name, $field)
782 return $this->_getDeclaration($name, $field);
786 // {{{ _getTimeDeclaration()
789 * Obtain DBMS specific SQL code portion needed to declare a time
790 * field to be used in statements like CREATE TABLE.
792 * @param string $name name the field to be declared.
793 * @param array $field associative array with the name of the properties
794 * of the field being declared as array indexes. Currently, the types
795 * of supported field properties are as follows:
798 * Time value to be used as default for this field.
801 * Boolean flag that indicates whether this field is constrained
802 * to not be set to null.
803 * @return string DBMS specific SQL code portion that should be used to
804 * declare the specified field.
807 function _getTimeDeclaration($name, $field)
809 return $this->_getDeclaration($name, $field);
813 // {{{ _getFloatDeclaration()
816 * Obtain DBMS specific SQL code portion needed to declare a float type
817 * field to be used in statements like CREATE TABLE.
819 * @param string $name name the field to be declared.
820 * @param array $field associative array with the name of the properties
821 * of the field being declared as array indexes. Currently, the types
822 * of supported field properties are as follows:
825 * Float value to be used as default for this field.
828 * Boolean flag that indicates whether this field is constrained
829 * to not be set to null.
830 * @return string DBMS specific SQL code portion that should be used to
831 * declare the specified field.
834 function _getFloatDeclaration($name, $field)
836 return $this->_getDeclaration($name, $field);
840 // {{{ _getDecimalDeclaration()
843 * Obtain DBMS specific SQL code portion needed to declare a decimal type
844 * field to be used in statements like CREATE TABLE.
846 * @param string $name name the field to be declared.
847 * @param array $field associative array with the name of the properties
848 * of the field being declared as array indexes. Currently, the types
849 * of supported field properties are as follows:
852 * Decimal value to be used as default for this field.
855 * Boolean flag that indicates whether this field is constrained
856 * to not be set to null.
857 * @return string DBMS specific SQL code portion that should be used to
858 * declare the specified field.
861 function _getDecimalDeclaration($name, $field)
863 return $this->_getDeclaration($name, $field);
867 // {{{ compareDefinition()
870 * Obtain an array of changes that may need to applied
872 * @param array $current new definition
873 * @param array $previous old definition
874 * @return array containing all changes that will need to be applied
877 function compareDefinition($current, $previous)
879 $type = !empty($current['type']) ? $current['type'] : null;
881 if (!method_exists($this, "_compare{$type}Definition")) {
882 $db = $this->getDBInstance();
883 if (MDB2::isError($db)) {
886 if (!empty($db->options['datatype_map_callback'][$type])) {
887 $parameter = array('current' => $current, 'previous' => $previous);
888 $change = call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
891 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
892 'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
895 if (empty($previous['type']) || $previous['type'] != $type) {
899 $change = $this->{"_compare{$type}Definition"}($current, $previous);
901 if ($previous['type'] != $type) {
902 $change['type'] = true;
905 $previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
906 $notnull = !empty($current['notnull']) ? $current['notnull'] : false;
907 if ($previous_notnull != $notnull) {
908 $change['notnull'] = true;
911 $previous_default = array_key_exists('default', $previous) ? $previous['default'] :
913 $default = array_key_exists('default', $current) ? $current['default'] :
915 if ($previous_default !== $default) {
916 $change['default'] = true;
923 // {{{ _compareIntegerDefinition()
926 * Obtain an array of changes that may need to applied to an integer field
928 * @param array $current new definition
929 * @param array $previous old definition
930 * @return array containing all changes that will need to be applied
933 function _compareIntegerDefinition($current, $previous)
936 $previous_length = !empty($previous['length']) ? $previous['length'] : 4;
937 $length = !empty($current['length']) ? $current['length'] : 4;
938 if ($previous_length != $length) {
939 $change['length'] = $length;
941 $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false;
942 $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false;
943 if ($previous_unsigned != $unsigned) {
944 $change['unsigned'] = true;
946 $previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false;
947 $autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false;
948 if ($previous_autoincrement != $autoincrement) {
949 $change['autoincrement'] = true;
955 // {{{ _compareTextDefinition()
958 * Obtain an array of changes that may need to applied to an text field
960 * @param array $current new definition
961 * @param array $previous old definition
962 * @return array containing all changes that will need to be applied
965 function _compareTextDefinition($current, $previous)
968 $previous_length = !empty($previous['length']) ? $previous['length'] : 0;
969 $length = !empty($current['length']) ? $current['length'] : 0;
970 if ($previous_length != $length) {
971 $change['length'] = true;
973 $previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0;
974 $fixed = !empty($current['fixed']) ? $current['fixed'] : 0;
975 if ($previous_fixed != $fixed) {
976 $change['fixed'] = true;
982 // {{{ _compareCLOBDefinition()
985 * Obtain an array of changes that may need to applied to an CLOB field
987 * @param array $current new definition
988 * @param array $previous old definition
989 * @return array containing all changes that will need to be applied
992 function _compareCLOBDefinition($current, $previous)
994 return $this->_compareTextDefinition($current, $previous);
998 // {{{ _compareBLOBDefinition()
1001 * Obtain an array of changes that may need to applied to an BLOB field
1003 * @param array $current new definition
1004 * @param array $previous old definition
1005 * @return array containing all changes that will need to be applied
1008 function _compareBLOBDefinition($current, $previous)
1010 return $this->_compareTextDefinition($current, $previous);
1014 // {{{ _compareDateDefinition()
1017 * Obtain an array of changes that may need to applied to an date field
1019 * @param array $current new definition
1020 * @param array $previous old definition
1021 * @return array containing all changes that will need to be applied
1024 function _compareDateDefinition($current, $previous)
1030 // {{{ _compareTimeDefinition()
1033 * Obtain an array of changes that may need to applied to an time field
1035 * @param array $current new definition
1036 * @param array $previous old definition
1037 * @return array containing all changes that will need to be applied
1040 function _compareTimeDefinition($current, $previous)
1046 // {{{ _compareTimestampDefinition()
1049 * Obtain an array of changes that may need to applied to an timestamp field
1051 * @param array $current new definition
1052 * @param array $previous old definition
1053 * @return array containing all changes that will need to be applied
1056 function _compareTimestampDefinition($current, $previous)
1062 // {{{ _compareBooleanDefinition()
1065 * Obtain an array of changes that may need to applied to an boolean field
1067 * @param array $current new definition
1068 * @param array $previous old definition
1069 * @return array containing all changes that will need to be applied
1072 function _compareBooleanDefinition($current, $previous)
1078 // {{{ _compareFloatDefinition()
1081 * Obtain an array of changes that may need to applied to an float field
1083 * @param array $current new definition
1084 * @param array $previous old definition
1085 * @return array containing all changes that will need to be applied
1088 function _compareFloatDefinition($current, $previous)
1094 // {{{ _compareDecimalDefinition()
1097 * Obtain an array of changes that may need to applied to an decimal field
1099 * @param array $current new definition
1100 * @param array $previous old definition
1101 * @return array containing all changes that will need to be applied
1104 function _compareDecimalDefinition($current, $previous)
1113 * Convert a text value into a DBMS specific format that is suitable to
1114 * compose query statements.
1116 * @param string $value text string value that is intended to be converted.
1117 * @param string $type type to which the value should be converted to
1118 * @param bool $quote determines if the value should be quoted and escaped
1119 * @param bool $escape_wildcards if to escape escape wildcards
1120 * @return string text string that represents the given argument value in
1121 * a DBMS specific format.
1124 function quote($value, $type = null, $quote = true, $escape_wildcards = false)
1126 $db = $this->getDBInstance();
1127 if (MDB2::isError($db)) {
1131 if ((null === $value)
1132 || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
1140 if (null === $type) {
1141 switch (gettype($value)) {
1146 // todo: default to decimal as float is quite unusual
1154 $value = serialize($value);
1159 if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
1160 $type = 'timestamp';
1161 } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
1163 } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
1170 } elseif (!empty($db->options['datatype_map'][$type])) {
1171 $type = $db->options['datatype_map'][$type];
1172 if (!empty($db->options['datatype_map_callback'][$type])) {
1173 $parameter = array('type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards);
1174 return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
1178 if (!method_exists($this, "_quote{$type}")) {
1179 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1180 'type not defined: '.$type, __FUNCTION__);
1182 $value = $this->{"_quote{$type}"}($value, $quote, $escape_wildcards);
1183 if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern']
1184 && $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern']
1186 $value.= $this->patternEscapeString();
1192 // {{{ _quoteInteger()
1195 * Convert a text value into a DBMS specific format that is suitable to
1196 * compose query statements.
1198 * @param string $value text string value that is intended to be converted.
1199 * @param bool $quote determines if the value should be quoted and escaped
1200 * @param bool $escape_wildcards if to escape escape wildcards
1201 * @return string text string that represents the given argument value in
1202 * a DBMS specific format.
1205 function _quoteInteger($value, $quote, $escape_wildcards)
1214 * Convert a text value into a DBMS specific format that is suitable to
1215 * compose query statements.
1217 * @param string $value text string value that is intended to be converted.
1218 * @param bool $quote determines if the value should be quoted and escaped
1219 * @param bool $escape_wildcards if to escape escape wildcards
1220 * @return string text string that already contains any DBMS specific
1221 * escaped character sequences.
1224 function _quoteText($value, $quote, $escape_wildcards)
1230 $db = $this->getDBInstance();
1231 if (MDB2::isError($db)) {
1235 $value = $db->escape($value, $escape_wildcards);
1236 if (MDB2::isError($value)) {
1239 return "'".$value."'";
1246 * Convert a text value into a DBMS specific format that is suitable to
1247 * compose query statements.
1249 * @param string $value text string value that is intended to be converted.
1250 * @return string text string that represents the given argument value in
1251 * a DBMS specific format.
1254 function _readFile($value)
1257 if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1259 if (strtolower($match[1]) == 'file://') {
1262 $value = @fopen($value, 'r');
1265 if (is_resource($value)) {
1266 $db = $this->getDBInstance();
1267 if (MDB2::isError($db)) {
1273 while (!@feof($fp)) {
1274 $value.= @fread($fp, $db->options['lob_buffer_length']);
1288 * Convert a text value into a DBMS specific format that is suitable to
1289 * compose query statements.
1291 * @param string $value text string value that is intended to be converted.
1292 * @param bool $quote determines if the value should be quoted and escaped
1293 * @param bool $escape_wildcards if to escape escape wildcards
1294 * @return string text string that represents the given argument value in
1295 * a DBMS specific format.
1298 function _quoteLOB($value, $quote, $escape_wildcards)
1300 $db = $this->getDBInstance();
1301 if (MDB2::isError($db)) {
1304 if ($db->options['lob_allow_url_include']) {
1305 $value = $this->_readFile($value);
1306 if (MDB2::isError($value)) {
1310 return $this->_quoteText($value, $quote, $escape_wildcards);
1317 * Convert a text value into a DBMS specific format that is suitable to
1318 * compose query statements.
1320 * @param string $value text string value that is intended to be converted.
1321 * @param bool $quote determines if the value should be quoted and escaped
1322 * @param bool $escape_wildcards if to escape escape wildcards
1323 * @return string text string that represents the given argument value in
1324 * a DBMS specific format.
1327 function _quoteCLOB($value, $quote, $escape_wildcards)
1329 return $this->_quoteLOB($value, $quote, $escape_wildcards);
1336 * Convert a text value into a DBMS specific format that is suitable to
1337 * compose query statements.
1339 * @param string $value text string value that is intended to be converted.
1340 * @param bool $quote determines if the value should be quoted and escaped
1341 * @param bool $escape_wildcards if to escape escape wildcards
1342 * @return string text string that represents the given argument value in
1343 * a DBMS specific format.
1346 function _quoteBLOB($value, $quote, $escape_wildcards)
1348 return $this->_quoteLOB($value, $quote, $escape_wildcards);
1352 // {{{ _quoteBoolean()
1355 * Convert a text value into a DBMS specific format that is suitable to
1356 * compose query statements.
1358 * @param string $value text string value that is intended to be converted.
1359 * @param bool $quote determines if the value should be quoted and escaped
1360 * @param bool $escape_wildcards if to escape escape wildcards
1361 * @return string text string that represents the given argument value in
1362 * a DBMS specific format.
1365 function _quoteBoolean($value, $quote, $escape_wildcards)
1367 return ($value ? 1 : 0);
1374 * Convert a text value into a DBMS specific format that is suitable to
1375 * compose query statements.
1377 * @param string $value text string value that is intended to be converted.
1378 * @param bool $quote determines if the value should be quoted and escaped
1379 * @param bool $escape_wildcards if to escape escape wildcards
1380 * @return string text string that represents the given argument value in
1381 * a DBMS specific format.
1384 function _quoteDate($value, $quote, $escape_wildcards)
1386 if ($value === 'CURRENT_DATE') {
1387 $db = $this->getDBInstance();
1388 if (MDB2::isError($db)) {
1391 if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1392 return $db->function->now('date');
1394 return 'CURRENT_DATE';
1396 return $this->_quoteText($value, $quote, $escape_wildcards);
1400 // {{{ _quoteTimestamp()
1403 * Convert a text value into a DBMS specific format that is suitable to
1404 * compose query statements.
1406 * @param string $value text string value that is intended to be converted.
1407 * @param bool $quote determines if the value should be quoted and escaped
1408 * @param bool $escape_wildcards if to escape escape wildcards
1409 * @return string text string that represents the given argument value in
1410 * a DBMS specific format.
1413 function _quoteTimestamp($value, $quote, $escape_wildcards)
1415 if ($value === 'CURRENT_TIMESTAMP') {
1416 $db = $this->getDBInstance();
1417 if (MDB2::isError($db)) {
1420 if (isset($db->function) && is_object($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1421 return $db->function->now('timestamp');
1423 return 'CURRENT_TIMESTAMP';
1425 return $this->_quoteText($value, $quote, $escape_wildcards);
1432 * Convert a text value into a DBMS specific format that is suitable to
1433 * compose query statements.
1435 * @param string $value text string value that is intended to be converted.
1436 * @param bool $quote determines if the value should be quoted and escaped
1437 * @param bool $escape_wildcards if to escape escape wildcards
1438 * @return string text string that represents the given argument value in
1439 * a DBMS specific format.
1442 function _quoteTime($value, $quote, $escape_wildcards)
1444 if ($value === 'CURRENT_TIME') {
1445 $db = $this->getDBInstance();
1446 if (MDB2::isError($db)) {
1449 if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1450 return $db->function->now('time');
1452 return 'CURRENT_TIME';
1454 return $this->_quoteText($value, $quote, $escape_wildcards);
1458 // {{{ _quoteFloat()
1461 * Convert a text value into a DBMS specific format that is suitable to
1462 * compose query statements.
1464 * @param string $value text string value that is intended to be converted.
1465 * @param bool $quote determines if the value should be quoted and escaped
1466 * @param bool $escape_wildcards if to escape escape wildcards
1467 * @return string text string that represents the given argument value in
1468 * a DBMS specific format.
1471 function _quoteFloat($value, $quote, $escape_wildcards)
1473 if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
1474 $decimal = $this->_quoteDecimal($matches[1], $quote, $escape_wildcards);
1475 $sign = $matches[2];
1476 $exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
1477 $value = $decimal.'E'.$sign.$exponent;
1479 $value = $this->_quoteDecimal($value, $quote, $escape_wildcards);
1485 // {{{ _quoteDecimal()
1488 * Convert a text value into a DBMS specific format that is suitable to
1489 * compose query statements.
1491 * @param string $value text string value that is intended to be converted.
1492 * @param bool $quote determines if the value should be quoted and escaped
1493 * @param bool $escape_wildcards if to escape escape wildcards
1494 * @return string text string that represents the given argument value in
1495 * a DBMS specific format.
1498 function _quoteDecimal($value, $quote, $escape_wildcards)
1500 $value = (string)$value;
1501 $value = preg_replace('/[^\d\.,\-+eE]/', '', $value);
1502 if (preg_match('/[^\.\d]/', $value)) {
1503 if (strpos($value, ',')) {
1505 if (!strpos($value, '.')) {
1506 // convert the last "," to a "."
1507 $value = strrev(str_replace(',', '.', strrev($value)));
1509 } elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) {
1510 $value = str_replace('.', '', $value);
1511 // convert the last "," to a "."
1512 $value = strrev(str_replace(',', '.', strrev($value)));
1515 $value = str_replace(',', '', $value);
1523 // {{{ writeLOBToFile()
1526 * retrieve LOB from the database
1528 * @param resource $lob stream handle
1529 * @param string $file name of the file into which the LOb should be fetched
1530 * @return mixed MDB2_OK on success, a MDB2 error on failure
1533 function writeLOBToFile($lob, $file)
1535 $db = $this->getDBInstance();
1536 if (MDB2::isError($db)) {
1540 if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
1541 if ($match[1] == 'file://') {
1546 $fp = @fopen($file, 'wb');
1547 while (!@feof($lob)) {
1548 $result = @fread($lob, $db->options['lob_buffer_length']);
1549 $read = strlen($result);
1550 if (@fwrite($fp, $result, $read) != $read) {
1552 return $db->raiseError(MDB2_ERROR, null, null,
1553 'could not write to the output file', __FUNCTION__);
1561 // {{{ _retrieveLOB()
1564 * retrieve LOB from the database
1566 * @param array $lob array
1567 * @return mixed MDB2_OK on success, a MDB2 error on failure
1570 function _retrieveLOB(&$lob)
1572 if (null === $lob['value']) {
1573 $lob['value'] = $lob['resource'];
1575 $lob['loaded'] = true;
1583 * Read data from large object input stream.
1585 * @param resource $lob stream handle
1586 * @param string $data reference to a variable that will hold data
1587 * to be read from the large object input stream
1588 * @param integer $length value that indicates the largest ammount ofdata
1589 * to be read from the large object input stream.
1590 * @return mixed the effective number of bytes read from the large object
1591 * input stream on sucess or an MDB2 error object.
1595 function _readLOB($lob, $length)
1597 return substr($lob['value'], $lob['position'], $length);
1604 * Determine whether it was reached the end of the large object and
1605 * therefore there is no more data to be read for the its input stream.
1607 * @param array $lob array
1608 * @return mixed true or false on success, a MDB2 error on failure
1611 function _endOfLOB($lob)
1613 return $lob['endOfLOB'];
1620 * Free any resources allocated during the lifetime of the large object
1623 * @param resource $lob stream handle
1626 function destroyLOB($lob)
1628 $lob_data = stream_get_meta_data($lob);
1629 $lob_index = $lob_data['wrapper_data']->lob_index;
1631 if (isset($this->lobs[$lob_index])) {
1632 $this->_destroyLOB($this->lobs[$lob_index]);
1633 unset($this->lobs[$lob_index]);
1639 // {{{ _destroyLOB()
1642 * Free any resources allocated during the lifetime of the large object
1645 * @param array $lob array
1648 function _destroyLOB(&$lob)
1654 // {{{ implodeArray()
1657 * apply a type to all values of an array and return as a comma seperated string
1658 * useful for generating IN statements
1662 * @param array $array data array
1663 * @param string $type determines type of the field
1665 * @return string comma seperated values
1667 function implodeArray($array, $type = false)
1669 if (!is_array($array) || empty($array)) {
1673 foreach ($array as $value) {
1674 $return[] = $this->quote($value, $type);
1679 return implode(', ', $return);
1683 // {{{ matchPattern()
1686 * build a pattern matching string
1690 * @param array $pattern even keys are strings, odd are patterns (% and _)
1691 * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
1692 * @param string $field optional field name that is being matched against
1693 * (might be required when emulating ILIKE)
1695 * @return string SQL pattern
1697 function matchPattern($pattern, $operator = null, $field = null)
1699 $db = $this->getDBInstance();
1700 if (MDB2::isError($db)) {
1705 if (null !== $operator) {
1706 $operator = strtoupper($operator);
1707 switch ($operator) {
1710 if (null === $field) {
1711 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1712 'case insensitive LIKE matching requires passing the field name', __FUNCTION__);
1714 $db->loadModule('Function', null, true);
1715 $match = $db->function->lower($field).' LIKE ';
1718 if (null === $field) {
1719 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1720 'case insensitive NOT ILIKE matching requires passing the field name', __FUNCTION__);
1722 $db->loadModule('Function', null, true);
1723 $match = $db->function->lower($field).' NOT LIKE ';
1727 $match = (null === $field) ? 'LIKE ' : ($field.' LIKE ');
1730 $match = (null === $field) ? 'NOT LIKE ' : ($field.' NOT LIKE ');
1733 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1734 'not a supported operator type:'. $operator, __FUNCTION__);
1738 foreach ($pattern as $key => $value) {
1742 $escaped = $db->escape($value);
1743 if (MDB2::isError($escaped)) {
1746 $match.= $db->escapePattern($escaped);
1750 $match.= $this->patternEscapeString();
1755 // {{{ patternEscapeString()
1758 * build string to define pattern escape character
1762 * @return string define pattern escape character
1764 function patternEscapeString()
1770 // {{{ mapNativeDatatype()
1773 * Maps a native array description of a field to a MDB2 datatype and length
1775 * @param array $field native field description
1776 * @return array containing the various possible types, length, sign, fixed
1779 function mapNativeDatatype($field)
1781 $db = $this->getDBInstance();
1782 if (MDB2::isError($db)) {
1786 // If the user has specified an option to map the native field
1787 // type to a custom MDB2 datatype...
1788 $db_type = strtok($field['type'], '(), ');
1789 if (!empty($db->options['nativetype_map_callback'][$db_type])) {
1790 return call_user_func_array($db->options['nativetype_map_callback'][$db_type], array($db, $field));
1793 // Otherwise perform the built-in (i.e. normal) MDB2 native type to
1794 // MDB2 datatype conversion
1795 return $this->_mapNativeDatatype($field);
1799 // {{{ _mapNativeDatatype()
1802 * Maps a native array description of a field to a MDB2 datatype and length
1804 * @param array $field native field description
1805 * @return array containing the various possible types, length, sign, fixed
1808 function _mapNativeDatatype($field)
1810 $db = $this->getDBInstance();
1811 if (MDB2::isError($db)) {
1815 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1816 'method not implemented', __FUNCTION__);
1820 // {{{ mapPrepareDatatype()
1823 * Maps an mdb2 datatype to mysqli prepare type
1825 * @param string $type
1829 function mapPrepareDatatype($type)
1831 $db = $this->getDBInstance();
1832 if (MDB2::isError($db)) {
1836 if (!empty($db->options['datatype_map'][$type])) {
1837 $type = $db->options['datatype_map'][$type];
1838 if (!empty($db->options['datatype_map_callback'][$type])) {
1839 $parameter = array('type' => $type);
1840 return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));