Subversion-Projekte lars-tiefland.niewerth

Revision

Revision 7 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP versions 4 and 5                                                 |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 1998-2006 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.112 2006/10/14 13:42:00 lsmith Exp $
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
 * @package MDB2
59
 * @category Database
60
 * @author Lukas Smith <smith@pooteeweet.org>
61
 */
62
class MDB2_Driver_Datatype_Common extends MDB2_Module_Common
63
{
64
    var $valid_default_values = array(
65
        'text'      => '',
66
        'boolean'   => true,
67
        'integer'   => 0,
68
        'decimal'   => 0.0,
69
        'float'     => 0.0,
70
        'timestamp' => '1970-01-01 00:00:00',
71
        'time'      => '00:00:00',
72
        'date'      => '1970-01-01',
73
        'clob'      => '',
74
        'blob'      => '',
75
    );
76
 
77
    /**
78
     * contains all LOB objects created with this MDB2 instance
79
     * @var array
80
     * @access protected
81
     */
82
    var $lobs = array();
83
 
84
    // }}}
85
    // {{{ getValidTypes()
86
 
87
    /**
88
     * Get the list of valid types
89
     *
90
     * This function returns an array of valid types as keys with the values
91
     * being possible default values for all native datatypes and mapped types
92
     * for custom datatypes.
93
     *
94
     * @return mixed array on success, a MDB2 error on failure
95
     * @access public
96
     */
97
    function getValidTypes()
98
    {
99
        $types = $this->valid_default_values;
100
        $db =& $this->getDBInstance();
101
        if (PEAR::isError($db)) {
102
            return $db;
103
        }
104
        if (!empty($db->options['datatype_map'])) {
105
            foreach ($db->options['datatype_map'] as $type => $mapped_type) {
106
                if (array_key_exists($mapped_type, $types)) {
107
                    $types[$type] = $types[$mapped_type];
108
                } elseif (!empty($db->options['datatype_map_callback'][$type])) {
109
                    $parameter = array('type' => $type, 'mapped_type' => $mapped_type);
110
                    $default =  call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
111
                    $types[$type] = $default;
112
                }
113
            }
114
        }
115
        return $types;
116
    }
117
 
118
    // }}}
119
    // {{{ checkResultTypes()
120
 
121
    /**
122
     * Define the list of types to be associated with the columns of a given
123
     * result set.
124
     *
125
     * This function may be called before invoking fetchRow(), fetchOne()
126
     * fetchCole() and fetchAll() so that the necessary data type
127
     * conversions are performed on the data to be retrieved by them. If this
128
     * function is not called, the type of all result set columns is assumed
129
     * to be text, thus leading to not perform any conversions.
130
     *
131
     * @param string $types array variable that lists the
132
     *       data types to be expected in the result set columns. If this array
133
     *       contains less types than the number of columns that are returned
134
     *       in the result set, the remaining columns are assumed to be of the
135
     *       type text. Currently, the types clob and blob are not fully
136
     *       supported.
137
     * @return mixed MDB2_OK on success, a MDB2 error on failure
138
     * @access public
139
     */
140
    function checkResultTypes($types)
141
    {
142
        $types = is_array($types) ? $types : array($types);
143
        foreach ($types as $key => $type) {
144
            if (!isset($this->valid_default_values[$type])) {
145
                $db =& $this->getDBInstance();
146
                if (PEAR::isError($db)) {
147
                    return $db;
148
                }
149
                if (empty($db->options['datatype_map'][$type])) {
150
                    return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
151
                        $type.' for '.$key.' is not a supported column type', __FUNCTION__);
152
                }
153
            }
154
        }
155
        return $types;
156
    }
157
 
158
    // }}}
159
    // {{{ _baseConvertResult()
160
 
161
    /**
162
     * general type conversion method
163
     *
164
     * @param mixed $value refernce to a value to be converted
165
     * @param string $type specifies which type to convert to
166
     * @return object a MDB2 error on failure
167
     * @access protected
168
     */
169
    function _baseConvertResult($value, $type, $rtrim = true)
170
    {
171
        switch ($type) {
172
        case 'text':
173
            if ($rtrim) {
174
                $value = rtrim($value);
175
            }
176
            return $value;
177
        case 'integer':
178
            return intval($value);
179
        case 'boolean':
180
            return !empty($value);
181
        case 'decimal':
182
            return $value;
183
        case 'float':
184
            return doubleval($value);
185
        case 'date':
186
            return $value;
187
        case 'time':
188
            return $value;
189
        case 'timestamp':
190
            return $value;
191
        case 'clob':
192
        case 'blob':
193
            $this->lobs[] = array(
194
                'buffer' => null,
195
                'position' => 0,
196
                'lob_index' => null,
197
                'endOfLOB' => false,
198
                'resource' => $value,
199
                'value' => null,
200
                'loaded' => false,
201
            );
202
            end($this->lobs);
203
            $lob_index = key($this->lobs);
204
            $this->lobs[$lob_index]['lob_index'] = $lob_index;
205
            return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+');
206
        }
207
 
208
        $db =& $this->getDBInstance();
209
        if (PEAR::isError($db)) {
210
            return $db;
211
        }
212
 
213
        return $db->raiseError(MDB2_ERROR_INVALID, null, null,
214
            'attempt to convert result value to an unknown type :' . $type, __FUNCTION__);
215
    }
216
 
217
    // }}}
218
    // {{{ convertResult()
219
 
220
    /**
221
     * convert a value to a RDBMS indepdenant MDB2 type
222
     *
223
     * @param mixed $value value to be converted
224
     * @param string $type specifies which type to convert to
225
     * @param bool   $rtrim   if to rtrim text values or not
226
     * @return mixed converted value
227
     * @access public
228
     */
229
    function convertResult($value, $type, $rtrim = true)
230
    {
231
        if (is_null($value)) {
232
            return null;
233
        }
234
        $db =& $this->getDBInstance();
235
        if (PEAR::isError($db)) {
236
            return $db;
237
        }
238
        if (!empty($db->options['datatype_map'][$type])) {
239
            $type = $db->options['datatype_map'][$type];
240
            if (!empty($db->options['datatype_map_callback'][$type])) {
241
                $parameter = array('type' => $type, 'value' => $value, 'rtrim' => $rtrim);
242
                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
243
            }
244
        }
245
        return $this->_baseConvertResult($value, $type, $rtrim);
246
    }
247
 
248
    // }}}
249
    // {{{ convertResultRow()
250
 
251
    /**
252
     * convert a result row
253
     *
254
     * @param array $types
255
     * @param array $row specifies the types to convert to
256
     * @param bool   $rtrim   if to rtrim text values or not
257
     * @return mixed MDB2_OK on success,  a MDB2 error on failure
258
     * @access public
259
     */
260
    function convertResultRow($types, $row, $rtrim = true)
261
    {
262
        reset($types);
263
        $current_column = -1;
264
        foreach ($row as $key => $value) {
265
            ++$current_column;
266
            if (!isset($value)) {
267
                continue;
268
            }
269
            if (isset($types[$current_column])) {
270
                $type = $types[$current_column];
271
            } elseif (isset($types[$key])) {
272
                $type = $types[$key];
273
            } elseif (current($types)) {
274
                $type = current($types);
275
                next($types);
276
            } else {
277
                continue;
278
            }
279
            $value = $this->convertResult($row[$key], $type, $rtrim);
280
            if (PEAR::isError($value)) {
281
                return $value;
282
            }
283
            $row[$key] = $value;
284
        }
285
        return $row;
286
    }
287
 
288
    // }}}
289
    // {{{ getDeclaration()
290
 
291
    /**
292
     * Obtain DBMS specific SQL code portion needed to declare
293
     * of the given type
294
     *
295
     * @param string $type type to which the value should be converted to
296
     * @param string  $name   name the field to be declared.
297
     * @param string  $field  definition of the field
298
     * @return string  DBMS specific SQL code portion that should be used to
299
     *                 declare the specified field.
300
     * @access public
301
     */
302
    function getDeclaration($type, $name, $field)
303
    {
304
        $db =& $this->getDBInstance();
305
        if (PEAR::isError($db)) {
306
            return $db;
307
        }
308
 
309
        if (!empty($db->options['datatype_map'][$type])) {
310
            $type = $db->options['datatype_map'][$type];
311
            if (!empty($db->options['datatype_map_callback'][$type])) {
312
                $parameter = array('type' => $type, 'name' => $name, 'field' => $field);
313
                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
314
            }
315
        }
316
 
317
        if (!method_exists($this, "_get{$type}Declaration")) {
318
            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
319
                'type not defined: '.$type, __FUNCTION__);
320
        }
321
        return $this->{"_get{$type}Declaration"}($name, $field);
322
    }
323
 
324
    // }}}
325
    // {{{ getTypeDeclaration()
326
 
327
    /**
328
     * Obtain DBMS specific SQL code portion needed to declare an text type
329
     * field to be used in statements like CREATE TABLE.
330
     *
331
     * @param array $field  associative array with the name of the properties
332
     *      of the field being declared as array indexes. Currently, the types
333
     *      of supported field properties are as follows:
334
     *
335
     *      length
336
     *          Integer value that determines the maximum length of the text
337
     *          field. If this argument is missing the field should be
338
     *          declared to have the longest length allowed by the DBMS.
339
     *
340
     *      default
341
     *          Text value to be used as default for this field.
342
     *
343
     *      notnull
344
     *          Boolean flag that indicates whether this field is constrained
345
     *          to not be set to null.
346
     * @return string  DBMS specific SQL code portion that should be used to
347
     *      declare the specified field.
348
     * @access public
349
     */
350
    function getTypeDeclaration($field)
351
    {
352
        $db =& $this->getDBInstance();
353
        if (PEAR::isError($db)) {
354
            return $db;
355
        }
356
 
357
        switch ($field['type']) {
358
        case 'text':
359
            $length = !empty($field['length']) ? $field['length'] : $db->options['default_text_field_length'];
360
            $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
361
            return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')')
362
                : ($length ? 'VARCHAR('.$length.')' : 'TEXT');
363
        case 'clob':
364
            return 'TEXT';
365
        case 'blob':
366
            return 'TEXT';
367
        case 'integer':
368
            return 'INT';
369
        case 'boolean':
370
            return 'INT';
371
        case 'date':
372
            return 'CHAR ('.strlen('YYYY-MM-DD').')';
373
        case 'time':
374
            return 'CHAR ('.strlen('HH:MM:SS').')';
375
        case 'timestamp':
376
            return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
377
        case 'float':
378
            return 'TEXT';
379
        case 'decimal':
380
            return 'TEXT';
381
        }
382
        return '';
383
    }
384
 
385
    // }}}
386
    // {{{ _getDeclaration()
387
 
388
    /**
389
     * Obtain DBMS specific SQL code portion needed to declare a generic type
390
     * field to be used in statements like CREATE TABLE.
391
     *
392
     * @param string $name   name the field to be declared.
393
     * @param array  $field  associative array with the name of the properties
394
     *      of the field being declared as array indexes. Currently, the types
395
     *      of supported field properties are as follows:
396
     *
397
     *      length
398
     *          Integer value that determines the maximum length of the text
399
     *          field. If this argument is missing the field should be
400
     *          declared to have the longest length allowed by the DBMS.
401
     *
402
     *      default
403
     *          Text value to be used as default for this field.
404
     *
405
     *      notnull
406
     *          Boolean flag that indicates whether this field is constrained
407
     *          to not be set to null.
408
     *      charset
409
     *          Text value with the default CHARACTER SET for this field.
410
     *      collation
411
     *          Text value with the default COLLATION for this field.
412
     * @return string  DBMS specific SQL code portion that should be used to
413
     *      declare the specified field.
414
     * @access protected
415
     */
416
    function _getDeclaration($name, $field)
417
    {
418
        $db =& $this->getDBInstance();
419
        if (PEAR::isError($db)) {
420
            return $db;
421
        }
422
 
423
        $default = '';
424
        if (array_key_exists('default', $field)) {
425
            if ($field['default'] === '') {
426
                $field['default'] = empty($field['notnull'])
427
                    ? null : $this->valid_default_values[$field['type']];
428
                if ($field['default'] === ''
429
                    && ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
430
                ) {
431
                    $field['default'] = ' ';
432
                }
433
            }
434
            $default = ' DEFAULT '.$this->quote($field['default'], $field['type']);
435
        } elseif (empty($field['notnull'])) {
436
            $default = ' DEFAULT NULL';
437
        }
438
 
439
        $charset = empty($field['charset']) ? '' :
440
            ' '.$this->_getCharsetFieldDeclaration($field['charset']);
441
 
442
        $collation = empty($field['collation']) ? '' :
443
            ' '.$this->_getCollationFieldDeclaration($field['collation']);
444
 
445
        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
446
        $name = $db->quoteIdentifier($name, true);
447
        return $name.' '.$this->getTypeDeclaration($field).$charset.$default.$notnull.$collation;
448
    }
449
 
450
    // }}}
451
    // {{{ _getCharsetFieldDeclaration()
452
 
453
    /**
454
     * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
455
     * of a field declaration to be used in statements like CREATE TABLE.
456
     *
457
     * @param string $charset   name of the charset
458
     * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
459
     *                 of a field declaration.
460
     */
461
    function _getCharsetFieldDeclaration($charset)
462
    {
463
        return '';
464
    }
465
 
466
    // }}}
467
    // {{{ _getCollationFieldDeclaration()
468
 
469
    /**
470
     * Obtain DBMS specific SQL code portion needed to set the COLLATION
471
     * of a field declaration to be used in statements like CREATE TABLE.
472
     *
473
     * @param string $collation   name of the collation
474
     * @return string  DBMS specific SQL code portion needed to set the COLLATION
475
     *                 of a field declaration.
476
     */
477
    function _getCollationFieldDeclaration($collation)
478
    {
479
        return '';
480
    }
481
 
482
    // }}}
483
    // {{{ _getIntegerDeclaration()
484
 
485
    /**
486
     * Obtain DBMS specific SQL code portion needed to declare an integer type
487
     * field to be used in statements like CREATE TABLE.
488
     *
489
     * @param string $name name the field to be declared.
490
     * @param array $field associative array with the name of the properties
491
     *       of the field being declared as array indexes. Currently, the types
492
     *       of supported field properties are as follows:
493
     *
494
     *       unsigned
495
     *           Boolean flag that indicates whether the field should be
496
     *           declared as unsigned integer if possible.
497
     *
498
     *       default
499
     *           Integer value to be used as default for this field.
500
     *
501
     *       notnull
502
     *           Boolean flag that indicates whether this field is constrained
503
     *           to not be set to null.
504
     * @return string DBMS specific SQL code portion that should be used to
505
     *       declare the specified field.
506
     * @access protected
507
     */
508
    function _getIntegerDeclaration($name, $field)
509
    {
510
        if (!empty($field['unsigned'])) {
511
            $db =& $this->getDBInstance();
512
            if (PEAR::isError($db)) {
513
                return $db;
514
            }
515
 
516
            $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
517
        }
518
        return $this->_getDeclaration($name, $field);
519
    }
520
 
521
    // }}}
522
    // {{{ _getTextDeclaration()
523
 
524
    /**
525
     * Obtain DBMS specific SQL code portion needed to declare an text type
526
     * field to be used in statements like CREATE TABLE.
527
     *
528
     * @param string $name name the field to be declared.
529
     * @param array $field associative array with the name of the properties
530
     *       of the field being declared as array indexes. Currently, the types
531
     *       of supported field properties are as follows:
532
     *
533
     *       length
534
     *           Integer value that determines the maximum length of the text
535
     *           field. If this argument is missing the field should be
536
     *           declared to have the longest length allowed by the DBMS.
537
     *
538
     *       default
539
     *           Text value to be used as default for this field.
540
     *
541
     *       notnull
542
     *           Boolean flag that indicates whether this field is constrained
543
     *           to not be set to null.
544
     * @return string DBMS specific SQL code portion that should be used to
545
     *       declare the specified field.
546
     * @access protected
547
     */
548
    function _getTextDeclaration($name, $field)
549
    {
550
        return $this->_getDeclaration($name, $field);
551
    }
552
 
553
    // }}}
554
    // {{{ _getCLOBDeclaration()
555
 
556
    /**
557
     * Obtain DBMS specific SQL code portion needed to declare an character
558
     * large object type field to be used in statements like CREATE TABLE.
559
     *
560
     * @param string $name name the field to be declared.
561
     * @param array $field associative array with the name of the properties
562
     *        of the field being declared as array indexes. Currently, the types
563
     *        of supported field properties are as follows:
564
     *
565
     *        length
566
     *            Integer value that determines the maximum length of the large
567
     *            object field. If this argument is missing the field should be
568
     *            declared to have the longest length allowed by the DBMS.
569
     *
570
     *        notnull
571
     *            Boolean flag that indicates whether this field is constrained
572
     *            to not be set to null.
573
     * @return string DBMS specific SQL code portion that should be used to
574
     *        declare the specified field.
575
     * @access public
576
     */
577
    function _getCLOBDeclaration($name, $field)
578
    {
579
        $db =& $this->getDBInstance();
580
        if (PEAR::isError($db)) {
581
            return $db;
582
        }
583
 
584
        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
585
        $name = $db->quoteIdentifier($name, true);
586
        return $name.' '.$this->getTypeDeclaration($field).$notnull;
587
    }
588
 
589
    // }}}
590
    // {{{ _getBLOBDeclaration()
591
 
592
    /**
593
     * Obtain DBMS specific SQL code portion needed to declare an binary large
594
     * object type field to be used in statements like CREATE TABLE.
595
     *
596
     * @param string $name name the field to be declared.
597
     * @param array $field associative array with the name of the properties
598
     *        of the field being declared as array indexes. Currently, the types
599
     *        of supported field properties are as follows:
600
     *
601
     *        length
602
     *            Integer value that determines the maximum length of the large
603
     *            object field. If this argument is missing the field should be
604
     *            declared to have the longest length allowed by the DBMS.
605
     *
606
     *        notnull
607
     *            Boolean flag that indicates whether this field is constrained
608
     *            to not be set to null.
609
     * @return string DBMS specific SQL code portion that should be used to
610
     *        declare the specified field.
611
     * @access protected
612
     */
613
    function _getBLOBDeclaration($name, $field)
614
    {
615
        $db =& $this->getDBInstance();
616
        if (PEAR::isError($db)) {
617
            return $db;
618
        }
619
 
620
        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
621
        $name = $db->quoteIdentifier($name, true);
622
        return $name.' '.$this->getTypeDeclaration($field).$notnull;
623
    }
624
 
625
    // }}}
626
    // {{{ _getBooleanDeclaration()
627
 
628
    /**
629
     * Obtain DBMS specific SQL code portion needed to declare a boolean type
630
     * field to be used in statements like CREATE TABLE.
631
     *
632
     * @param string $name name the field to be declared.
633
     * @param array $field associative array with the name of the properties
634
     *       of the field being declared as array indexes. Currently, the types
635
     *       of supported field properties are as follows:
636
     *
637
     *       default
638
     *           Boolean value to be used as default for this field.
639
     *
640
     *       notnullL
641
     *           Boolean flag that indicates whether this field is constrained
642
     *           to not be set to null.
643
     * @return string DBMS specific SQL code portion that should be used to
644
     *       declare the specified field.
645
     * @access protected
646
     */
647
    function _getBooleanDeclaration($name, $field)
648
    {
649
        return $this->_getDeclaration($name, $field);
650
    }
651
 
652
    // }}}
653
    // {{{ _getDateDeclaration()
654
 
655
    /**
656
     * Obtain DBMS specific SQL code portion needed to declare a date type
657
     * field to be used in statements like CREATE TABLE.
658
     *
659
     * @param string $name name the field to be declared.
660
     * @param array $field associative array with the name of the properties
661
     *       of the field being declared as array indexes. Currently, the types
662
     *       of supported field properties are as follows:
663
     *
664
     *       default
665
     *           Date value to be used as default for this field.
666
     *
667
     *       notnull
668
     *           Boolean flag that indicates whether this field is constrained
669
     *           to not be set to null.
670
     * @return string DBMS specific SQL code portion that should be used to
671
     *       declare the specified field.
672
     * @access protected
673
     */
674
    function _getDateDeclaration($name, $field)
675
    {
676
        return $this->_getDeclaration($name, $field);
677
    }
678
 
679
    // }}}
680
    // {{{ _getTimestampDeclaration()
681
 
682
    /**
683
     * Obtain DBMS specific SQL code portion needed to declare a timestamp
684
     * field to be used in statements like CREATE TABLE.
685
     *
686
     * @param string $name name the field to be declared.
687
     * @param array $field associative array with the name of the properties
688
     *       of the field being declared as array indexes. Currently, the types
689
     *       of supported field properties are as follows:
690
     *
691
     *       default
692
     *           Timestamp value to be used as default for this field.
693
     *
694
     *       notnull
695
     *           Boolean flag that indicates whether this field is constrained
696
     *           to not be set to null.
697
     * @return string DBMS specific SQL code portion that should be used to
698
     *       declare the specified field.
699
     * @access protected
700
     */
701
    function _getTimestampDeclaration($name, $field)
702
    {
703
        return $this->_getDeclaration($name, $field);
704
    }
705
 
706
    // }}}
707
    // {{{ _getTimeDeclaration()
708
 
709
    /**
710
     * Obtain DBMS specific SQL code portion needed to declare a time
711
     * field to be used in statements like CREATE TABLE.
712
     *
713
     * @param string $name name the field to be declared.
714
     * @param array $field associative array with the name of the properties
715
     *       of the field being declared as array indexes. Currently, the types
716
     *       of supported field properties are as follows:
717
     *
718
     *       default
719
     *           Time value to be used as default for this field.
720
     *
721
     *       notnull
722
     *           Boolean flag that indicates whether this field is constrained
723
     *           to not be set to null.
724
     * @return string DBMS specific SQL code portion that should be used to
725
     *       declare the specified field.
726
     * @access protected
727
     */
728
    function _getTimeDeclaration($name, $field)
729
    {
730
        return $this->_getDeclaration($name, $field);
731
    }
732
 
733
    // }}}
734
    // {{{ _getFloatDeclaration()
735
 
736
    /**
737
     * Obtain DBMS specific SQL code portion needed to declare a float type
738
     * field to be used in statements like CREATE TABLE.
739
     *
740
     * @param string $name name the field to be declared.
741
     * @param array $field associative array with the name of the properties
742
     *       of the field being declared as array indexes. Currently, the types
743
     *       of supported field properties are as follows:
744
     *
745
     *       default
746
     *           Float value to be used as default for this field.
747
     *
748
     *       notnull
749
     *           Boolean flag that indicates whether this field is constrained
750
     *           to not be set to null.
751
     * @return string DBMS specific SQL code portion that should be used to
752
     *       declare the specified field.
753
     * @access protected
754
     */
755
    function _getFloatDeclaration($name, $field)
756
    {
757
        return $this->_getDeclaration($name, $field);
758
    }
759
 
760
    // }}}
761
    // {{{ _getDecimalDeclaration()
762
 
763
    /**
764
     * Obtain DBMS specific SQL code portion needed to declare a decimal type
765
     * field to be used in statements like CREATE TABLE.
766
     *
767
     * @param string $name name the field to be declared.
768
     * @param array $field associative array with the name of the properties
769
     *       of the field being declared as array indexes. Currently, the types
770
     *       of supported field properties are as follows:
771
     *
772
     *       default
773
     *           Decimal value to be used as default for this field.
774
     *
775
     *       notnull
776
     *           Boolean flag that indicates whether this field is constrained
777
     *           to not be set to null.
778
     * @return string DBMS specific SQL code portion that should be used to
779
     *       declare the specified field.
780
     * @access protected
781
     */
782
    function _getDecimalDeclaration($name, $field)
783
    {
784
        return $this->_getDeclaration($name, $field);
785
    }
786
 
787
    // }}}
788
    // {{{ compareDefinition()
789
 
790
    /**
791
     * Obtain an array of changes that may need to applied
792
     *
793
     * @param array $current new definition
794
     * @param array  $previous old definition
795
     * @return array  containing all changes that will need to be applied
796
     * @access public
797
     */
798
    function compareDefinition($current, $previous)
799
    {
800
        $type = !empty($current['type']) ? $current['type'] : null;
801
 
802
        if (!method_exists($this, "_compare{$type}Definition")) {
803
            $db =& $this->getDBInstance();
804
            if (PEAR::isError($db)) {
805
                return $db;
806
            }
807
 
808
            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
809
                'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
810
        }
811
 
812
        if (empty($previous['type']) || $previous['type'] != $type) {
813
            return $current;
814
        }
815
 
816
        $change = $this->{"_compare{$type}Definition"}($current, $previous);
817
 
818
        if ($previous['type'] != $type) {
819
            $change['type'] = true;
820
        }
821
 
822
        $previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
823
        $notnull = !empty($current['notnull']) ? $current['notnull'] : false;
824
        if ($previous_notnull != $notnull) {
825
            $change['notnull'] = true;
826
        }
827
 
828
        $previous_default = array_key_exists('default', $previous) ? $previous['default'] :
829
            ($previous_notnull ? '' : null);
830
        $default = array_key_exists('default', $current) ? $current['default'] :
831
            ($notnull ? '' : null);
832
        if ($previous_default !== $default) {
833
            $change['default'] = true;
834
        }
835
 
836
        return $change;
837
    }
838
 
839
    // }}}
840
    // {{{ _compareIntegerDefinition()
841
 
842
    /**
843
     * Obtain an array of changes that may need to applied to an integer field
844
     *
845
     * @param array $current new definition
846
     * @param array  $previous old definition
847
     * @return array  containing all changes that will need to be applied
848
     * @access protected
849
     */
850
    function _compareIntegerDefinition($current, $previous)
851
    {
852
        $change = array();
853
        $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false;
854
        $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false;
855
        if ($previous_unsigned != $unsigned) {
856
            $change['unsigned'] = true;
857
        }
858
        $previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false;
859
        $autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false;
860
        if ($previous_autoincrement != $autoincrement) {
861
            $change['autoincrement'] = true;
862
        }
863
        return $change;
864
    }
865
 
866
    // }}}
867
    // {{{ _compareTextDefinition()
868
 
869
    /**
870
     * Obtain an array of changes that may need to applied to an text field
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 protected
876
     */
877
    function _compareTextDefinition($current, $previous)
878
    {
879
        $change = array();
880
        $previous_length = !empty($previous['length']) ? $previous['length'] : 0;
881
        $length = !empty($current['length']) ? $current['length'] : 0;
882
        if ($previous_length != $length) {
883
            $change['length'] = true;
884
        }
885
        $previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0;
886
        $fixed = !empty($current['fixed']) ? $current['fixed'] : 0;
887
        if ($previous_fixed != $fixed) {
888
            $change['fixed'] = true;
889
        }
890
        return $change;
891
    }
892
 
893
    // }}}
894
    // {{{ _compareCLOBDefinition()
895
 
896
    /**
897
     * Obtain an array of changes that may need to applied to an CLOB field
898
     *
899
     * @param array $current new definition
900
     * @param array  $previous old definition
901
     * @return array  containing all changes that will need to be applied
902
     * @access protected
903
     */
904
    function _compareCLOBDefinition($current, $previous)
905
    {
906
        return $this->_compareTextDefinition($current, $previous);
907
    }
908
 
909
    // }}}
910
    // {{{ _compareBLOBDefinition()
911
 
912
    /**
913
     * Obtain an array of changes that may need to applied to an BLOB field
914
     *
915
     * @param array $current new definition
916
     * @param array  $previous old definition
917
     * @return array  containing all changes that will need to be applied
918
     * @access protected
919
     */
920
    function _compareBLOBDefinition($current, $previous)
921
    {
922
        return $this->_compareTextDefinition($current, $previous);
923
    }
924
 
925
    // }}}
926
    // {{{ _compareDateDefinition()
927
 
928
    /**
929
     * Obtain an array of changes that may need to applied to an date field
930
     *
931
     * @param array $current new definition
932
     * @param array  $previous old definition
933
     * @return array  containing all changes that will need to be applied
934
     * @access protected
935
     */
936
    function _compareDateDefinition($current, $previous)
937
    {
938
        return array();
939
    }
940
 
941
    // }}}
942
    // {{{ _compareTimeDefinition()
943
 
944
    /**
945
     * Obtain an array of changes that may need to applied to an time field
946
     *
947
     * @param array $current new definition
948
     * @param array  $previous old definition
949
     * @return array  containing all changes that will need to be applied
950
     * @access protected
951
     */
952
    function _compareTimeDefinition($current, $previous)
953
    {
954
        return array();
955
    }
956
 
957
    // }}}
958
    // {{{ _compareTimestampDefinition()
959
 
960
    /**
961
     * Obtain an array of changes that may need to applied to an timestamp field
962
     *
963
     * @param array $current new definition
964
     * @param array  $previous old definition
965
     * @return array  containing all changes that will need to be applied
966
     * @access protected
967
     */
968
    function _compareTimestampDefinition($current, $previous)
969
    {
970
        return array();
971
    }
972
 
973
    // }}}
974
    // {{{ _compareBooleanDefinition()
975
 
976
    /**
977
     * Obtain an array of changes that may need to applied to an boolean field
978
     *
979
     * @param array $current new definition
980
     * @param array  $previous old definition
981
     * @return array  containing all changes that will need to be applied
982
     * @access protected
983
     */
984
    function _compareBooleanDefinition($current, $previous)
985
    {
986
        return array();
987
    }
988
 
989
    // }}}
990
    // {{{ _compareFloatDefinition()
991
 
992
    /**
993
     * Obtain an array of changes that may need to applied to an float field
994
     *
995
     * @param array $current new definition
996
     * @param array  $previous old definition
997
     * @return array  containing all changes that will need to be applied
998
     * @access protected
999
     */
1000
    function _compareFloatDefinition($current, $previous)
1001
    {
1002
        return array();
1003
    }
1004
 
1005
    // }}}
1006
    // {{{ _compareDecimalDefinition()
1007
 
1008
    /**
1009
     * Obtain an array of changes that may need to applied to an decimal field
1010
     *
1011
     * @param array $current new definition
1012
     * @param array  $previous old definition
1013
     * @return array  containing all changes that will need to be applied
1014
     * @access protected
1015
     */
1016
    function _compareDecimalDefinition($current, $previous)
1017
    {
1018
        return array();
1019
    }
1020
 
1021
    // }}}
1022
    // {{{ quote()
1023
 
1024
    /**
1025
     * Convert a text value into a DBMS specific format that is suitable to
1026
     * compose query statements.
1027
     *
1028
     * @param string $value text string value that is intended to be converted.
1029
     * @param string $type type to which the value should be converted to
1030
     * @param bool $quote determines if the value should be quoted and escaped
1031
     * @param bool $escape_wildcards if to escape escape wildcards
1032
     * @return string text string that represents the given argument value in
1033
     *       a DBMS specific format.
1034
     * @access public
1035
     */
1036
    function quote($value, $type = null, $quote = true, $escape_wildcards = false)
1037
    {
1038
        $db =& $this->getDBInstance();
1039
        if (PEAR::isError($db)) {
1040
            return $db;
1041
        }
1042
 
1043
        if (is_null($value)
1044
            || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
1045
        ) {
1046
            if (!$quote) {
1047
                return null;
1048
            }
1049
            return 'NULL';
1050
        }
1051
 
1052
        if (is_null($type)) {
1053
            switch (gettype($value)) {
1054
            case 'integer':
1055
                $type = 'integer';
1056
                break;
1057
            case 'double':
1058
                // todo: default to decimal as float is quite unusual
1059
                // $type = 'float';
1060
                $type = 'decimal';
1061
                break;
1062
            case 'boolean':
1063
                $type = 'boolean';
1064
                break;
1065
            case 'array':
1066
                 $value = serialize($value);
1067
            case 'object':
1068
                 $type = 'text';
1069
                break;
1070
            default:
1071
                if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
1072
                    $type = 'timestamp';
1073
                } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
1074
                    $type = 'time';
1075
                } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
1076
                    $type = 'date';
1077
                } else {
1078
                    $type = 'text';
1079
                }
1080
                break;
1081
            }
1082
        } elseif (!empty($db->options['datatype_map'][$type])) {
1083
            $type = $db->options['datatype_map'][$type];
1084
            if (!empty($db->options['datatype_map_callback'][$type])) {
1085
                $parameter = array('type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards);
1086
                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
1087
            }
1088
        }
1089
 
1090
        if (!method_exists($this, "_quote{$type}")) {
1091
            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1092
                'type not defined: '.$type, __FUNCTION__);
1093
        }
1094
        $value = $this->{"_quote{$type}"}($value, $quote, $escape_wildcards);
1095
        if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern']
1096
            && $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern']
1097
        ) {
1098
            $value.= $this->patternEscapeString();
1099
        }
1100
        return $value;
1101
    }
1102
 
1103
    // }}}
1104
    // {{{ _quoteInteger()
1105
 
1106
    /**
1107
     * Convert a text value into a DBMS specific format that is suitable to
1108
     * compose query statements.
1109
     *
1110
     * @param string $value text string value that is intended to be converted.
1111
     * @param bool $quote determines if the value should be quoted and escaped
1112
     * @param bool $escape_wildcards if to escape escape wildcards
1113
     * @return string text string that represents the given argument value in
1114
     *       a DBMS specific format.
1115
     * @access protected
1116
     */
1117
    function _quoteInteger($value, $quote, $escape_wildcards)
1118
    {
1119
        return (int)$value;
1120
    }
1121
 
1122
    // }}}
1123
    // {{{ _quoteText()
1124
 
1125
    /**
1126
     * Convert a text value into a DBMS specific format that is suitable to
1127
     * compose query statements.
1128
     *
1129
     * @param string $value text string value that is intended to be converted.
1130
     * @param bool $quote determines if the value should be quoted and escaped
1131
     * @param bool $escape_wildcards if to escape escape wildcards
1132
     * @return string text string that already contains any DBMS specific
1133
     *       escaped character sequences.
1134
     * @access protected
1135
     */
1136
    function _quoteText($value, $quote, $escape_wildcards)
1137
    {
1138
        if (!$quote) {
1139
            return $value;
1140
        }
1141
 
1142
        $db =& $this->getDBInstance();
1143
        if (PEAR::isError($db)) {
1144
            return $db;
1145
        }
1146
 
1147
        $value = $db->escape($value, $escape_wildcards);
1148
        return "'".$value."'";
1149
    }
1150
 
1151
    // }}}
1152
    // {{{ _readFile()
1153
 
1154
    /**
1155
     * Convert a text value into a DBMS specific format that is suitable to
1156
     * compose query statements.
1157
     *
1158
     * @param string $value text string value that is intended to be converted.
1159
     * @return string text string that represents the given argument value in
1160
     *       a DBMS specific format.
1161
     * @access protected
1162
     */
1163
    function _readFile($value)
1164
    {
1165
        $close = false;
1166
        if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1167
            $close = true;
1168
            if ($match[1] == 'file://') {
1169
                $value = $match[2];
1170
            }
1171
            $value = @fopen($value, 'r');
1172
        }
1173
 
1174
        if (is_resource($value)) {
1175
            $db =& $this->getDBInstance();
1176
            if (PEAR::isError($db)) {
1177
                return $db;
1178
            }
1179
 
1180
            $fp = $value;
1181
            $value = '';
1182
            while (!@feof($fp)) {
1183
                $value.= @fread($fp, $db->options['lob_buffer_length']);
1184
            }
1185
            if ($close) {
1186
                @fclose($fp);
1187
            }
1188
        }
1189
 
1190
        return $value;
1191
    }
1192
 
1193
    // }}}
1194
    // {{{ _quoteLOB()
1195
 
1196
    /**
1197
     * Convert a text value into a DBMS specific format that is suitable to
1198
     * compose query statements.
1199
     *
1200
     * @param string $value text string value that is intended to be converted.
1201
     * @param bool $quote determines if the value should be quoted and escaped
1202
     * @param bool $escape_wildcards if to escape escape wildcards
1203
     * @return string text string that represents the given argument value in
1204
     *       a DBMS specific format.
1205
     * @access protected
1206
     */
1207
    function _quoteLOB($value, $quote, $escape_wildcards)
1208
    {
1209
        $value = $this->_readFile($value);
1210
        return $this->_quoteText($value, $quote, $escape_wildcards);
1211
    }
1212
 
1213
    // }}}
1214
    // {{{ _quoteCLOB()
1215
 
1216
    /**
1217
     * Convert a text value into a DBMS specific format that is suitable to
1218
     * compose query statements.
1219
     *
1220
     * @param string $value text string value that is intended to be converted.
1221
     * @param bool $quote determines if the value should be quoted and escaped
1222
     * @param bool $escape_wildcards if to escape escape wildcards
1223
     * @return string text string that represents the given argument value in
1224
     *       a DBMS specific format.
1225
     * @access protected
1226
     */
1227
    function _quoteCLOB($value, $quote, $escape_wildcards)
1228
    {
1229
        return $this->_quoteLOB($value, $quote, $escape_wildcards);
1230
    }
1231
 
1232
    // }}}
1233
    // {{{ _quoteBLOB()
1234
 
1235
    /**
1236
     * Convert a text value into a DBMS specific format that is suitable to
1237
     * compose query statements.
1238
     *
1239
     * @param string $value text string value that is intended to be converted.
1240
     * @param bool $quote determines if the value should be quoted and escaped
1241
     * @param bool $escape_wildcards if to escape escape wildcards
1242
     * @return string text string that represents the given argument value in
1243
     *       a DBMS specific format.
1244
     * @access protected
1245
     */
1246
    function _quoteBLOB($value, $quote, $escape_wildcards)
1247
    {
1248
        return $this->_quoteLOB($value, $quote, $escape_wildcards);
1249
    }
1250
 
1251
    // }}}
1252
    // {{{ _quoteBoolean()
1253
 
1254
    /**
1255
     * Convert a text value into a DBMS specific format that is suitable to
1256
     * compose query statements.
1257
     *
1258
     * @param string $value text string value that is intended to be converted.
1259
     * @param bool $quote determines if the value should be quoted and escaped
1260
     * @param bool $escape_wildcards if to escape escape wildcards
1261
     * @return string text string that represents the given argument value in
1262
     *       a DBMS specific format.
1263
     * @access protected
1264
     */
1265
    function _quoteBoolean($value, $quote, $escape_wildcards)
1266
    {
1267
        return ($value ? 1 : 0);
1268
    }
1269
 
1270
    // }}}
1271
    // {{{ _quoteDate()
1272
 
1273
    /**
1274
     * Convert a text value into a DBMS specific format that is suitable to
1275
     * compose query statements.
1276
     *
1277
     * @param string $value text string value that is intended to be converted.
1278
     * @param bool $quote determines if the value should be quoted and escaped
1279
     * @param bool $escape_wildcards if to escape escape wildcards
1280
     * @return string text string that represents the given argument value in
1281
     *       a DBMS specific format.
1282
     * @access protected
1283
     */
1284
    function _quoteDate($value, $quote, $escape_wildcards)
1285
    {
1286
        if ($value === 'CURRENT_DATE') {
1287
            $db =& $this->getDBInstance();
1288
            if (PEAR::isError($db)) {
1289
                return $db;
1290
            }
1291
            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1292
                return $db->function->now('date');
1293
            }
1294
            return 'CURRENT_DATE';
1295
        }
1296
        return $this->_quoteText($value, $quote, $escape_wildcards);
1297
    }
1298
 
1299
    // }}}
1300
    // {{{ _quoteTimestamp()
1301
 
1302
    /**
1303
     * Convert a text value into a DBMS specific format that is suitable to
1304
     * compose query statements.
1305
     *
1306
     * @param string $value text string value that is intended to be converted.
1307
     * @param bool $quote determines if the value should be quoted and escaped
1308
     * @param bool $escape_wildcards if to escape escape wildcards
1309
     * @return string text string that represents the given argument value in
1310
     *       a DBMS specific format.
1311
     * @access protected
1312
     */
1313
    function _quoteTimestamp($value, $quote, $escape_wildcards)
1314
    {
1315
        if ($value === 'CURRENT_TIMESTAMP') {
1316
            $db =& $this->getDBInstance();
1317
            if (PEAR::isError($db)) {
1318
                return $db;
1319
            }
1320
            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1321
                return $db->function->now('timestamp');
1322
            }
1323
            return 'CURRENT_TIMESTAMP';
1324
        }
1325
        return $this->_quoteText($value, $quote, $escape_wildcards);
1326
    }
1327
 
1328
    // }}}
1329
    // {{{ _quoteTime()
1330
 
1331
    /**
1332
     * Convert a text value into a DBMS specific format that is suitable to
1333
     *       compose query statements.
1334
     *
1335
     * @param string $value text string value that is intended to be converted.
1336
     * @param bool $quote determines if the value should be quoted and escaped
1337
     * @param bool $escape_wildcards if to escape escape wildcards
1338
     * @return string text string that represents the given argument value in
1339
     *       a DBMS specific format.
1340
     * @access protected
1341
     */
1342
    function _quoteTime($value, $quote, $escape_wildcards)
1343
    {
1344
        if ($value === 'CURRENT_TIME') {
1345
            $db =& $this->getDBInstance();
1346
            if (PEAR::isError($db)) {
1347
                return $db;
1348
            }
1349
            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1350
                return $db->function->now('time');
1351
            }
1352
            return 'CURRENT_TIME';
1353
        }
1354
        return $this->_quoteText($value, $quote, $escape_wildcards);
1355
    }
1356
 
1357
    // }}}
1358
    // {{{ _quoteFloat()
1359
 
1360
    /**
1361
     * Convert a text value into a DBMS specific format that is suitable to
1362
     * compose query statements.
1363
     *
1364
     * @param string $value text string value that is intended to be converted.
1365
     * @param bool $quote determines if the value should be quoted and escaped
1366
     * @param bool $escape_wildcards if to escape escape wildcards
1367
     * @return string text string that represents the given argument value in
1368
     *       a DBMS specific format.
1369
     * @access protected
1370
     */
1371
    function _quoteFloat($value, $quote, $escape_wildcards)
1372
    {
1373
        if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
1374
            $decimal = $this->_quoteDecimal($matches[1], $quote, $escape_wildcards);
1375
            $sign = $matches[2];
1376
            $exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
1377
            $value = $decimal.'E'.$sign.$exponent;
1378
        } else {
1379
            $value = $this->_quoteDecimal($value, $quote, $escape_wildcards);
1380
        }
1381
        return $value;
1382
    }
1383
 
1384
    // }}}
1385
    // {{{ _quoteDecimal()
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 _quoteDecimal($value, $quote, $escape_wildcards)
1399
    {
1400
        $value = (string)$value;
1401
        if (preg_match('/[^.0-9]/', $value)) {
1402
            if (strpos($value, ',')) {
1403
                // 1000,00
1404
                if (!strpos($value, '.')) {
1405
                    // convert the last "," to a "."
1406
                    $value = strrev(str_replace(',', '.', strrev($value)));
1407
                // 1.000,00
1408
                } elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) {
1409
                    $value = str_replace('.', '', $value);
1410
                    // convert the last "," to a "."
1411
                    $value = strrev(str_replace(',', '.', strrev($value)));
1412
                // 1,000.00
1413
                } else {
1414
                    $value = str_replace(',', '', $value);
1415
                }
1416
            }
1417
        }
1418
        return $value;
1419
    }
1420
 
1421
    // }}}
1422
    // {{{ writeLOBToFile()
1423
 
1424
    /**
1425
     * retrieve LOB from the database
1426
     *
1427
     * @param resource $lob stream handle
1428
     * @param string $file name of the file into which the LOb should be fetched
1429
     * @return mixed MDB2_OK on success, a MDB2 error on failure
1430
     * @access protected
1431
     */
1432
    function writeLOBToFile($lob, $file)
1433
    {
1434
        $db =& $this->getDBInstance();
1435
        if (PEAR::isError($db)) {
1436
            return $db;
1437
        }
1438
 
1439
        if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
1440
            if ($match[1] == 'file://') {
1441
                $file = $match[2];
1442
            }
1443
        }
1444
 
1445
        $fp = @fopen($file, 'wb');
1446
        while (!@feof($lob)) {
1447
            $result = @fread($lob, $db->options['lob_buffer_length']);
1448
            $read = strlen($result);
1449
            if (@fwrite($fp, $result, $read) != $read) {
1450
                @fclose($fp);
1451
                return $db->raiseError(MDB2_ERROR, null, null,
1452
                    'could not write to the output file', __FUNCTION__);
1453
            }
1454
        }
1455
        @fclose($fp);
1456
        return MDB2_OK;
1457
    }
1458
 
1459
    // }}}
1460
    // {{{ _retrieveLOB()
1461
 
1462
    /**
1463
     * retrieve LOB from the database
1464
     *
1465
     * @param array $lob array
1466
     * @return mixed MDB2_OK on success, a MDB2 error on failure
1467
     * @access protected
1468
     */
1469
    function _retrieveLOB(&$lob)
1470
    {
1471
        if (is_null($lob['value'])) {
1472
            $lob['value'] = $lob['resource'];
1473
        }
1474
        $lob['loaded'] = true;
1475
        return MDB2_OK;
1476
    }
1477
 
1478
    // }}}
1479
    // {{{ readLOB()
1480
 
1481
    /**
1482
     * Read data from large object input stream.
1483
     *
1484
     * @param resource $lob stream handle
1485
     * @param string $data reference to a variable that will hold data
1486
     *                          to be read from the large object input stream
1487
     * @param integer $length    value that indicates the largest ammount ofdata
1488
     *                          to be read from the large object input stream.
1489
     * @return mixed the effective number of bytes read from the large object
1490
     *                      input stream on sucess or an MDB2 error object.
1491
     * @access public
1492
     * @see endOfLOB()
1493
     */
1494
    function _readLOB($lob, $length)
1495
    {
1496
        return substr($lob['value'], $lob['position'], $length);
1497
    }
1498
 
1499
    // }}}
1500
    // {{{ _endOfLOB()
1501
 
1502
    /**
1503
     * Determine whether it was reached the end of the large object and
1504
     * therefore there is no more data to be read for the its input stream.
1505
     *
1506
     * @param array $lob array
1507
     * @return mixed true or false on success, a MDB2 error on failure
1508
     * @access protected
1509
     */
1510
    function _endOfLOB($lob)
1511
    {
1512
        return $lob['endOfLOB'];
1513
    }
1514
 
1515
    // }}}
1516
    // {{{ destroyLOB()
1517
 
1518
    /**
1519
     * Free any resources allocated during the lifetime of the large object
1520
     * handler object.
1521
     *
1522
     * @param resource $lob stream handle
1523
     * @access public
1524
     */
1525
    function destroyLOB($lob)
1526
    {
1527
        $lob_data = stream_get_meta_data($lob);
1528
        $lob_index = $lob_data['wrapper_data']->lob_index;
1529
        fclose($lob);
1530
        if (isset($this->lobs[$lob_index])) {
1531
            $this->_destroyLOB($this->lobs[$lob_index]);
1532
            unset($this->lobs[$lob_index]);
1533
        }
1534
        return MDB2_OK;
1535
    }
1536
 
1537
    // }}}
1538
    // {{{ _destroyLOB()
1539
 
1540
    /**
1541
     * Free any resources allocated during the lifetime of the large object
1542
     * handler object.
1543
     *
1544
     * @param array $lob array
1545
     * @access private
1546
     */
1547
    function _destroyLOB(&$lob)
1548
    {
1549
        return MDB2_OK;
1550
    }
1551
 
1552
    // }}}
1553
    // {{{ implodeArray()
1554
 
1555
    /**
1556
     * apply a type to all values of an array and return as a comma seperated string
1557
     * useful for generating IN statements
1558
     *
1559
     * @access public
1560
     *
1561
     * @param array $array data array
1562
     * @param string $type determines type of the field
1563
     *
1564
     * @return string comma seperated values
1565
     */
1566
    function implodeArray($array, $type = false)
1567
    {
1568
        if (!is_array($array) || empty($array)) {
1569
            return 'NULL';
1570
        }
1571
        if ($type) {
1572
            foreach ($array as $value) {
1573
                $return[] = $this->quote($value, $type);
1574
            }
1575
        } else {
1576
            $return = $array;
1577
        }
1578
        return implode(', ', $return);
1579
    }
1580
 
1581
    // }}}
1582
    // {{{ matchPattern()
1583
 
1584
    /**
1585
     * build a pattern matching string
1586
     *
1587
     * EXPERIMENTAL
1588
     *
1589
     * WARNING: this function is experimental and may change signature at
1590
     * any time until labelled as non-experimental
1591
     *
1592
     * @access public
1593
     *
1594
     * @param array $pattern even keys are strings, odd are patterns (% and _)
1595
     * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future)
1596
     * @param string $field optional field name that is being matched against
1597
     *                  (might be required when emulating ILIKE)
1598
     *
1599
     * @return string SQL pattern
1600
     */
1601
    function matchPattern($pattern, $operator = null, $field = null)
1602
    {
1603
        $db =& $this->getDBInstance();
1604
        if (PEAR::isError($db)) {
1605
            return $db;
1606
        }
1607
 
1608
        $match = '';
1609
        if (!is_null($operator)) {
1610
            $operator = strtoupper($operator);
1611
            switch ($operator) {
1612
            // case insensitive
1613
            case 'ILIKE':
1614
                if (is_null($field)) {
1615
                    return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1616
                        'case insensitive LIKE matching requires passing the field name', __FUNCTION__);
1617
                }
1618
                $db->loadModule('Function', null, true);
1619
                $match = $db->function->lower($field).' '.'LIKE ';
1620
                break;
1621
            // case sensitive
1622
            case 'LIKE':
1623
                $match = is_null($field) ? 'LIKE ' : $field.' LIKE ';
1624
                break;
1625
            default:
1626
                return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1627
                    'not a supported operator type:'. $operator, __FUNCTION__);
1628
            }
1629
        }
1630
        $match.= "'";
1631
        foreach ($pattern as $key => $value) {
1632
            if ($key % 2) {
1633
                $match.= $value;
1634
            } else {
1635
                if ($operator === 'ILIKE') {
1636
                    $value = strtolower($value);
1637
                }
1638
                $match.= $db->escapePattern($db->escape($value));
1639
            }
1640
        }
1641
        $match.= "'";
1642
        $match.= $this->patternEscapeString();
1643
        return $match;
1644
    }
1645
 
1646
    // }}}
1647
    // {{{ patternEscapeString()
1648
 
1649
    /**
1650
     * build string to define pattern escape character
1651
     *
1652
     * EXPERIMENTAL
1653
     *
1654
     * WARNING: this function is experimental and may change signature at
1655
     * any time until labelled as non-experimental
1656
     *
1657
     * @access public
1658
     *
1659
     * @return string define pattern escape character
1660
     */
1661
    function patternEscapeString()
1662
    {
1663
        return '';
1664
    }
1665
 
1666
    // }}}
1667
    // {{{ mapNativeDatatype()
1668
 
1669
    /**
1670
     * Maps a native array description of a field to a MDB2 datatype and length
1671
     *
1672
     * @param array  $field native field description
1673
     * @return array containing the various possible types, length, sign, fixed
1674
     * @access public
1675
     */
1676
    function mapNativeDatatype($field)
1677
    {
1678
        $db =& $this->getDBInstance();
1679
        if (PEAR::isError($db)) {
1680
            return $db;
1681
        }
1682
 
1683
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
1684
            'method not implemented', __FUNCTION__);
1685
    }
1686
 
1687
    // }}}
1688
    // {{{ mapPrepareDatatype()
1689
 
1690
    /**
1691
     * Maps an mdb2 datatype to mysqli prepare type
1692
     *
1693
     * @param string $type
1694
     * @return string
1695
     * @access public
1696
     */
1697
    function mapPrepareDatatype($type)
1698
    {
1699
        $db =& $this->getDBInstance();
1700
        if (PEAR::isError($db)) {
1701
            return $db;
1702
        }
1703
 
1704
        if (!empty($db->options['datatype_map'][$type])) {
1705
            $type = $db->options['datatype_map'][$type];
1706
            if (!empty($db->options['datatype_map_callback'][$type])) {
1707
                $parameter = array('type' => $type);
1708
                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
1709
            }
1710
        }
1711
 
1712
        return $type;
1713
    }
1714
}
1715
 
1716
?>