source: branches/version-2_5-dev/data/module/MDB2/Driver/Datatype/Common.php @ 20119

Revision 20119, 61.8 KB checked in by nanasess, 13 years ago (diff)

module 以下は svn:keywords を除外

  • Property svn:eol-style set to LF
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
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,v 1.139 2008/12/04 11:50:42 afz Exp $
46
47require_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 */
65class 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 (PEAR::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 (PEAR::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 (PEAR::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 (is_null($value)) {
236            return null;
237        }
238        $db =& $this->getDBInstance();
239        if (PEAR::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        foreach ($row as $key => $value) {
268            if (empty($types[$key])) {
269                continue;
270            }
271            $value = $this->convertResult($row[$key], $types[$key], $rtrim);
272            if (PEAR::isError($value)) {
273                return $value;
274            }
275            $row[$key] = $value;
276        }
277        return $row;
278    }
279
280    // }}}
281    // {{{ _sortResultFieldTypes()
282
283    /**
284     * convert a result row
285     *
286     * @param array $types
287     * @param array $row specifies the types to convert to
288     * @param bool   $rtrim   if to rtrim text values or not
289     * @return mixed MDB2_OK on success,  a MDB2 error on failure
290     * @access public
291     */
292    function _sortResultFieldTypes($columns, $types)
293    {
294        $n_cols = count($columns);
295        $n_types = count($types);
296        if ($n_cols > $n_types) {
297            for ($i= $n_cols - $n_types; $i >= 0; $i--) {
298                $types[] = null;
299            }
300        }
301        $sorted_types = array();
302        foreach ($columns as $col) {
303            $sorted_types[$col] = null;
304        }
305        foreach ($types as $name => $type) {
306            if (array_key_exists($name, $sorted_types)) {
307                $sorted_types[$name] = $type;
308                unset($types[$name]);
309            }
310        }
311        // if there are left types in the array, fill the null values of the
312        // sorted array with them, in order.
313        if (count($types)) {
314            reset($types);
315            foreach (array_keys($sorted_types) as $k) {
316                if (is_null($sorted_types[$k])) {
317                    $sorted_types[$k] = current($types);
318                    next($types);
319                }
320            }
321        }
322        return $sorted_types;
323    }
324
325    // }}}
326    // {{{ getDeclaration()
327
328    /**
329     * Obtain DBMS specific SQL code portion needed to declare
330     * of the given type
331     *
332     * @param string $type type to which the value should be converted to
333     * @param string  $name   name the field to be declared.
334     * @param string  $field  definition of the field
335     * @return string  DBMS specific SQL code portion that should be used to
336     *                 declare the specified field.
337     * @access public
338     */
339    function getDeclaration($type, $name, $field)
340    {
341        $db =& $this->getDBInstance();
342        if (PEAR::isError($db)) {
343            return $db;
344        }
345
346        if (!empty($db->options['datatype_map'][$type])) {
347            $type = $db->options['datatype_map'][$type];
348            if (!empty($db->options['datatype_map_callback'][$type])) {
349                $parameter = array('type' => $type, 'name' => $name, 'field' => $field);
350                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
351            }
352            $field['type'] = $type;
353        }
354
355        if (!method_exists($this, "_get{$type}Declaration")) {
356            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
357                'type not defined: '.$type, __FUNCTION__);
358        }
359        return $this->{"_get{$type}Declaration"}($name, $field);
360    }
361
362    // }}}
363    // {{{ getTypeDeclaration()
364
365    /**
366     * Obtain DBMS specific SQL code portion needed to declare an text type
367     * field to be used in statements like CREATE TABLE.
368     *
369     * @param array $field  associative array with the name of the properties
370     *      of the field being declared as array indexes. Currently, the types
371     *      of supported field properties are as follows:
372     *
373     *      length
374     *          Integer value that determines the maximum length of the text
375     *          field. If this argument is missing the field should be
376     *          declared to have the longest length allowed by the DBMS.
377     *
378     *      default
379     *          Text value to be used as default for this field.
380     *
381     *      notnull
382     *          Boolean flag that indicates whether this field is constrained
383     *          to not be set to null.
384     * @return string  DBMS specific SQL code portion that should be used to
385     *      declare the specified field.
386     * @access public
387     */
388    function getTypeDeclaration($field)
389    {
390        $db =& $this->getDBInstance();
391        if (PEAR::isError($db)) {
392            return $db;
393        }
394
395        switch ($field['type']) {
396        case 'text':
397            $length = !empty($field['length']) ? $field['length'] : $db->options['default_text_field_length'];
398            $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
399            return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
400                : ($length ? 'VARCHAR('.$length.')' : 'TEXT');
401        case 'clob':
402            return 'TEXT';
403        case 'blob':
404            return 'TEXT';
405        case 'integer':
406            return 'INT';
407        case 'boolean':
408            return 'INT';
409        case 'date':
410            return 'CHAR ('.strlen('YYYY-MM-DD').')';
411        case 'time':
412            return 'CHAR ('.strlen('HH:MM:SS').')';
413        case 'timestamp':
414            return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
415        case 'float':
416            return 'TEXT';
417        case 'decimal':
418            return 'TEXT';
419        }
420        return '';
421    }
422
423    // }}}
424    // {{{ _getDeclaration()
425
426    /**
427     * Obtain DBMS specific SQL code portion needed to declare a generic type
428     * field to be used in statements like CREATE TABLE.
429     *
430     * @param string $name   name the field to be declared.
431     * @param array  $field  associative array with the name of the properties
432     *      of the field being declared as array indexes. Currently, the types
433     *      of supported field properties are as follows:
434     *
435     *      length
436     *          Integer value that determines the maximum length of the text
437     *          field. If this argument is missing the field should be
438     *          declared to have the longest length allowed by the DBMS.
439     *
440     *      default
441     *          Text value to be used as default for this field.
442     *
443     *      notnull
444     *          Boolean flag that indicates whether this field is constrained
445     *          to not be set to null.
446     *      charset
447     *          Text value with the default CHARACTER SET for this field.
448     *      collation
449     *          Text value with the default COLLATION for this field.
450     * @return string  DBMS specific SQL code portion that should be used to
451     *      declare the specified field, or a MDB2_Error on failure
452     * @access protected
453     */
454    function _getDeclaration($name, $field)
455    {
456        $db =& $this->getDBInstance();
457        if (PEAR::isError($db)) {
458            return $db;
459        }
460
461        $name = $db->quoteIdentifier($name, true);
462        $declaration_options = $db->datatype->_getDeclarationOptions($field);
463        if (PEAR::isError($declaration_options)) {
464            return $declaration_options;
465        }
466        return $name.' '.$this->getTypeDeclaration($field).$declaration_options;
467    }
468
469    // }}}
470    // {{{ _getDeclarationOptions()
471
472    /**
473     * Obtain DBMS specific SQL code portion needed to declare a generic type
474     * field to be used in statement like CREATE TABLE, without the field name
475     * and type values (ie. just the character set, default value, if the
476     * field is permitted to be NULL or not, and the collation options).
477     *
478     * @param array  $field  associative array with the name of the properties
479     *      of the field being declared as array indexes. Currently, the types
480     *      of supported field properties are as follows:
481     *
482     *      default
483     *          Text value to be used as default for this field.
484     *      notnull
485     *          Boolean flag that indicates whether this field is constrained
486     *          to not be set to null.
487     *      charset
488     *          Text value with the default CHARACTER SET for this field.
489     *      collation
490     *          Text value with the default COLLATION for this field.
491     * @return string  DBMS specific SQL code portion that should be used to
492     *      declare the specified field's options.
493     * @access protected
494     */
495    function _getDeclarationOptions($field)
496    {
497        $charset = empty($field['charset']) ? '' :
498            ' '.$this->_getCharsetFieldDeclaration($field['charset']);
499
500        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
501        $default = '';
502        if (array_key_exists('default', $field)) {
503            if ($field['default'] === '') {
504                $db =& $this->getDBInstance();
505                if (PEAR::isError($db)) {
506                    return $db;
507                }
508                $valid_default_values = $this->getValidTypes();
509                $field['default'] = $valid_default_values[$field['type']];
510                if ($field['default'] === ''&& ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)) {
511                    $field['default'] = ' ';
512                }
513            }
514            if (!is_null($field['default'])) {
515                $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
516            }
517        }
518
519        $collation = empty($field['collation']) ? '' :
520            ' '.$this->_getCollationFieldDeclaration($field['collation']);
521
522        return $charset.$default.$notnull.$collation;
523    }
524
525    // }}}
526    // {{{ _getCharsetFieldDeclaration()
527   
528    /**
529     * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
530     * of a field declaration to be used in statements like CREATE TABLE.
531     *
532     * @param string $charset   name of the charset
533     * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
534     *                 of a field declaration.
535     */
536    function _getCharsetFieldDeclaration($charset)
537    {
538        return '';
539    }
540
541    // }}}
542    // {{{ _getCollationFieldDeclaration()
543
544    /**
545     * Obtain DBMS specific SQL code portion needed to set the COLLATION
546     * of a field declaration to be used in statements like CREATE TABLE.
547     *
548     * @param string $collation   name of the collation
549     * @return string  DBMS specific SQL code portion needed to set the COLLATION
550     *                 of a field declaration.
551     */
552    function _getCollationFieldDeclaration($collation)
553    {
554        return '';
555    }
556
557    // }}}
558    // {{{ _getIntegerDeclaration()
559
560    /**
561     * Obtain DBMS specific SQL code portion needed to declare an integer type
562     * field to be used in statements like CREATE TABLE.
563     *
564     * @param string $name name the field to be declared.
565     * @param array $field associative array with the name of the properties
566     *       of the field being declared as array indexes. Currently, the types
567     *       of supported field properties are as follows:
568     *
569     *       unsigned
570     *           Boolean flag that indicates whether the field should be
571     *           declared as unsigned integer if possible.
572     *
573     *       default
574     *           Integer value to be used as default for this field.
575     *
576     *       notnull
577     *           Boolean flag that indicates whether this field is constrained
578     *           to not be set to null.
579     * @return string DBMS specific SQL code portion that should be used to
580     *       declare the specified field.
581     * @access protected
582     */
583    function _getIntegerDeclaration($name, $field)
584    {
585        if (!empty($field['unsigned'])) {
586            $db =& $this->getDBInstance();
587            if (PEAR::isError($db)) {
588                return $db;
589            }
590
591            $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
592        }
593        return $this->_getDeclaration($name, $field);
594    }
595
596    // }}}
597    // {{{ _getTextDeclaration()
598
599    /**
600     * Obtain DBMS specific SQL code portion needed to declare an text type
601     * field to be used in statements like CREATE TABLE.
602     *
603     * @param string $name name the field to be declared.
604     * @param array $field associative array with the name of the properties
605     *       of the field being declared as array indexes. Currently, the types
606     *       of supported field properties are as follows:
607     *
608     *       length
609     *           Integer value that determines the maximum length of the text
610     *           field. If this argument is missing the field should be
611     *           declared to have the longest length allowed by the DBMS.
612     *
613     *       default
614     *           Text value to be used as default for this field.
615     *
616     *       notnull
617     *           Boolean flag that indicates whether this field is constrained
618     *           to not be set to null.
619     * @return string DBMS specific SQL code portion that should be used to
620     *       declare the specified field.
621     * @access protected
622     */
623    function _getTextDeclaration($name, $field)
624    {
625        return $this->_getDeclaration($name, $field);
626    }
627
628    // }}}
629    // {{{ _getCLOBDeclaration()
630
631    /**
632     * Obtain DBMS specific SQL code portion needed to declare an character
633     * large object type field to be used in statements like CREATE TABLE.
634     *
635     * @param string $name name the field to be declared.
636     * @param array $field associative array with the name of the properties
637     *        of the field being declared as array indexes. Currently, the types
638     *        of supported field properties are as follows:
639     *
640     *        length
641     *            Integer value that determines the maximum length of the large
642     *            object field. If this argument is missing the field should be
643     *            declared to have the longest length allowed by the DBMS.
644     *
645     *        notnull
646     *            Boolean flag that indicates whether this field is constrained
647     *            to not be set to null.
648     * @return string DBMS specific SQL code portion that should be used to
649     *        declare the specified field.
650     * @access public
651     */
652    function _getCLOBDeclaration($name, $field)
653    {
654        $db =& $this->getDBInstance();
655        if (PEAR::isError($db)) {
656            return $db;
657        }
658
659        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
660        $name = $db->quoteIdentifier($name, true);
661        return $name.' '.$this->getTypeDeclaration($field).$notnull;
662    }
663
664    // }}}
665    // {{{ _getBLOBDeclaration()
666
667    /**
668     * Obtain DBMS specific SQL code portion needed to declare an binary large
669     * object type field to be used in statements like CREATE TABLE.
670     *
671     * @param string $name name the field to be declared.
672     * @param array $field associative array with the name of the properties
673     *        of the field being declared as array indexes. Currently, the types
674     *        of supported field properties are as follows:
675     *
676     *        length
677     *            Integer value that determines the maximum length of the large
678     *            object field. If this argument is missing the field should be
679     *            declared to have the longest length allowed by the DBMS.
680     *
681     *        notnull
682     *            Boolean flag that indicates whether this field is constrained
683     *            to not be set to null.
684     * @return string DBMS specific SQL code portion that should be used to
685     *        declare the specified field.
686     * @access protected
687     */
688    function _getBLOBDeclaration($name, $field)
689    {
690        $db =& $this->getDBInstance();
691        if (PEAR::isError($db)) {
692            return $db;
693        }
694
695        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
696        $name = $db->quoteIdentifier($name, true);
697        return $name.' '.$this->getTypeDeclaration($field).$notnull;
698    }
699
700    // }}}
701    // {{{ _getBooleanDeclaration()
702
703    /**
704     * Obtain DBMS specific SQL code portion needed to declare a boolean type
705     * field to be used in statements like CREATE TABLE.
706     *
707     * @param string $name name the field to be declared.
708     * @param array $field associative array with the name of the properties
709     *       of the field being declared as array indexes. Currently, the types
710     *       of supported field properties are as follows:
711     *
712     *       default
713     *           Boolean value to be used as default for this field.
714     *
715     *       notnullL
716     *           Boolean flag that indicates whether this field is constrained
717     *           to not be set to null.
718     * @return string DBMS specific SQL code portion that should be used to
719     *       declare the specified field.
720     * @access protected
721     */
722    function _getBooleanDeclaration($name, $field)
723    {
724        return $this->_getDeclaration($name, $field);
725    }
726
727    // }}}
728    // {{{ _getDateDeclaration()
729
730    /**
731     * Obtain DBMS specific SQL code portion needed to declare a date type
732     * field to be used in statements like CREATE TABLE.
733     *
734     * @param string $name name the field to be declared.
735     * @param array $field associative array with the name of the properties
736     *       of the field being declared as array indexes. Currently, the types
737     *       of supported field properties are as follows:
738     *
739     *       default
740     *           Date value to be used as default for this field.
741     *
742     *       notnull
743     *           Boolean flag that indicates whether this field is constrained
744     *           to not be set to null.
745     * @return string DBMS specific SQL code portion that should be used to
746     *       declare the specified field.
747     * @access protected
748     */
749    function _getDateDeclaration($name, $field)
750    {
751        return $this->_getDeclaration($name, $field);
752    }
753
754    // }}}
755    // {{{ _getTimestampDeclaration()
756
757    /**
758     * Obtain DBMS specific SQL code portion needed to declare a timestamp
759     * field to be used in statements like CREATE TABLE.
760     *
761     * @param string $name name the field to be declared.
762     * @param array $field associative array with the name of the properties
763     *       of the field being declared as array indexes. Currently, the types
764     *       of supported field properties are as follows:
765     *
766     *       default
767     *           Timestamp value to be used as default for this field.
768     *
769     *       notnull
770     *           Boolean flag that indicates whether this field is constrained
771     *           to not be set to null.
772     * @return string DBMS specific SQL code portion that should be used to
773     *       declare the specified field.
774     * @access protected
775     */
776    function _getTimestampDeclaration($name, $field)
777    {
778        return $this->_getDeclaration($name, $field);
779    }
780
781    // }}}
782    // {{{ _getTimeDeclaration()
783
784    /**
785     * Obtain DBMS specific SQL code portion needed to declare a time
786     * field to be used in statements like CREATE TABLE.
787     *
788     * @param string $name name the field to be declared.
789     * @param array $field associative array with the name of the properties
790     *       of the field being declared as array indexes. Currently, the types
791     *       of supported field properties are as follows:
792     *
793     *       default
794     *           Time value to be used as default for this field.
795     *
796     *       notnull
797     *           Boolean flag that indicates whether this field is constrained
798     *           to not be set to null.
799     * @return string DBMS specific SQL code portion that should be used to
800     *       declare the specified field.
801     * @access protected
802     */
803    function _getTimeDeclaration($name, $field)
804    {
805        return $this->_getDeclaration($name, $field);
806    }
807
808    // }}}
809    // {{{ _getFloatDeclaration()
810
811    /**
812     * Obtain DBMS specific SQL code portion needed to declare a float type
813     * field to be used in statements like CREATE TABLE.
814     *
815     * @param string $name name the field to be declared.
816     * @param array $field associative array with the name of the properties
817     *       of the field being declared as array indexes. Currently, the types
818     *       of supported field properties are as follows:
819     *
820     *       default
821     *           Float value to be used as default for this field.
822     *
823     *       notnull
824     *           Boolean flag that indicates whether this field is constrained
825     *           to not be set to null.
826     * @return string DBMS specific SQL code portion that should be used to
827     *       declare the specified field.
828     * @access protected
829     */
830    function _getFloatDeclaration($name, $field)
831    {
832        return $this->_getDeclaration($name, $field);
833    }
834
835    // }}}
836    // {{{ _getDecimalDeclaration()
837
838    /**
839     * Obtain DBMS specific SQL code portion needed to declare a decimal type
840     * field to be used in statements like CREATE TABLE.
841     *
842     * @param string $name name the field to be declared.
843     * @param array $field associative array with the name of the properties
844     *       of the field being declared as array indexes. Currently, the types
845     *       of supported field properties are as follows:
846     *
847     *       default
848     *           Decimal value to be used as default for this field.
849     *
850     *       notnull
851     *           Boolean flag that indicates whether this field is constrained
852     *           to not be set to null.
853     * @return string DBMS specific SQL code portion that should be used to
854     *       declare the specified field.
855     * @access protected
856     */
857    function _getDecimalDeclaration($name, $field)
858    {
859        return $this->_getDeclaration($name, $field);
860    }
861
862    // }}}
863    // {{{ compareDefinition()
864
865    /**
866     * Obtain an array of changes that may need to applied
867     *
868     * @param array $current new definition
869     * @param array  $previous old definition
870     * @return array  containing all changes that will need to be applied
871     * @access public
872     */
873    function compareDefinition($current, $previous)
874    {
875        $type = !empty($current['type']) ? $current['type'] : null;
876
877        if (!method_exists($this, "_compare{$type}Definition")) {
878            $db =& $this->getDBInstance();
879            if (PEAR::isError($db)) {
880                return $db;
881            }
882            if (!empty($db->options['datatype_map_callback'][$type])) {
883                $parameter = array('current' => $current, 'previous' => $previous);
884                $change =  call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
885                return $change;
886            }
887            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
888                'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
889        }
890
891        if (empty($previous['type']) || $previous['type'] != $type) {
892            return $current;
893        }
894
895        $change = $this->{"_compare{$type}Definition"}($current, $previous);
896
897        if ($previous['type'] != $type) {
898            $change['type'] = true;
899        }
900
901        $previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
902        $notnull = !empty($current['notnull']) ? $current['notnull'] : false;
903        if ($previous_notnull != $notnull) {
904            $change['notnull'] = true;
905        }
906
907        $previous_default = array_key_exists('default', $previous) ? $previous['default'] :
908            ($previous_notnull ? '' : null);
909        $default = array_key_exists('default', $current) ? $current['default'] :
910            ($notnull ? '' : null);
911        if ($previous_default !== $default) {
912            $change['default'] = true;
913        }
914
915        return $change;
916    }
917
918    // }}}
919    // {{{ _compareIntegerDefinition()
920
921    /**
922     * Obtain an array of changes that may need to applied to an integer field
923     *
924     * @param array $current new definition
925     * @param array  $previous old definition
926     * @return array  containing all changes that will need to be applied
927     * @access protected
928     */
929    function _compareIntegerDefinition($current, $previous)
930    {
931        $change = array();
932        $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false;
933        $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false;
934        if ($previous_unsigned != $unsigned) {
935            $change['unsigned'] = true;
936        }
937        $previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false;
938        $autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false;
939        if ($previous_autoincrement != $autoincrement) {
940            $change['autoincrement'] = true;
941        }
942        return $change;
943    }
944
945    // }}}
946    // {{{ _compareTextDefinition()
947
948    /**
949     * Obtain an array of changes that may need to applied to an text field
950     *
951     * @param array $current new definition
952     * @param array  $previous old definition
953     * @return array  containing all changes that will need to be applied
954     * @access protected
955     */
956    function _compareTextDefinition($current, $previous)
957    {
958        $change = array();
959        $previous_length = !empty($previous['length']) ? $previous['length'] : 0;
960        $length = !empty($current['length']) ? $current['length'] : 0;
961        if ($previous_length != $length) {
962            $change['length'] = true;
963        }
964        $previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0;
965        $fixed = !empty($current['fixed']) ? $current['fixed'] : 0;
966        if ($previous_fixed != $fixed) {
967            $change['fixed'] = true;
968        }
969        return $change;
970    }
971
972    // }}}
973    // {{{ _compareCLOBDefinition()
974
975    /**
976     * Obtain an array of changes that may need to applied to an CLOB field
977     *
978     * @param array $current new definition
979     * @param array  $previous old definition
980     * @return array  containing all changes that will need to be applied
981     * @access protected
982     */
983    function _compareCLOBDefinition($current, $previous)
984    {
985        return $this->_compareTextDefinition($current, $previous);
986    }
987
988    // }}}
989    // {{{ _compareBLOBDefinition()
990
991    /**
992     * Obtain an array of changes that may need to applied to an BLOB field
993     *
994     * @param array $current new definition
995     * @param array  $previous old definition
996     * @return array  containing all changes that will need to be applied
997     * @access protected
998     */
999    function _compareBLOBDefinition($current, $previous)
1000    {
1001        return $this->_compareTextDefinition($current, $previous);
1002    }
1003
1004    // }}}
1005    // {{{ _compareDateDefinition()
1006
1007    /**
1008     * Obtain an array of changes that may need to applied to an date field
1009     *
1010     * @param array $current new definition
1011     * @param array  $previous old definition
1012     * @return array  containing all changes that will need to be applied
1013     * @access protected
1014     */
1015    function _compareDateDefinition($current, $previous)
1016    {
1017        return array();
1018    }
1019
1020    // }}}
1021    // {{{ _compareTimeDefinition()
1022
1023    /**
1024     * Obtain an array of changes that may need to applied to an time field
1025     *
1026     * @param array $current new definition
1027     * @param array  $previous old definition
1028     * @return array  containing all changes that will need to be applied
1029     * @access protected
1030     */
1031    function _compareTimeDefinition($current, $previous)
1032    {
1033        return array();
1034    }
1035
1036    // }}}
1037    // {{{ _compareTimestampDefinition()
1038
1039    /**
1040     * Obtain an array of changes that may need to applied to an timestamp field
1041     *
1042     * @param array $current new definition
1043     * @param array  $previous old definition
1044     * @return array  containing all changes that will need to be applied
1045     * @access protected
1046     */
1047    function _compareTimestampDefinition($current, $previous)
1048    {
1049        return array();
1050    }
1051
1052    // }}}
1053    // {{{ _compareBooleanDefinition()
1054
1055    /**
1056     * Obtain an array of changes that may need to applied to an boolean field
1057     *
1058     * @param array $current new definition
1059     * @param array  $previous old definition
1060     * @return array  containing all changes that will need to be applied
1061     * @access protected
1062     */
1063    function _compareBooleanDefinition($current, $previous)
1064    {
1065        return array();
1066    }
1067
1068    // }}}
1069    // {{{ _compareFloatDefinition()
1070
1071    /**
1072     * Obtain an array of changes that may need to applied to an float field
1073     *
1074     * @param array $current new definition
1075     * @param array  $previous old definition
1076     * @return array  containing all changes that will need to be applied
1077     * @access protected
1078     */
1079    function _compareFloatDefinition($current, $previous)
1080    {
1081        return array();
1082    }
1083
1084    // }}}
1085    // {{{ _compareDecimalDefinition()
1086
1087    /**
1088     * Obtain an array of changes that may need to applied to an decimal field
1089     *
1090     * @param array $current new definition
1091     * @param array  $previous old definition
1092     * @return array  containing all changes that will need to be applied
1093     * @access protected
1094     */
1095    function _compareDecimalDefinition($current, $previous)
1096    {
1097        return array();
1098    }
1099
1100    // }}}
1101    // {{{ quote()
1102
1103    /**
1104     * Convert a text value into a DBMS specific format that is suitable to
1105     * compose query statements.
1106     *
1107     * @param string $value text string value that is intended to be converted.
1108     * @param string $type type to which the value should be converted to
1109     * @param bool $quote determines if the value should be quoted and escaped
1110     * @param bool $escape_wildcards if to escape escape wildcards
1111     * @return string text string that represents the given argument value in
1112     *       a DBMS specific format.
1113     * @access public
1114     */
1115    function quote($value, $type = null, $quote = true, $escape_wildcards = false)
1116    {
1117        $db =& $this->getDBInstance();
1118        if (PEAR::isError($db)) {
1119            return $db;
1120        }
1121
1122        if (is_null($value)
1123            || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
1124        ) {
1125            if (!$quote) {
1126                return null;
1127            }
1128            return 'NULL';
1129        }
1130
1131        if (is_null($type)) {
1132            switch (gettype($value)) {
1133            case 'integer':
1134                $type = 'integer';
1135                break;
1136            case 'double':
1137                // todo: default to decimal as float is quite unusual
1138                // $type = 'float';
1139                $type = 'decimal';
1140                break;
1141            case 'boolean':
1142                $type = 'boolean';
1143                break;
1144            case 'array':
1145                 $value = serialize($value);
1146            case 'object':
1147                 $type = 'text';
1148                break;
1149            default:
1150                if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
1151                    $type = 'timestamp';
1152                } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
1153                    $type = 'time';
1154                } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
1155                    $type = 'date';
1156                } else {
1157                    $type = 'text';
1158                }
1159                break;
1160            }
1161        } elseif (!empty($db->options['datatype_map'][$type])) {
1162            $type = $db->options['datatype_map'][$type];
1163            if (!empty($db->options['datatype_map_callback'][$type])) {
1164                $parameter = array('type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards);
1165                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
1166            }
1167        }
1168
1169        if (!method_exists($this, "_quote{$type}")) {
1170            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1171                'type not defined: '.$type, __FUNCTION__);
1172        }
1173        $value = $this->{"_quote{$type}"}($value, $quote, $escape_wildcards);
1174        if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern']
1175            && $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern']
1176        ) {
1177            $value.= $this->patternEscapeString();
1178        }
1179        return $value;
1180    }
1181
1182    // }}}
1183    // {{{ _quoteInteger()
1184
1185    /**
1186     * Convert a text value into a DBMS specific format that is suitable to
1187     * compose query statements.
1188     *
1189     * @param string $value text string value that is intended to be converted.
1190     * @param bool $quote determines if the value should be quoted and escaped
1191     * @param bool $escape_wildcards if to escape escape wildcards
1192     * @return string text string that represents the given argument value in
1193     *       a DBMS specific format.
1194     * @access protected
1195     */
1196    function _quoteInteger($value, $quote, $escape_wildcards)
1197    {
1198        return (int)$value;
1199    }
1200
1201    // }}}
1202    // {{{ _quoteText()
1203
1204    /**
1205     * Convert a text value into a DBMS specific format that is suitable to
1206     * compose query statements.
1207     *
1208     * @param string $value text string value that is intended to be converted.
1209     * @param bool $quote determines if the value should be quoted and escaped
1210     * @param bool $escape_wildcards if to escape escape wildcards
1211     * @return string text string that already contains any DBMS specific
1212     *       escaped character sequences.
1213     * @access protected
1214     */
1215    function _quoteText($value, $quote, $escape_wildcards)
1216    {
1217        if (!$quote) {
1218            return $value;
1219        }
1220
1221        $db =& $this->getDBInstance();
1222        if (PEAR::isError($db)) {
1223            return $db;
1224        }
1225
1226        $value = $db->escape($value, $escape_wildcards);
1227        if (PEAR::isError($value)) {
1228            return $value;
1229        }
1230        return "'".$value."'";
1231    }
1232
1233    // }}}
1234    // {{{ _readFile()
1235
1236    /**
1237     * Convert a text value into a DBMS specific format that is suitable to
1238     * compose query statements.
1239     *
1240     * @param string $value text string value that is intended to be converted.
1241     * @return string text string that represents the given argument value in
1242     *       a DBMS specific format.
1243     * @access protected
1244     */
1245    function _readFile($value)
1246    {
1247        $close = false;
1248        if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1249            $close = true;
1250            if ($match[1] == 'file://') {
1251                $value = $match[2];
1252            }
1253            $value = @fopen($value, 'r');
1254        }
1255
1256        if (is_resource($value)) {
1257            $db =& $this->getDBInstance();
1258            if (PEAR::isError($db)) {
1259                return $db;
1260            }
1261
1262            $fp = $value;
1263            $value = '';
1264            while (!@feof($fp)) {
1265                $value.= @fread($fp, $db->options['lob_buffer_length']);
1266            }
1267            if ($close) {
1268                @fclose($fp);
1269            }
1270        }
1271
1272        return $value;
1273    }
1274
1275    // }}}
1276    // {{{ _quoteLOB()
1277
1278    /**
1279     * Convert a text value into a DBMS specific format that is suitable to
1280     * compose query statements.
1281     *
1282     * @param string $value text string value that is intended to be converted.
1283     * @param bool $quote determines if the value should be quoted and escaped
1284     * @param bool $escape_wildcards if to escape escape wildcards
1285     * @return string text string that represents the given argument value in
1286     *       a DBMS specific format.
1287     * @access protected
1288     */
1289    function _quoteLOB($value, $quote, $escape_wildcards)
1290    {
1291        $value = $this->_readFile($value);
1292        if (PEAR::isError($value)) {
1293            return $value;
1294        }
1295        return $this->_quoteText($value, $quote, $escape_wildcards);
1296    }
1297
1298    // }}}
1299    // {{{ _quoteCLOB()
1300
1301    /**
1302     * Convert a text value into a DBMS specific format that is suitable to
1303     * compose query statements.
1304     *
1305     * @param string $value text string value that is intended to be converted.
1306     * @param bool $quote determines if the value should be quoted and escaped
1307     * @param bool $escape_wildcards if to escape escape wildcards
1308     * @return string text string that represents the given argument value in
1309     *       a DBMS specific format.
1310     * @access protected
1311     */
1312    function _quoteCLOB($value, $quote, $escape_wildcards)
1313    {
1314        return $this->_quoteLOB($value, $quote, $escape_wildcards);
1315    }
1316
1317    // }}}
1318    // {{{ _quoteBLOB()
1319
1320    /**
1321     * Convert a text value into a DBMS specific format that is suitable to
1322     * compose query statements.
1323     *
1324     * @param string $value text string value that is intended to be converted.
1325     * @param bool $quote determines if the value should be quoted and escaped
1326     * @param bool $escape_wildcards if to escape escape wildcards
1327     * @return string text string that represents the given argument value in
1328     *       a DBMS specific format.
1329     * @access protected
1330     */
1331    function _quoteBLOB($value, $quote, $escape_wildcards)
1332    {
1333        return $this->_quoteLOB($value, $quote, $escape_wildcards);
1334    }
1335
1336    // }}}
1337    // {{{ _quoteBoolean()
1338
1339    /**
1340     * Convert a text value into a DBMS specific format that is suitable to
1341     * compose query statements.
1342     *
1343     * @param string $value text string value that is intended to be converted.
1344     * @param bool $quote determines if the value should be quoted and escaped
1345     * @param bool $escape_wildcards if to escape escape wildcards
1346     * @return string text string that represents the given argument value in
1347     *       a DBMS specific format.
1348     * @access protected
1349     */
1350    function _quoteBoolean($value, $quote, $escape_wildcards)
1351    {
1352        return ($value ? 1 : 0);
1353    }
1354
1355    // }}}
1356    // {{{ _quoteDate()
1357
1358    /**
1359     * Convert a text value into a DBMS specific format that is suitable to
1360     * compose query statements.
1361     *
1362     * @param string $value text string value that is intended to be converted.
1363     * @param bool $quote determines if the value should be quoted and escaped
1364     * @param bool $escape_wildcards if to escape escape wildcards
1365     * @return string text string that represents the given argument value in
1366     *       a DBMS specific format.
1367     * @access protected
1368     */
1369    function _quoteDate($value, $quote, $escape_wildcards)
1370    {
1371        if ($value === 'CURRENT_DATE') {
1372            $db =& $this->getDBInstance();
1373            if (PEAR::isError($db)) {
1374                return $db;
1375            }
1376            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1377                return $db->function->now('date');
1378            }
1379            return 'CURRENT_DATE';
1380        }
1381        return $this->_quoteText($value, $quote, $escape_wildcards);
1382    }
1383
1384    // }}}
1385    // {{{ _quoteTimestamp()
1386
1387    /**
1388     * Convert a text value into a DBMS specific format that is suitable to
1389     * compose query statements.
1390     *
1391     * @param string $value text string value that is intended to be converted.
1392     * @param bool $quote determines if the value should be quoted and escaped
1393     * @param bool $escape_wildcards if to escape escape wildcards
1394     * @return string text string that represents the given argument value in
1395     *       a DBMS specific format.
1396     * @access protected
1397     */
1398    function _quoteTimestamp($value, $quote, $escape_wildcards)
1399    {
1400        if ($value === 'CURRENT_TIMESTAMP') {
1401            $db =& $this->getDBInstance();
1402            if (PEAR::isError($db)) {
1403                return $db;
1404            }
1405            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1406                return $db->function->now('timestamp');
1407            }
1408            return 'CURRENT_TIMESTAMP';
1409        }
1410        return $this->_quoteText($value, $quote, $escape_wildcards);
1411    }
1412
1413    // }}}
1414    // {{{ _quoteTime()
1415
1416    /**
1417     * Convert a text value into a DBMS specific format that is suitable to
1418     *       compose query statements.
1419     *
1420     * @param string $value text string value that is intended to be converted.
1421     * @param bool $quote determines if the value should be quoted and escaped
1422     * @param bool $escape_wildcards if to escape escape wildcards
1423     * @return string text string that represents the given argument value in
1424     *       a DBMS specific format.
1425     * @access protected
1426     */
1427    function _quoteTime($value, $quote, $escape_wildcards)
1428    {
1429        if ($value === 'CURRENT_TIME') {
1430            $db =& $this->getDBInstance();
1431            if (PEAR::isError($db)) {
1432                return $db;
1433            }
1434            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1435                return $db->function->now('time');
1436            }
1437            return 'CURRENT_TIME';
1438        }
1439        return $this->_quoteText($value, $quote, $escape_wildcards);
1440    }
1441
1442    // }}}
1443    // {{{ _quoteFloat()
1444
1445    /**
1446     * Convert a text value into a DBMS specific format that is suitable to
1447     * compose query statements.
1448     *
1449     * @param string $value text string value that is intended to be converted.
1450     * @param bool $quote determines if the value should be quoted and escaped
1451     * @param bool $escape_wildcards if to escape escape wildcards
1452     * @return string text string that represents the given argument value in
1453     *       a DBMS specific format.
1454     * @access protected
1455     */
1456    function _quoteFloat($value, $quote, $escape_wildcards)
1457    {
1458        if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
1459            $decimal = $this->_quoteDecimal($matches[1], $quote, $escape_wildcards);
1460            $sign = $matches[2];
1461            $exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
1462            $value = $decimal.'E'.$sign.$exponent;
1463        } else {
1464            $value = $this->_quoteDecimal($value, $quote, $escape_wildcards);
1465        }
1466        return $value;
1467    }
1468
1469    // }}}
1470    // {{{ _quoteDecimal()
1471
1472    /**
1473     * Convert a text value into a DBMS specific format that is suitable to
1474     * compose query statements.
1475     *
1476     * @param string $value text string value that is intended to be converted.
1477     * @param bool $quote determines if the value should be quoted and escaped
1478     * @param bool $escape_wildcards if to escape escape wildcards
1479     * @return string text string that represents the given argument value in
1480     *       a DBMS specific format.
1481     * @access protected
1482     */
1483    function _quoteDecimal($value, $quote, $escape_wildcards)
1484    {
1485        $value = (string)$value;
1486        $value = preg_replace('/[^\d\.,\-+eE]/', '', $value);
1487        if (preg_match('/[^\.\d]/', $value)) {
1488            if (strpos($value, ',')) {
1489                // 1000,00
1490                if (!strpos($value, '.')) {
1491                    // convert the last "," to a "."
1492                    $value = strrev(str_replace(',', '.', strrev($value)));
1493                // 1.000,00
1494                } elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) {
1495                    $value = str_replace('.', '', $value);
1496                    // convert the last "," to a "."
1497                    $value = strrev(str_replace(',', '.', strrev($value)));
1498                // 1,000.00
1499                } else {
1500                    $value = str_replace(',', '', $value);
1501                }
1502            }
1503        }
1504        return $value;
1505    }
1506
1507    // }}}
1508    // {{{ writeLOBToFile()
1509
1510    /**
1511     * retrieve LOB from the database
1512     *
1513     * @param resource $lob stream handle
1514     * @param string $file name of the file into which the LOb should be fetched
1515     * @return mixed MDB2_OK on success, a MDB2 error on failure
1516     * @access protected
1517     */
1518    function writeLOBToFile($lob, $file)
1519    {
1520        $db =& $this->getDBInstance();
1521        if (PEAR::isError($db)) {
1522            return $db;
1523        }
1524
1525        if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
1526            if ($match[1] == 'file://') {
1527                $file = $match[2];
1528            }
1529        }
1530
1531        $fp = @fopen($file, 'wb');
1532        while (!@feof($lob)) {
1533            $result = @fread($lob, $db->options['lob_buffer_length']);
1534            $read = strlen($result);
1535            if (@fwrite($fp, $result, $read) != $read) {
1536                @fclose($fp);
1537                return $db->raiseError(MDB2_ERROR, null, null,
1538                    'could not write to the output file', __FUNCTION__);
1539            }
1540        }
1541        @fclose($fp);
1542        return MDB2_OK;
1543    }
1544
1545    // }}}
1546    // {{{ _retrieveLOB()
1547
1548    /**
1549     * retrieve LOB from the database
1550     *
1551     * @param array $lob array
1552     * @return mixed MDB2_OK on success, a MDB2 error on failure
1553     * @access protected
1554     */
1555    function _retrieveLOB(&$lob)
1556    {
1557        if (is_null($lob['value'])) {
1558            $lob['value'] = $lob['resource'];
1559        }
1560        $lob['loaded'] = true;
1561        return MDB2_OK;
1562    }
1563
1564    // }}}
1565    // {{{ readLOB()
1566
1567    /**
1568     * Read data from large object input stream.
1569     *
1570     * @param resource $lob stream handle
1571     * @param string $data reference to a variable that will hold data
1572     *                          to be read from the large object input stream
1573     * @param integer $length    value that indicates the largest ammount ofdata
1574     *                          to be read from the large object input stream.
1575     * @return mixed the effective number of bytes read from the large object
1576     *                      input stream on sucess or an MDB2 error object.
1577     * @access public
1578     * @see endOfLOB()
1579     */
1580    function _readLOB($lob, $length)
1581    {
1582        return substr($lob['value'], $lob['position'], $length);
1583    }
1584
1585    // }}}
1586    // {{{ _endOfLOB()
1587
1588    /**
1589     * Determine whether it was reached the end of the large object and
1590     * therefore there is no more data to be read for the its input stream.
1591     *
1592     * @param array $lob array
1593     * @return mixed true or false on success, a MDB2 error on failure
1594     * @access protected
1595     */
1596    function _endOfLOB($lob)
1597    {
1598        return $lob['endOfLOB'];
1599    }
1600
1601    // }}}
1602    // {{{ destroyLOB()
1603
1604    /**
1605     * Free any resources allocated during the lifetime of the large object
1606     * handler object.
1607     *
1608     * @param resource $lob stream handle
1609     * @access public
1610     */
1611    function destroyLOB($lob)
1612    {
1613        $lob_data = stream_get_meta_data($lob);
1614        $lob_index = $lob_data['wrapper_data']->lob_index;
1615        fclose($lob);
1616        if (isset($this->lobs[$lob_index])) {
1617            $this->_destroyLOB($this->lobs[$lob_index]);
1618            unset($this->lobs[$lob_index]);
1619        }
1620        return MDB2_OK;
1621    }
1622
1623    // }}}
1624    // {{{ _destroyLOB()
1625
1626    /**
1627     * Free any resources allocated during the lifetime of the large object
1628     * handler object.
1629     *
1630     * @param array $lob array
1631     * @access private
1632     */
1633    function _destroyLOB(&$lob)
1634    {
1635        return MDB2_OK;
1636    }
1637
1638    // }}}
1639    // {{{ implodeArray()
1640
1641    /**
1642     * apply a type to all values of an array and return as a comma seperated string
1643     * useful for generating IN statements
1644     *
1645     * @access public
1646     *
1647     * @param array $array data array
1648     * @param string $type determines type of the field
1649     *
1650     * @return string comma seperated values
1651     */
1652    function implodeArray($array, $type = false)
1653    {
1654        if (!is_array($array) || empty($array)) {
1655            return 'NULL';
1656        }
1657        if ($type) {
1658            foreach ($array as $value) {
1659                $return[] = $this->quote($value, $type);
1660            }
1661        } else {
1662            $return = $array;
1663        }
1664        return implode(', ', $return);
1665    }
1666
1667    // }}}
1668    // {{{ matchPattern()
1669
1670    /**
1671     * build a pattern matching string
1672     *
1673     * @access public
1674     *
1675     * @param array $pattern even keys are strings, odd are patterns (% and _)
1676     * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
1677     * @param string $field optional field name that is being matched against
1678     *                  (might be required when emulating ILIKE)
1679     *
1680     * @return string SQL pattern
1681     */
1682    function matchPattern($pattern, $operator = null, $field = null)
1683    {
1684        $db =& $this->getDBInstance();
1685        if (PEAR::isError($db)) {
1686            return $db;
1687        }
1688
1689        $match = '';
1690        if (!is_null($operator)) {
1691            $operator = strtoupper($operator);
1692            switch ($operator) {
1693            // case insensitive
1694            case 'ILIKE':
1695                if (is_null($field)) {
1696                    return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1697                        'case insensitive LIKE matching requires passing the field name', __FUNCTION__);
1698                }
1699                $db->loadModule('Function', null, true);
1700                $match = $db->function->lower($field).' LIKE ';
1701                break;
1702            // case sensitive
1703            case 'LIKE':
1704                $match = is_null($field) ? 'LIKE ' : $field.' LIKE ';
1705                break;
1706            default:
1707                return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1708                    'not a supported operator type:'. $operator, __FUNCTION__);
1709            }
1710        }
1711        $match.= "'";
1712        foreach ($pattern as $key => $value) {
1713            if ($key % 2) {
1714                $match.= $value;
1715            } else {
1716                if ($operator === 'ILIKE') {
1717                    $value = strtolower($value);
1718                }
1719                $escaped = $db->escape($value);
1720                if (PEAR::isError($escaped)) {
1721                    return $escaped;
1722                }
1723                $match.= $db->escapePattern($escaped);
1724            }
1725        }
1726        $match.= "'";
1727        $match.= $this->patternEscapeString();
1728        return $match;
1729    }
1730
1731    // }}}
1732    // {{{ patternEscapeString()
1733
1734    /**
1735     * build string to define pattern escape character
1736     *
1737     * @access public
1738     *
1739     * @return string define pattern escape character
1740     */
1741    function patternEscapeString()
1742    {
1743        return '';
1744    }
1745
1746    // }}}
1747    // {{{ mapNativeDatatype()
1748
1749    /**
1750     * Maps a native array description of a field to a MDB2 datatype and length
1751     *
1752     * @param array  $field native field description
1753     * @return array containing the various possible types, length, sign, fixed
1754     * @access public
1755     */
1756    function mapNativeDatatype($field)
1757    {
1758        $db =& $this->getDBInstance();
1759        if (PEAR::isError($db)) {
1760            return $db;
1761        }
1762
1763        // If the user has specified an option to map the native field
1764        // type to a custom MDB2 datatype...
1765        $db_type = strtok($field['type'], '(), ');
1766        if (!empty($db->options['nativetype_map_callback'][$db_type])) {
1767            return call_user_func_array($db->options['nativetype_map_callback'][$db_type], array($db, $field));
1768        }
1769
1770        // Otherwise perform the built-in (i.e. normal) MDB2 native type to
1771        // MDB2 datatype conversion
1772        return $this->_mapNativeDatatype($field);
1773    }
1774
1775    // }}}
1776    // {{{ _mapNativeDatatype()
1777
1778    /**
1779     * Maps a native array description of a field to a MDB2 datatype and length
1780     *
1781     * @param array  $field native field description
1782     * @return array containing the various possible types, length, sign, fixed
1783     * @access public
1784     */
1785    function _mapNativeDatatype($field)
1786    {
1787        $db =& $this->getDBInstance();
1788        if (PEAR::isError($db)) {
1789            return $db;
1790        }
1791
1792        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1793            'method not implemented', __FUNCTION__);
1794    }
1795
1796    // }}}
1797    // {{{ mapPrepareDatatype()
1798
1799    /**
1800     * Maps an mdb2 datatype to mysqli prepare type
1801     *
1802     * @param string $type
1803     * @return string
1804     * @access public
1805     */
1806    function mapPrepareDatatype($type)
1807    {
1808        $db =& $this->getDBInstance();
1809        if (PEAR::isError($db)) {
1810            return $db;
1811        }
1812
1813        if (!empty($db->options['datatype_map'][$type])) {
1814            $type = $db->options['datatype_map'][$type];
1815            if (!empty($db->options['datatype_map_callback'][$type])) {
1816                $parameter = array('type' => $type);
1817                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
1818            }
1819        }
1820
1821        return $type;
1822    }
1823}
1824?>
Note: See TracBrowser for help on using the repository browser.