Initial repo created
[timetracker.git] / WEB-INF / lib / pear / MDB2 / Driver / Datatype / Common.php
1 <?php
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.                            |
12 // |                                                                      |
13 // | Redistribution and use in source and binary forms, with or without   |
14 // | modification, are permitted provided that the following conditions   |
15 // | are met:                                                             |
16 // |                                                                      |
17 // | Redistributions of source code must retain the above copyright       |
18 // | notice, this list of conditions and the following disclaimer.        |
19 // |                                                                      |
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. |
23 // |                                                                      |
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.                                                  |
28 // |                                                                      |
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 // +----------------------------------------------------------------------+
44 //
45 // $Id: Common.php 328137 2012-10-25 02:26:35Z danielc $
46
47 require_once 'MDB2/LOB.php';
48
49 /**
50  * @package  MDB2
51  * @category Database
52  * @author   Lukas Smith <smith@pooteeweet.org>
53  */
54
55 /**
56  * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
57  *
58  * To load this module in the MDB2 object:
59  * $mdb->loadModule('Datatype');
60  *
61  * @package MDB2
62  * @category Database
63  * @author Lukas Smith <smith@pooteeweet.org>
64  */
65 class MDB2_Driver_Datatype_Common extends MDB2_Module_Common
66 {
67     var $valid_default_values = array(
68         'text'      => '',
69         'boolean'   => true,
70         'integer'   => 0,
71         'decimal'   => 0.0,
72         'float'     => 0.0,
73         'timestamp' => '1970-01-01 00:00:00',
74         'time'      => '00:00:00',
75         'date'      => '1970-01-01',
76         'clob'      => '',
77         'blob'      => '',
78     );
79
80     /**
81      * contains all LOB objects created with this MDB2 instance
82      * @var array
83      * @access protected
84      */
85     var $lobs = array();
86
87     // }}}
88     // {{{ getValidTypes()
89
90     /**
91      * Get the list of valid types
92      *
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.
96      *
97      * @return mixed array on success, a MDB2 error on failure
98      * @access public
99      */
100     function getValidTypes()
101     {
102         $types = $this->valid_default_values;
103         $db = $this->getDBInstance();
104         if (MDB2::isError($db)) {
105             return $db;
106         }
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;
115                 }
116             }
117         }
118         return $types;
119     }
120
121     // }}}
122     // {{{ checkResultTypes()
123
124     /**
125      * Define the list of types to be associated with the columns of a given
126      * result set.
127      *
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.
133      *
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
139      *       supported.
140      * @return mixed MDB2_OK on success, a MDB2 error on failure
141      * @access public
142      */
143     function checkResultTypes($types)
144     {
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)) {
150                     return $db;
151                 }
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__);
155                 }
156             }
157         }
158         return $types;
159     }
160
161     // }}}
162     // {{{ _baseConvertResult()
163
164     /**
165      * General type conversion method
166      *
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
171      * @access protected
172      */
173     function _baseConvertResult($value, $type, $rtrim = true)
174     {
175         switch ($type) {
176         case 'text':
177             if ($rtrim) {
178                 $value = rtrim($value);
179             }
180             return $value;
181         case 'integer':
182             return intval($value);
183         case 'boolean':
184             return !empty($value);
185         case 'decimal':
186             return $value;
187         case 'float':
188             return doubleval($value);
189         case 'date':
190             return $value;
191         case 'time':
192             return $value;
193         case 'timestamp':
194             return $value;
195         case 'clob':
196         case 'blob':
197             $this->lobs[] = array(
198                 'buffer' => null,
199                 'position' => 0,
200                 'lob_index' => null,
201                 'endOfLOB' => false,
202                 'resource' => $value,
203                 'value' => null,
204                 'loaded' => false,
205             );
206             end($this->lobs);
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+');
210         }
211
212         $db = $this->getDBInstance();
213         if (MDB2::isError($db)) {
214             return $db;
215         }
216
217         return $db->raiseError(MDB2_ERROR_INVALID, null, null,
218             'attempt to convert result value to an unknown type :' . $type, __FUNCTION__);
219     }
220
221     // }}}
222     // {{{ convertResult()
223
224     /**
225      * Convert a value to a RDBMS indipendent MDB2 type
226      *
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
231      * @access public
232      */
233     function convertResult($value, $type, $rtrim = true)
234     {
235         if (null === $value) {
236             return null;
237         }
238         $db = $this->getDBInstance();
239         if (MDB2::isError($db)) {
240             return $db;
241         }
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));
247             }
248         }
249         return $this->_baseConvertResult($value, $type, $rtrim);
250     }
251
252     // }}}
253     // {{{ convertResultRow()
254
255     /**
256      * Convert a result row
257      *
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
262      * @access public
263      */
264     function convertResultRow($types, $row, $rtrim = true)
265     {
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);
270         }
271         foreach ($row as $key => $value) {
272             if (empty($types[$key])) {
273                 continue;
274             }
275             $value = $this->convertResult($row[$key], $types[$key], $rtrim);
276             if (MDB2::isError($value)) {
277                 return $value;
278             }
279             $row[$key] = $value;
280         }
281         return $row;
282     }
283
284     // }}}
285     // {{{ _sortResultFieldTypes()
286
287     /**
288      * convert a result row
289      *
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
294      * @access public
295      */
296     function _sortResultFieldTypes($columns, $types)
297     {
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--) {
302                 $types[] = null;
303             }
304         }
305         $sorted_types = array();
306         foreach ($columns as $col) {
307             $sorted_types[$col] = null;
308         }
309         foreach ($types as $name => $type) {
310             if (array_key_exists($name, $sorted_types)) {
311                 $sorted_types[$name] = $type;
312                 unset($types[$name]);
313             }
314         }
315         // if there are left types in the array, fill the null values of the
316         // sorted array with them, in order.
317         if (count($types)) {
318             reset($types);
319             foreach (array_keys($sorted_types) as $k) {
320                 if (null === $sorted_types[$k]) {
321                     $sorted_types[$k] = current($types);
322                     next($types);
323                 }
324             }
325         }
326         return $sorted_types;
327     }
328
329     // }}}
330     // {{{ getDeclaration()
331
332     /**
333      * Obtain DBMS specific SQL code portion needed to declare
334      * of the given type
335      *
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.
341      * @access public
342      */
343     function getDeclaration($type, $name, $field)
344     {
345         $db = $this->getDBInstance();
346         if (MDB2::isError($db)) {
347             return $db;
348         }
349
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));
355             }
356             $field['type'] = $type;
357         }
358
359         if (!method_exists($this, "_get{$type}Declaration")) {
360             return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
361                 'type not defined: '.$type, __FUNCTION__);
362         }
363         return $this->{"_get{$type}Declaration"}($name, $field);
364     }
365
366     // }}}
367     // {{{ getTypeDeclaration()
368
369     /**
370      * Obtain DBMS specific SQL code portion needed to declare an text type
371      * field to be used in statements like CREATE TABLE.
372      *
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:
376      *
377      *      length
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.
381      *
382      *      default
383      *          Text value to be used as default for this field.
384      *
385      *      notnull
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.
390      * @access public
391      */
392     function getTypeDeclaration($field)
393     {
394         $db = $this->getDBInstance();
395         if (MDB2::isError($db)) {
396             return $db;
397         }
398
399         switch ($field['type']) {
400         case 'text':
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');
405         case 'clob':
406             return 'TEXT';
407         case 'blob':
408             return 'TEXT';
409         case 'integer':
410             return 'INT';
411         case 'boolean':
412             return 'INT';
413         case 'date':
414             return 'CHAR ('.strlen('YYYY-MM-DD').')';
415         case 'time':
416             return 'CHAR ('.strlen('HH:MM:SS').')';
417         case 'timestamp':
418             return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
419         case 'float':
420             return 'TEXT';
421         case 'decimal':
422             return 'TEXT';
423         }
424         return '';
425     }
426
427     // }}}
428     // {{{ _getDeclaration()
429
430     /**
431      * Obtain DBMS specific SQL code portion needed to declare a generic type
432      * field to be used in statements like CREATE TABLE.
433      *
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:
438      *
439      *      length
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.
443      *
444      *      default
445      *          Text value to be used as default for this field.
446      *
447      *      notnull
448      *          Boolean flag that indicates whether this field is constrained
449      *          to not be set to null.
450      *      charset
451      *          Text value with the default CHARACTER SET for this field.
452      *      collation
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
456      * @access protected
457      */
458     function _getDeclaration($name, $field)
459     {
460         $db = $this->getDBInstance();
461         if (MDB2::isError($db)) {
462             return $db;
463         }
464
465         $name = $db->quoteIdentifier($name, true);
466         $declaration_options = $db->datatype->_getDeclarationOptions($field);
467         if (MDB2::isError($declaration_options)) {
468             return $declaration_options;
469         }
470         return $name.' '.$this->getTypeDeclaration($field).$declaration_options;
471     }
472
473     // }}}
474     // {{{ _getDeclarationOptions()
475
476     /**
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).
481      *
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:
485      *
486      *      default
487      *          Text value to be used as default for this field.
488      *      notnull
489      *          Boolean flag that indicates whether this field is constrained
490      *          to not be set to null.
491      *      charset
492      *          Text value with the default CHARACTER SET for this field.
493      *      collation
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.
497      * @access protected
498      */
499     function _getDeclarationOptions($field)
500     {
501         $charset = empty($field['charset']) ? '' :
502             ' '.$this->_getCharsetFieldDeclaration($field['charset']);
503
504         $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
505         $default = '';
506         if (array_key_exists('default', $field)) {
507             if ($field['default'] === '') {
508                 $db = $this->getDBInstance();
509                 if (MDB2::isError($db)) {
510                     return $db;
511                 }
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'] = ' ';
516                 }
517             }
518             if (null !== $field['default']) {
519                 $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
520             }
521         }
522
523         $collation = empty($field['collation']) ? '' :
524             ' '.$this->_getCollationFieldDeclaration($field['collation']);
525
526         return $charset.$default.$notnull.$collation;
527     }
528
529     // }}}
530     // {{{ _getCharsetFieldDeclaration()
531
532     /**
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.
535      *
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.
539      */
540     function _getCharsetFieldDeclaration($charset)
541     {
542         return '';
543     }
544
545     // }}}
546     // {{{ _getCollationFieldDeclaration()
547
548     /**
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.
551      *
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.
555      */
556     function _getCollationFieldDeclaration($collation)
557     {
558         return '';
559     }
560
561     // }}}
562     // {{{ _getIntegerDeclaration()
563
564     /**
565      * Obtain DBMS specific SQL code portion needed to declare an integer type
566      * field to be used in statements like CREATE TABLE.
567      *
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:
572      *
573      *       unsigned
574      *           Boolean flag that indicates whether the field should be
575      *           declared as unsigned integer if possible.
576      *
577      *       default
578      *           Integer value to be used as default for this field.
579      *
580      *       notnull
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.
585      * @access protected
586      */
587     function _getIntegerDeclaration($name, $field)
588     {
589         if (!empty($field['unsigned'])) {
590             $db = $this->getDBInstance();
591             if (MDB2::isError($db)) {
592                 return $db;
593             }
594
595             $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
596         }
597         return $this->_getDeclaration($name, $field);
598     }
599
600     // }}}
601     // {{{ _getTextDeclaration()
602
603     /**
604      * Obtain DBMS specific SQL code portion needed to declare an text type
605      * field to be used in statements like CREATE TABLE.
606      *
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:
611      *
612      *       length
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.
616      *
617      *       default
618      *           Text value to be used as default for this field.
619      *
620      *       notnull
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.
625      * @access protected
626      */
627     function _getTextDeclaration($name, $field)
628     {
629         return $this->_getDeclaration($name, $field);
630     }
631
632     // }}}
633     // {{{ _getCLOBDeclaration()
634
635     /**
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.
638      *
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:
643      *
644      *        length
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.
648      *
649      *        notnull
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.
654      * @access public
655      */
656     function _getCLOBDeclaration($name, $field)
657     {
658         $db = $this->getDBInstance();
659         if (MDB2::isError($db)) {
660             return $db;
661         }
662
663         $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
664         $name = $db->quoteIdentifier($name, true);
665         return $name.' '.$this->getTypeDeclaration($field).$notnull;
666     }
667
668     // }}}
669     // {{{ _getBLOBDeclaration()
670
671     /**
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.
674      *
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:
679      *
680      *        length
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.
684      *
685      *        notnull
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.
690      * @access protected
691      */
692     function _getBLOBDeclaration($name, $field)
693     {
694         $db = $this->getDBInstance();
695         if (MDB2::isError($db)) {
696             return $db;
697         }
698
699         $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
700         $name = $db->quoteIdentifier($name, true);
701         return $name.' '.$this->getTypeDeclaration($field).$notnull;
702     }
703
704     // }}}
705     // {{{ _getBooleanDeclaration()
706
707     /**
708      * Obtain DBMS specific SQL code portion needed to declare a boolean type
709      * field to be used in statements like CREATE TABLE.
710      *
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:
715      *
716      *       default
717      *           Boolean value to be used as default for this field.
718      *
719      *       notnullL
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.
724      * @access protected
725      */
726     function _getBooleanDeclaration($name, $field)
727     {
728         return $this->_getDeclaration($name, $field);
729     }
730
731     // }}}
732     // {{{ _getDateDeclaration()
733
734     /**
735      * Obtain DBMS specific SQL code portion needed to declare a date type
736      * field to be used in statements like CREATE TABLE.
737      *
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:
742      *
743      *       default
744      *           Date value to be used as default for this field.
745      *
746      *       notnull
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.
751      * @access protected
752      */
753     function _getDateDeclaration($name, $field)
754     {
755         return $this->_getDeclaration($name, $field);
756     }
757
758     // }}}
759     // {{{ _getTimestampDeclaration()
760
761     /**
762      * Obtain DBMS specific SQL code portion needed to declare a timestamp
763      * field to be used in statements like CREATE TABLE.
764      *
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:
769      *
770      *       default
771      *           Timestamp value to be used as default for this field.
772      *
773      *       notnull
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.
778      * @access protected
779      */
780     function _getTimestampDeclaration($name, $field)
781     {
782         return $this->_getDeclaration($name, $field);
783     }
784
785     // }}}
786     // {{{ _getTimeDeclaration()
787
788     /**
789      * Obtain DBMS specific SQL code portion needed to declare a time
790      * field to be used in statements like CREATE TABLE.
791      *
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:
796      *
797      *       default
798      *           Time value to be used as default for this field.
799      *
800      *       notnull
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.
805      * @access protected
806      */
807     function _getTimeDeclaration($name, $field)
808     {
809         return $this->_getDeclaration($name, $field);
810     }
811
812     // }}}
813     // {{{ _getFloatDeclaration()
814
815     /**
816      * Obtain DBMS specific SQL code portion needed to declare a float type
817      * field to be used in statements like CREATE TABLE.
818      *
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:
823      *
824      *       default
825      *           Float value to be used as default for this field.
826      *
827      *       notnull
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.
832      * @access protected
833      */
834     function _getFloatDeclaration($name, $field)
835     {
836         return $this->_getDeclaration($name, $field);
837     }
838
839     // }}}
840     // {{{ _getDecimalDeclaration()
841
842     /**
843      * Obtain DBMS specific SQL code portion needed to declare a decimal type
844      * field to be used in statements like CREATE TABLE.
845      *
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:
850      *
851      *       default
852      *           Decimal value to be used as default for this field.
853      *
854      *       notnull
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.
859      * @access protected
860      */
861     function _getDecimalDeclaration($name, $field)
862     {
863         return $this->_getDeclaration($name, $field);
864     }
865
866     // }}}
867     // {{{ compareDefinition()
868
869     /**
870      * Obtain an array of changes that may need to applied
871      *
872      * @param array $current new definition
873      * @param array  $previous old definition
874      * @return array  containing all changes that will need to be applied
875      * @access public
876      */
877     function compareDefinition($current, $previous)
878     {
879         $type = !empty($current['type']) ? $current['type'] : null;
880
881         if (!method_exists($this, "_compare{$type}Definition")) {
882             $db = $this->getDBInstance();
883             if (MDB2::isError($db)) {
884                 return $db;
885             }
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));
889                 return $change;
890             }
891             return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
892                 'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
893         }
894
895         if (empty($previous['type']) || $previous['type'] != $type) {
896             return $current;
897         }
898
899         $change = $this->{"_compare{$type}Definition"}($current, $previous);
900
901         if ($previous['type'] != $type) {
902             $change['type'] = true;
903         }
904
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;
909         }
910
911         $previous_default = array_key_exists('default', $previous) ? $previous['default'] :
912             null;
913         $default = array_key_exists('default', $current) ? $current['default'] :
914             null;
915         if ($previous_default !== $default) {
916             $change['default'] = true;
917         }
918
919         return $change;
920     }
921
922     // }}}
923     // {{{ _compareIntegerDefinition()
924
925     /**
926      * Obtain an array of changes that may need to applied to an integer field
927      *
928      * @param array $current new definition
929      * @param array  $previous old definition
930      * @return array  containing all changes that will need to be applied
931      * @access protected
932      */
933     function _compareIntegerDefinition($current, $previous)
934     {
935         $change = array();
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;
940         }
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;
945         }
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;
950         }
951         return $change;
952     }
953
954     // }}}
955     // {{{ _compareTextDefinition()
956
957     /**
958      * Obtain an array of changes that may need to applied to an text field
959      *
960      * @param array $current new definition
961      * @param array  $previous old definition
962      * @return array  containing all changes that will need to be applied
963      * @access protected
964      */
965     function _compareTextDefinition($current, $previous)
966     {
967         $change = array();
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;
972         }
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;
977         }
978         return $change;
979     }
980
981     // }}}
982     // {{{ _compareCLOBDefinition()
983
984     /**
985      * Obtain an array of changes that may need to applied to an CLOB field
986      *
987      * @param array $current new definition
988      * @param array  $previous old definition
989      * @return array  containing all changes that will need to be applied
990      * @access protected
991      */
992     function _compareCLOBDefinition($current, $previous)
993     {
994         return $this->_compareTextDefinition($current, $previous);
995     }
996
997     // }}}
998     // {{{ _compareBLOBDefinition()
999
1000     /**
1001      * Obtain an array of changes that may need to applied to an BLOB field
1002      *
1003      * @param array $current new definition
1004      * @param array  $previous old definition
1005      * @return array  containing all changes that will need to be applied
1006      * @access protected
1007      */
1008     function _compareBLOBDefinition($current, $previous)
1009     {
1010         return $this->_compareTextDefinition($current, $previous);
1011     }
1012
1013     // }}}
1014     // {{{ _compareDateDefinition()
1015
1016     /**
1017      * Obtain an array of changes that may need to applied to an date field
1018      *
1019      * @param array $current new definition
1020      * @param array  $previous old definition
1021      * @return array  containing all changes that will need to be applied
1022      * @access protected
1023      */
1024     function _compareDateDefinition($current, $previous)
1025     {
1026         return array();
1027     }
1028
1029     // }}}
1030     // {{{ _compareTimeDefinition()
1031
1032     /**
1033      * Obtain an array of changes that may need to applied to an time field
1034      *
1035      * @param array $current new definition
1036      * @param array  $previous old definition
1037      * @return array  containing all changes that will need to be applied
1038      * @access protected
1039      */
1040     function _compareTimeDefinition($current, $previous)
1041     {
1042         return array();
1043     }
1044
1045     // }}}
1046     // {{{ _compareTimestampDefinition()
1047
1048     /**
1049      * Obtain an array of changes that may need to applied to an timestamp field
1050      *
1051      * @param array $current new definition
1052      * @param array  $previous old definition
1053      * @return array  containing all changes that will need to be applied
1054      * @access protected
1055      */
1056     function _compareTimestampDefinition($current, $previous)
1057     {
1058         return array();
1059     }
1060
1061     // }}}
1062     // {{{ _compareBooleanDefinition()
1063
1064     /**
1065      * Obtain an array of changes that may need to applied to an boolean field
1066      *
1067      * @param array $current new definition
1068      * @param array  $previous old definition
1069      * @return array  containing all changes that will need to be applied
1070      * @access protected
1071      */
1072     function _compareBooleanDefinition($current, $previous)
1073     {
1074         return array();
1075     }
1076
1077     // }}}
1078     // {{{ _compareFloatDefinition()
1079
1080     /**
1081      * Obtain an array of changes that may need to applied to an float field
1082      *
1083      * @param array $current new definition
1084      * @param array  $previous old definition
1085      * @return array  containing all changes that will need to be applied
1086      * @access protected
1087      */
1088     function _compareFloatDefinition($current, $previous)
1089     {
1090         return array();
1091     }
1092
1093     // }}}
1094     // {{{ _compareDecimalDefinition()
1095
1096     /**
1097      * Obtain an array of changes that may need to applied to an decimal field
1098      *
1099      * @param array $current new definition
1100      * @param array  $previous old definition
1101      * @return array  containing all changes that will need to be applied
1102      * @access protected
1103      */
1104     function _compareDecimalDefinition($current, $previous)
1105     {
1106         return array();
1107     }
1108
1109     // }}}
1110     // {{{ quote()
1111
1112     /**
1113      * Convert a text value into a DBMS specific format that is suitable to
1114      * compose query statements.
1115      *
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.
1122      * @access public
1123      */
1124     function quote($value, $type = null, $quote = true, $escape_wildcards = false)
1125     {
1126         $db = $this->getDBInstance();
1127         if (MDB2::isError($db)) {
1128             return $db;
1129         }
1130
1131         if ((null === $value)
1132             || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
1133         ) {
1134             if (!$quote) {
1135                 return null;
1136             }
1137             return 'NULL';
1138         }
1139
1140         if (null === $type) {
1141             switch (gettype($value)) {
1142             case 'integer':
1143                 $type = 'integer';
1144                 break;
1145             case 'double':
1146                 // todo: default to decimal as float is quite unusual
1147                 // $type = 'float';
1148                 $type = 'decimal';
1149                 break;
1150             case 'boolean':
1151                 $type = 'boolean';
1152                 break;
1153             case 'array':
1154                  $value = serialize($value);
1155             case 'object':
1156                  $type = 'text';
1157                 break;
1158             default:
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)) {
1162                     $type = 'time';
1163                 } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
1164                     $type = 'date';
1165                 } else {
1166                     $type = 'text';
1167                 }
1168                 break;
1169             }
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));
1175             }
1176         }
1177
1178         if (!method_exists($this, "_quote{$type}")) {
1179             return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1180                 'type not defined: '.$type, __FUNCTION__);
1181         }
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']
1185         ) {
1186             $value.= $this->patternEscapeString();
1187         }
1188         return $value;
1189     }
1190
1191     // }}}
1192     // {{{ _quoteInteger()
1193
1194     /**
1195      * Convert a text value into a DBMS specific format that is suitable to
1196      * compose query statements.
1197      *
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.
1203      * @access protected
1204      */
1205     function _quoteInteger($value, $quote, $escape_wildcards)
1206     {
1207         return (int)$value;
1208     }
1209
1210     // }}}
1211     // {{{ _quoteText()
1212
1213     /**
1214      * Convert a text value into a DBMS specific format that is suitable to
1215      * compose query statements.
1216      *
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.
1222      * @access protected
1223      */
1224     function _quoteText($value, $quote, $escape_wildcards)
1225     {
1226         if (!$quote) {
1227             return $value;
1228         }
1229
1230         $db = $this->getDBInstance();
1231         if (MDB2::isError($db)) {
1232             return $db;
1233         }
1234
1235         $value = $db->escape($value, $escape_wildcards);
1236         if (MDB2::isError($value)) {
1237             return $value;
1238         }
1239         return "'".$value."'";
1240     }
1241
1242     // }}}
1243     // {{{ _readFile()
1244
1245     /**
1246      * Convert a text value into a DBMS specific format that is suitable to
1247      * compose query statements.
1248      *
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.
1252      * @access protected
1253      */
1254     function _readFile($value)
1255     {
1256         $close = false;
1257         if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1258             $close = true;
1259             if (strtolower($match[1]) == 'file://') {
1260                 $value = $match[2];
1261             }
1262             $value = @fopen($value, 'r');
1263         }
1264
1265         if (is_resource($value)) {
1266             $db = $this->getDBInstance();
1267             if (MDB2::isError($db)) {
1268                 return $db;
1269             }
1270
1271             $fp = $value;
1272             $value = '';
1273             while (!@feof($fp)) {
1274                 $value.= @fread($fp, $db->options['lob_buffer_length']);
1275             }
1276             if ($close) {
1277                 @fclose($fp);
1278             }
1279         }
1280
1281         return $value;
1282     }
1283
1284     // }}}
1285     // {{{ _quoteLOB()
1286
1287     /**
1288      * Convert a text value into a DBMS specific format that is suitable to
1289      * compose query statements.
1290      *
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.
1296      * @access protected
1297      */
1298     function _quoteLOB($value, $quote, $escape_wildcards)
1299     {
1300         $db = $this->getDBInstance();
1301         if (MDB2::isError($db)) {
1302             return $db;
1303         }
1304         if ($db->options['lob_allow_url_include']) {
1305             $value = $this->_readFile($value);
1306             if (MDB2::isError($value)) {
1307                 return $value;
1308             }
1309         }
1310         return $this->_quoteText($value, $quote, $escape_wildcards);
1311     }
1312
1313     // }}}
1314     // {{{ _quoteCLOB()
1315
1316     /**
1317      * Convert a text value into a DBMS specific format that is suitable to
1318      * compose query statements.
1319      *
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.
1325      * @access protected
1326      */
1327     function _quoteCLOB($value, $quote, $escape_wildcards)
1328     {
1329         return $this->_quoteLOB($value, $quote, $escape_wildcards);
1330     }
1331
1332     // }}}
1333     // {{{ _quoteBLOB()
1334
1335     /**
1336      * Convert a text value into a DBMS specific format that is suitable to
1337      * compose query statements.
1338      *
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.
1344      * @access protected
1345      */
1346     function _quoteBLOB($value, $quote, $escape_wildcards)
1347     {
1348         return $this->_quoteLOB($value, $quote, $escape_wildcards);
1349     }
1350
1351     // }}}
1352     // {{{ _quoteBoolean()
1353
1354     /**
1355      * Convert a text value into a DBMS specific format that is suitable to
1356      * compose query statements.
1357      *
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.
1363      * @access protected
1364      */
1365     function _quoteBoolean($value, $quote, $escape_wildcards)
1366     {
1367         return ($value ? 1 : 0);
1368     }
1369
1370     // }}}
1371     // {{{ _quoteDate()
1372
1373     /**
1374      * Convert a text value into a DBMS specific format that is suitable to
1375      * compose query statements.
1376      *
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.
1382      * @access protected
1383      */
1384     function _quoteDate($value, $quote, $escape_wildcards)
1385     {
1386         if ($value === 'CURRENT_DATE') {
1387             $db = $this->getDBInstance();
1388             if (MDB2::isError($db)) {
1389                 return $db;
1390             }
1391             if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1392                 return $db->function->now('date');
1393             }
1394             return 'CURRENT_DATE';
1395         }
1396         return $this->_quoteText($value, $quote, $escape_wildcards);
1397     }
1398
1399     // }}}
1400     // {{{ _quoteTimestamp()
1401
1402     /**
1403      * Convert a text value into a DBMS specific format that is suitable to
1404      * compose query statements.
1405      *
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.
1411      * @access protected
1412      */
1413     function _quoteTimestamp($value, $quote, $escape_wildcards)
1414     {
1415         if ($value === 'CURRENT_TIMESTAMP') {
1416             $db = $this->getDBInstance();
1417             if (MDB2::isError($db)) {
1418                 return $db;
1419             }
1420             if (isset($db->function) && is_object($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1421                 return $db->function->now('timestamp');
1422             }
1423             return 'CURRENT_TIMESTAMP';
1424         }
1425         return $this->_quoteText($value, $quote, $escape_wildcards);
1426     }
1427
1428     // }}}
1429     // {{{ _quoteTime()
1430
1431     /**
1432      * Convert a text value into a DBMS specific format that is suitable to
1433      *       compose query statements.
1434      *
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.
1440      * @access protected
1441      */
1442     function _quoteTime($value, $quote, $escape_wildcards)
1443     {
1444         if ($value === 'CURRENT_TIME') {
1445             $db = $this->getDBInstance();
1446             if (MDB2::isError($db)) {
1447                 return $db;
1448             }
1449             if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1450                 return $db->function->now('time');
1451             }
1452             return 'CURRENT_TIME';
1453         }
1454         return $this->_quoteText($value, $quote, $escape_wildcards);
1455     }
1456
1457     // }}}
1458     // {{{ _quoteFloat()
1459
1460     /**
1461      * Convert a text value into a DBMS specific format that is suitable to
1462      * compose query statements.
1463      *
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.
1469      * @access protected
1470      */
1471     function _quoteFloat($value, $quote, $escape_wildcards)
1472     {
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;
1478         } else {
1479             $value = $this->_quoteDecimal($value, $quote, $escape_wildcards);
1480         }
1481         return $value;
1482     }
1483
1484     // }}}
1485     // {{{ _quoteDecimal()
1486
1487     /**
1488      * Convert a text value into a DBMS specific format that is suitable to
1489      * compose query statements.
1490      *
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.
1496      * @access protected
1497      */
1498     function _quoteDecimal($value, $quote, $escape_wildcards)
1499     {
1500         $value = (string)$value;
1501         $value = preg_replace('/[^\d\.,\-+eE]/', '', $value);
1502         if (preg_match('/[^\.\d]/', $value)) {
1503             if (strpos($value, ',')) {
1504                 // 1000,00
1505                 if (!strpos($value, '.')) {
1506                     // convert the last "," to a "."
1507                     $value = strrev(str_replace(',', '.', strrev($value)));
1508                 // 1.000,00
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)));
1513                 // 1,000.00
1514                 } else {
1515                     $value = str_replace(',', '', $value);
1516                 }
1517             }
1518         }
1519         return $value;
1520     }
1521
1522     // }}}
1523     // {{{ writeLOBToFile()
1524
1525     /**
1526      * retrieve LOB from the database
1527      *
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
1531      * @access protected
1532      */
1533     function writeLOBToFile($lob, $file)
1534     {
1535         $db = $this->getDBInstance();
1536         if (MDB2::isError($db)) {
1537             return $db;
1538         }
1539
1540         if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
1541             if ($match[1] == 'file://') {
1542                 $file = $match[2];
1543             }
1544         }
1545
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) {
1551                 @fclose($fp);
1552                 return $db->raiseError(MDB2_ERROR, null, null,
1553                     'could not write to the output file', __FUNCTION__);
1554             }
1555         }
1556         @fclose($fp);
1557         return MDB2_OK;
1558     }
1559
1560     // }}}
1561     // {{{ _retrieveLOB()
1562
1563     /**
1564      * retrieve LOB from the database
1565      *
1566      * @param array $lob array
1567      * @return mixed MDB2_OK on success, a MDB2 error on failure
1568      * @access protected
1569      */
1570     function _retrieveLOB(&$lob)
1571     {
1572         if (null === $lob['value']) {
1573             $lob['value'] = $lob['resource'];
1574         }
1575         $lob['loaded'] = true;
1576         return MDB2_OK;
1577     }
1578
1579     // }}}
1580     // {{{ readLOB()
1581
1582     /**
1583      * Read data from large object input stream.
1584      *
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.
1592      * @access public
1593      * @see endOfLOB()
1594      */
1595     function _readLOB($lob, $length)
1596     {
1597         return substr($lob['value'], $lob['position'], $length);
1598     }
1599
1600     // }}}
1601     // {{{ _endOfLOB()
1602
1603     /**
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.
1606      *
1607      * @param array $lob array
1608      * @return mixed true or false on success, a MDB2 error on failure
1609      * @access protected
1610      */
1611     function _endOfLOB($lob)
1612     {
1613         return $lob['endOfLOB'];
1614     }
1615
1616     // }}}
1617     // {{{ destroyLOB()
1618
1619     /**
1620      * Free any resources allocated during the lifetime of the large object
1621      * handler object.
1622      *
1623      * @param resource $lob stream handle
1624      * @access public
1625      */
1626     function destroyLOB($lob)
1627     {
1628         $lob_data = stream_get_meta_data($lob);
1629         $lob_index = $lob_data['wrapper_data']->lob_index;
1630         fclose($lob);
1631         if (isset($this->lobs[$lob_index])) {
1632             $this->_destroyLOB($this->lobs[$lob_index]);
1633             unset($this->lobs[$lob_index]);
1634         }
1635         return MDB2_OK;
1636     }
1637
1638     // }}}
1639     // {{{ _destroyLOB()
1640
1641     /**
1642      * Free any resources allocated during the lifetime of the large object
1643      * handler object.
1644      *
1645      * @param array $lob array
1646      * @access private
1647      */
1648     function _destroyLOB(&$lob)
1649     {
1650         return MDB2_OK;
1651     }
1652
1653     // }}}
1654     // {{{ implodeArray()
1655
1656     /**
1657      * apply a type to all values of an array and return as a comma seperated string
1658      * useful for generating IN statements
1659      *
1660      * @access public
1661      *
1662      * @param array $array data array
1663      * @param string $type determines type of the field
1664      *
1665      * @return string comma seperated values
1666      */
1667     function implodeArray($array, $type = false)
1668     {
1669         if (!is_array($array) || empty($array)) {
1670             return 'NULL';
1671         }
1672         if ($type) {
1673             foreach ($array as $value) {
1674                 $return[] = $this->quote($value, $type);
1675             }
1676         } else {
1677             $return = $array;
1678         }
1679         return implode(', ', $return);
1680     }
1681
1682     // }}}
1683     // {{{ matchPattern()
1684
1685     /**
1686      * build a pattern matching string
1687      *
1688      * @access public
1689      *
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)
1694      *
1695      * @return string SQL pattern
1696      */
1697     function matchPattern($pattern, $operator = null, $field = null)
1698     {
1699         $db = $this->getDBInstance();
1700         if (MDB2::isError($db)) {
1701             return $db;
1702         }
1703
1704         $match = '';
1705         if (null !== $operator) {
1706             $operator = strtoupper($operator);
1707             switch ($operator) {
1708             // case insensitive
1709             case 'ILIKE':
1710                 if (null === $field) {
1711                     return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1712                         'case insensitive LIKE matching requires passing the field name', __FUNCTION__);
1713                 }
1714                 $db->loadModule('Function', null, true);
1715                 $match = $db->function->lower($field).' LIKE ';
1716                 break;
1717             case 'NOT ILIKE':
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__);
1721                 }
1722                 $db->loadModule('Function', null, true);
1723                 $match = $db->function->lower($field).' NOT LIKE ';
1724                 break;
1725             // case sensitive
1726             case 'LIKE':
1727                 $match = (null === $field) ? 'LIKE ' : ($field.' LIKE ');
1728                 break;
1729             case 'NOT LIKE':
1730                 $match = (null === $field) ? 'NOT LIKE ' : ($field.' NOT LIKE ');
1731                 break;
1732             default:
1733                 return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1734                     'not a supported operator type:'. $operator, __FUNCTION__);
1735             }
1736         }
1737         $match.= "'";
1738         foreach ($pattern as $key => $value) {
1739             if ($key % 2) {
1740                 $match.= $value;
1741             } else {
1742                 $escaped = $db->escape($value);
1743                 if (MDB2::isError($escaped)) {
1744                     return $escaped;
1745                 }
1746                 $match.= $db->escapePattern($escaped);
1747             }
1748         }
1749         $match.= "'";
1750         $match.= $this->patternEscapeString();
1751         return $match;
1752     }
1753
1754     // }}}
1755     // {{{ patternEscapeString()
1756
1757     /**
1758      * build string to define pattern escape character
1759      *
1760      * @access public
1761      *
1762      * @return string define pattern escape character
1763      */
1764     function patternEscapeString()
1765     {
1766         return '';
1767     }
1768
1769     // }}}
1770     // {{{ mapNativeDatatype()
1771
1772     /**
1773      * Maps a native array description of a field to a MDB2 datatype and length
1774      *
1775      * @param array  $field native field description
1776      * @return array containing the various possible types, length, sign, fixed
1777      * @access public
1778      */
1779     function mapNativeDatatype($field)
1780     {
1781         $db = $this->getDBInstance();
1782         if (MDB2::isError($db)) {
1783             return $db;
1784         }
1785
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));
1791         }
1792
1793         // Otherwise perform the built-in (i.e. normal) MDB2 native type to
1794         // MDB2 datatype conversion
1795         return $this->_mapNativeDatatype($field);
1796     }
1797
1798     // }}}
1799     // {{{ _mapNativeDatatype()
1800
1801     /**
1802      * Maps a native array description of a field to a MDB2 datatype and length
1803      *
1804      * @param array  $field native field description
1805      * @return array containing the various possible types, length, sign, fixed
1806      * @access public
1807      */
1808     function _mapNativeDatatype($field)
1809     {
1810         $db = $this->getDBInstance();
1811         if (MDB2::isError($db)) {
1812             return $db;
1813         }
1814
1815         return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1816             'method not implemented', __FUNCTION__);
1817     }
1818
1819     // }}}
1820     // {{{ mapPrepareDatatype()
1821
1822     /**
1823      * Maps an mdb2 datatype to mysqli prepare type
1824      *
1825      * @param string $type
1826      * @return string
1827      * @access public
1828      */
1829     function mapPrepareDatatype($type)
1830     {
1831         $db = $this->getDBInstance();
1832         if (MDB2::isError($db)) {
1833             return $db;
1834         }
1835
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));
1841             }
1842         }
1843
1844         return $type;
1845     }
1846 }
1847 ?>