root/releases/0.9rc2/lib/adodb/adodb-recordset.inc.php

Revision 269, 23.6 kB (checked in by ben, 3 years ago)

--

  • Property svn:eol-style set to native
Line 
1 <?php
2 /**
3  * @version V3.40 7 April 2003 (c) 2000-2003 John Lim (jlim@natsoft.com.my). All rights reserved.
4  * Released under both BSD license and Lesser GPL library license.
5  * Whenever there is any discrepancy between the two licenses,
6  * the BSD license will take precedence.
7  *
8  * Set tabs to 4 for best viewing.
9  *
10  * Latest version is available at http://php.weblogs.com
11  *
12  */
13
14  
15    /**
16      * RecordSet class that represents the dataset returned by the database.
17      * To keep memory overhead low, this class holds only the current row in memory.
18      * No prefetching of data is done, so the RecordCount() can return -1 ( which
19      * means recordcount not known).
20      */
21     class ADORecordSet {
22     /*
23      * public variables   
24      */
25     var $dataProvider = "native";
26     var $fields = false;     /* / holds the current row data */
27     var $blobSize = 64;     /* / any varchar/char field this size or greater is treated as a blob */
28                             /* / in other words, we use a text area for editting. */
29     var $canSeek = false;     /* / indicates that seek is supported */
30     var $sql;                 /* / sql text */
31     var $EOF = false;        /* / Indicates that the current record position is after the last record in a Recordset object.  */
32     
33     var $emptyTimeStamp = '&nbsp;'; /* / what to display when $time==0 */
34     var $emptyDate = '&nbsp;'; /* / what to display when $time==0 */
35     var $debug = false;
36     var $timeCreated=0;     /* / datetime in Unix format rs created -- for cached recordsets */
37
38     var $bind = false;         /* / used by Fields() to hold array - should be private? */
39     var $fetchMode;            /* / default fetch mode */
40     var $connection = false; /* / the parent connection */
41     /*
42      *    private variables   
43      */
44     var $_numOfRows = -1;    /** number of rows, or -1 */
45     var $_numOfFields = -1;    /** number of fields in recordset */
46     var $_queryID = -1;        /** This variable keeps the result link identifier.    */
47     var $_currentRow = -1;    /** This variable keeps the current row in the Recordset.    */
48     var $_closed = false;     /** has recordset been closed */
49     var $_inited = false;     /** Init() should only be called once */
50     var $_obj;                 /** Used by FetchObj */
51     var $_names;            /** Used by FetchObj */
52     
53     var $_currentPage = -1;    /** Added by Iván Oliva to implement recordset pagination */
54     var $_atFirstPage = false;    /** Added by Iván Oliva to implement recordset pagination */
55     var $_atLastPage = false;    /** Added by Iván Oliva to implement recordset pagination */
56     var $_lastPageNo = -1;
57     var $_maxRecordCount = 0;
58     var $dateHasTime = false;
59     
60     /**
61      * Constructor
62      *
63      * @param queryID      this is the queryID returned by ADOConnection->_query()
64      *
65      */
66     function ADORecordSet($queryID)
67     {
68         $this->_queryID = $queryID;
69     }
70     
71     
72     
73     function Init()
74     {
75         if ($this->_inited) return;
76         $this->_inited = true;
77         if ($this->_queryID) @$this->_initrs();
78         else {
79             $this->_numOfRows = 0;
80             $this->_numOfFields = 0;
81         }
82         if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
83             $this->_currentRow = 0;
84             if ($this->EOF = ($this->_fetch() === false)) {
85                 $this->_numOfRows = 0; /*  _numOfRows could be -1 */
86             }
87         } else {
88             $this->EOF = true;
89         }
90     }
91     
92     
93     /**
94      * Generate a SELECT tag string from a recordset, and return the string.
95      * If the recordset has 2 cols, we treat the 1st col as the containing
96      * the text to display to the user, and 2nd col as the return value. Default
97      * strings are compared with the FIRST column.
98      *
99      * @param name          name of SELECT tag
100      * @param [defstr]        the value to hilite. Use an array for multiple hilites for listbox.
101      * @param [blank1stItem]    true to leave the 1st item in list empty
102      * @param [multiple]        true for listbox, false for popup
103      * @param [size]        #rows to show for listbox. not used by popup
104      * @param [selectAttr]        additional attributes to defined for SELECT tag.
105      *                useful for holding javascript onChange='...' handlers.
106      & @param [compareFields0]    when we have 2 cols in recordset, we compare the defstr with
107      *                column 0 (1st col) if this is true. This is not documented.
108      *
109      * @return HTML
110      *
111      * changes by glen.davies@cce.ac.nz to support multiple hilited items
112      */
113     function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
114             $size=0, $selectAttr='',$compareFields0=true)
115     {
116         include_once(ADODB_DIR.'/adodb-lib.inc.php');
117         return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
118             $size, $selectAttr,$compareFields0);
119     }
120     
121     /**
122      * Generate a SELECT tag string from a recordset, and return the string.
123      * If the recordset has 2 cols, we treat the 1st col as the containing
124      * the text to display to the user, and 2nd col as the return value. Default
125      * strings are compared with the SECOND column.
126      *
127      */
128     function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')   
129     {
130         include_once(ADODB_DIR.'/adodb-lib.inc.php');
131         return _adodb_getmenu($this,$name,$defstr,$blank1stItem,$multiple,
132             $size, $selectAttr,false);
133     }
134
135
136     /**
137      * return recordset as a 2-dimensional array.
138      *
139      * @param [nRows]  is the number of rows to return. -1 means every row.
140      *
141      * @return an array indexed by the rows (0-based) from the recordset
142      */
143     function GetArray($nRows = -1)
144     {
145     global $ADODB_EXTENSION; if ($ADODB_EXTENSION) return adodb_getall($this,$nRows);
146         
147         $results = array();
148         $cnt = 0;
149         while (!$this->EOF && $nRows != $cnt) {
150             $results[] = $this->fields;
151             $this->MoveNext();
152             $cnt++;
153         }
154         return $results;
155     }
156     
157     /*
158     * Some databases allow multiple recordsets to be returned. This function
159     * will return true if there is a next recordset, or false if no more.
160     */
161     function NextRecordSet()
162     {
163         return false;
164     }
165     
166     /**
167      * return recordset as a 2-dimensional array.
168      * Helper function for ADOConnection->SelectLimit()
169      *
170      * @param offset    is the row to start calculations from (1-based)
171      * @param [nrows]    is the number of rows to return
172      *
173      * @return an array indexed by the rows (0-based) from the recordset
174      */
175     function GetArrayLimit($nrows,$offset=-1)
176     {   
177         if ($offset <= 0) {
178             return $this->GetArray($nrows);
179         }
180         
181         $this->Move($offset);
182         
183         $results = array();
184         $cnt = 0;
185         while (!$this->EOF && $nrows != $cnt) {
186             $results[$cnt++] = $this->fields;
187             $this->MoveNext();
188         }
189         
190         return $results;
191     }
192     
193     
194     /**
195      * Synonym for GetArray() for compatibility with ADO.
196      *
197      * @param [nRows]  is the number of rows to return. -1 means every row.
198      *
199      * @return an array indexed by the rows (0-based) from the recordset
200      */
201     function GetRows($nRows = -1)
202     {
203         return $this->GetArray($nRows);
204     }
205     
206     /**
207      * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
208      * The first column is treated as the key and is not included in the array.
209      * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
210      * $force_array == true.
211      *
212      * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
213      *     array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
214      *     read the source.
215      *
216      * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
217      * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
218      *
219      * @return an associative array indexed by the first column of the array,
220      *     or false if the  data has less than 2 cols.
221      */
222     function GetAssoc($force_array = false, $first2cols = false) {
223         $cols = $this->_numOfFields;
224         if ($cols < 2) {
225             return false;
226         }
227         $numIndex = isset($this->fields[0]);
228         $results = array();
229         
230         if (!$first2cols && ($cols > 2 || $force_array)) {
231             if ($numIndex) {
232                 while (!$this->EOF) {
233                     $results[trim($this->fields[0])] = array_slice($this->fields, 1);
234                     $this->MoveNext();
235                 }
236             } else {
237                 while (!$this->EOF) {
238                     $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
239                     $this->MoveNext();
240                 }
241             }
242         } else {
243             /*  return scalar values */
244             if ($numIndex) {
245                 while (!$this->EOF) {
246                 /*  some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string */
247                     $results[trim(($this->fields[0]))] = $this->fields[1];
248                     $this->MoveNext();
249                 }
250             } else {
251                 while (!$this->EOF) {
252                 /*  some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string */
253                     $v1 = trim(reset($this->fields));
254                     $v2 = ''.next($this->fields);
255                     $results[$v1] = $v2;
256                     $this->MoveNext();
257                 }
258             }
259         }
260         return $results;
261     }
262     
263     
264     /**
265      *
266      * @param v      is the character timestamp in YYYY-MM-DD hh:mm:ss format
267      * @param fmt     is the format to apply to it, using date()
268      *
269      * @return a timestamp formated as user desires
270      */
271     function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
272     {
273         $tt = $this->UnixTimeStamp($v);
274         /*  $tt == -1 if pre TIMESTAMP_FIRST_YEAR */
275         if (($tt === false || $tt == -1) && $v != false) return $v;
276         if ($tt == 0) return $this->emptyTimeStamp;
277         return adodb_date($fmt,$tt);
278     }
279     
280     
281     /**
282      * @param v      is the character date in YYYY-MM-DD format, returned by database
283      * @param fmt     is the format to apply to it, using date()
284      *
285      * @return a date formated as user desires
286      */
287     function UserDate($v,$fmt='Y-m-d')
288     {
289         $tt = $this->UnixDate($v);
290         /*  $tt == -1 if pre TIMESTAMP_FIRST_YEAR */
291         if (($tt === false || $tt == -1) && $v != false) return $v;
292         else if ($tt == 0) return $this->emptyDate;
293         else if ($tt == -1) { /*  pre-TIMESTAMP_FIRST_YEAR */
294         }
295         return adodb_date($fmt,$tt);
296     
297     }
298     
299     
300     /**
301      * @param $v is a date string in YYYY-MM-DD format
302      *
303      * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
304      */
305     function UnixDate($v)
306     {
307         
308         if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
309             ($v), $rr)) return false;
310             
311         if ($rr[1] <= 1903) return 0;
312         /*  h-m-s-MM-DD-YY */
313         return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
314     }
315     
316
317     /**
318      * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
319      *
320      * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
321      */
322     function UnixTimeStamp($v)
323     {
324         
325         if (!preg_match(
326             "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
327             ($v), $rr)) return false;
328         if ($rr[1] <= 1903 && $rr[2]<= 1) return 0;
329     
330         /*  h-m-s-MM-DD-YY */
331         if (!isset($rr[5])) return  adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
332         return  @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
333     }
334     
335     
336     /**
337     * PEAR DB Compat - do not use internally
338     */
339     function Free()
340     {
341         return $this->Close();
342     }
343     
344     
345     /**
346     * PEAR DB compat, number of rows
347     */
348     function NumRows()
349     {
350         return $this->_numOfRows;
351     }
352     
353     
354     /**
355     * PEAR DB compat, number of cols
356     */
357     function NumCols()
358     {
359         return $this->_numOfFields;
360     }
361     
362     /**
363     * Fetch a row, returning false if no more rows.
364     * This is PEAR DB compat mode.
365     *
366     * @return false or array containing the current record
367     */
368     function FetchRow()
369     {
370         if ($this->EOF) return false;
371         $arr = $this->fields;
372         $this->_currentRow++;
373         if (!$this->_fetch()) $this->EOF = true;
374         return $arr;
375     }
376     
377     
378     /**
379     * Fetch a row, returning PEAR_Error if no more rows.
380     * This is PEAR DB compat mode.
381     *
382     * @return DB_OK or error object
383     */
384     function FetchInto(&$arr)
385     {
386         if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
387         $arr = $this->fields;
388         $this->MoveNext();
389         return 1; /*  DB_OK */
390     }
391     
392     
393     /**
394      * Move to the first row in the recordset. Many databases do NOT support this.
395      *
396      * @return true or false
397      */
398     function MoveFirst()
399     {
400         if ($this->_currentRow == 0) return true;
401         return $this->Move(0);           
402     }           
403
404     
405     /**
406      * Move to the last row in the recordset.
407      *
408      * @return true or false
409      */
410     function MoveLast()
411     {
412         if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
413         if ($this->EOF) return false;
414         while (!$this->EOF) {
415             $f = $this->fields;
416             $this->MoveNext();
417         }
418         $this->fields = $f;
419         $this->EOF = false;
420         return true;
421     }
422     
423     
424     /**
425      * Move to next record in the recordset.
426      *
427      * @return true if there still rows available, or false if there are no more rows (EOF).
428      */
429     function MoveNext()
430     {
431         if (!$this->EOF) {
432             $this->_currentRow++;
433             if ($this->_fetch()) return true;
434         }
435         $this->EOF = true;
436         /* -- tested error handling when scrolling cursor -- seems useless.
437         $conn = $this->connection;
438         if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
439             $fn = $conn->raiseErrorFn;
440             $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
441         }
442         */
443         return false;
444     }   
445     
446     /**
447      * Random access to a specific row in the recordset. Some databases do not support
448      * access to previous rows in the databases (no scrolling backwards).
449      *
450      * @param rowNumber is the row to move to (0-based)
451      *
452      * @return true if there still rows available, or false if there are no more rows (EOF).
453      */
454     function Move($rowNumber = 0)
455     {
456         $this->EOF = false;
457         if ($rowNumber == $this->_currentRow) return true;
458         if ($rowNumber >= $this->_numOfRows)
459                if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
460                   
461         if ($this->canSeek) {
462     
463             if ($this->_seek($rowNumber)) {
464                 $this->_currentRow = $rowNumber;
465                 if ($this->_fetch()) {
466                     return true;
467                 }
468             } else {
469                 $this->EOF = true;
470                 return false;
471             }
472         } else {
473             if ($rowNumber < $this->_currentRow) return false;
474             global $ADODB_EXTENSION;
475             if ($ADODB_EXTENSION) {
476                 while (!$this->EOF && $this->_currentRow < $rowNumber) {
477                     adodb_movenext($this);
478                 }
479             } else {
480             
481                 while (! $this->EOF && $this->_currentRow < $rowNumber) {
482                     $this->_currentRow++;
483                     
484                     if (!$this->_fetch()) $this->EOF = true;
485                 }
486             }
487             return !($this->EOF);
488         }
489         
490         $this->fields = false;   
491         $this->EOF = true;
492         return false;
493     }
494     
495         
496     /**
497      * Get the value of a field in the current row by column name.
498      * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
499      *
500      * @param colname  is the field to access
501      *
502      * @return the value of $colname column
503      */
504     function Fields($colname)
505     {
506         return $this->fields[$colname];
507     }
508     
509     function GetAssocKeys($upper=true)
510     {
511         $this->bind = array();
512         for ($i=0; $i < $this->_numOfFields; $i++) {
513             $o = $this->FetchField($i);
514             if ($upper === 2) $this->bind[$o->name] = $i;
515             else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
516         }
517     }
518     
519   /**
520    * Use associative array to get fields array for databases that do not support
521    * associative arrays. Submitted by Paolo S. Asioli paolo.asioli@libero.it
522    *
523    * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
524    * before you execute your SQL statement, and access $rs->fields['col'] directly.
525    *
526    * $upper  0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
527    */
528     function GetRowAssoc($upper=1)
529     {
530     
531            if (!$this->bind) {
532             $this->GetAssocKeys($upper);
533         }
534         
535         $record = array();
536         foreach($this->bind as $k => $v) {
537             $record[$k] = $this->fields[$v];
538         }
539
540         return $record;
541     }
542     
543     
544     /**
545      * Clean up recordset
546      *
547      * @return true or false
548      */
549     function Close()
550     {
551         /*  free connection object - this seems to globally free the object */
552         /*  and not merely the reference, so don't do this... */
553         /*  $this->connection = fa