root/dev/common/includes/class.db.mysqli.php @ 424

Revision 424, 25.7 KB (checked in by kovell, 11 years ago)

Fixes: killlists with both involved corps and alliances work. Memory use reduced.

Line 
1<?php
2// mssql: select SCOPE_IDENTITY() AS id
3// postgresql: INSERT INTO mytable (lastname) VALUES ('Cher') RETURNING id;
4
5//! mysqli connection class.
6//! Establishes the connection to the database.
7class DBConnection_mysqli
8{
9    //! Set up a mysqli DB connection.
10    function DBConnection_mysqli()
11    {
12        static $conn_id;
13
14        if ($conn_id)
15        {
16            $this->id_ = $conn_id;
17            return;
18        }
19        if(defined('DB_PORT'))
20        {
21            if (!$this->id_ = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT))
22            die("Unable to connect to mysql database.");
23            $this->id_->set_charset('utf8');
24        }
25        else
26        {
27            if (!$this->id_ = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME))
28            die("Unable to connect to mysql database.");
29            $this->id_->set_charset('utf8');
30        }
31
32        //mysqli_select_db(DB_NAME);
33        $conn_id = $this->id_;
34    }
35    //! Return the connection id for this connection. Used for connection specific commands.
36    function id()
37    {
38        return $this->id_;
39    }
40    //! Return the number of rows affected by a query.
41    function affectedRows()
42    {
43        return mysqli_affected_rows($this->id_);
44    }
45}
46//! mysqli uncached query class. Manages SQL queries to a MySQL DB using mysqli.
47class DBNormalQuery_mysqli
48{
49    //! Prepare a connection for a new mysqli query.
50    function DBNormalQuery_mysqli()
51    {
52        $this->executed_ = false;
53        $this->dbconn_ = new DBConnection_mysqli;
54        static $totalexectime = 0;
55                $this->totalexectime_ = &$totalexectime;
56    }
57    //! Return the count of queries performed.
58
59    /*!
60     * \param $increase if true then increment the count.
61     * \return the count of queries so far.
62     */
63    function queryCount($increase = false)
64    {
65        static $count;
66
67        if ($increase)
68        {
69            $count++;
70        }
71
72        return $count;
73    }
74    //! Return the count of cached queries performed - 0 for uncache queries.
75    function queryCachedCount($increase = false)
76    {
77        return 0;
78    }
79    //! Execute an SQL string.
80
81    /*
82     * If DB_HALTONERROR is set then this will exit on an error.
83     * \return false on error or true if successful.
84     */
85    function execute($sql)
86    {
87        $t1 = strtok(microtime(), ' ') + strtok('');
88
89                if(isset($this->resid_)) $this->resid_->free();
90
91        $this->resid_ = mysqli_query($this->dbconn_->id(),$sql);
92
93        if ($this->resid_ === false || $this->dbconn_->id()->errno)
94        {
95            if(defined('KB_PROFILE'))
96                        {
97                                DBDebug::recordError("Database error: ".$this->dbconn_->id()->error);
98                                DBDebug::recordError("SQL: ".$sql);
99                        }
100            if (defined('DB_HALTONERROR') && DB_HALTONERROR)
101            {
102                echo "Database error: " . $this->dbconn_->id()->error . "<br>";
103                echo "SQL: " . $sql . "<br>";
104                exit;
105            }
106            else
107            {
108                return false;
109            }
110        }
111
112        $this->exectime_ = strtok(microtime(), ' ') + strtok('') - $t1;
113        $this->totalexectime_ += $this->exectime_;
114        $this->executed_ = true;
115
116        if(defined('KB_PROFILE')) DBDebug::profile($sql);
117
118        $this->queryCount(true);
119
120        return true;
121    }
122    //! Return the number of rows returned by the last query.
123    function recordCount()
124    {
125        if ($this->resid_)
126        {
127            return $this->resid_->num_rows;
128        }
129        return false;
130    }
131    //! Return the next row of results from the last query.
132    function getRow()
133    {
134        if ($this->resid_)
135        {
136            return $this->resid_->fetch_assoc();
137        }
138        return false;
139    }
140    //! Reset list of results to return the first row from the last query.
141    function rewind()
142    {
143        @mysqli_data_seek($this->resid_, 0);
144    }
145    //! Return the auto-increment ID from the last insert operation.
146    function getInsertID()
147    {
148        return $this->dbconn_->id()->insert_id;
149    }
150    //! Return the execution time of the last query.
151    function execTime()
152    {
153        return $this->exectime_;
154    }
155    //! Return true if a query has been executed or false if none has been.
156    function executed()
157    {
158        return $this->executed_;
159    }
160    //! Return the most recent error message for the DB connection.
161    function getErrorMsg()
162    {
163        $msg = $this->sql_ . "<br>";
164        $msg .= "Query failed. " . mysqli_error($this->dbconn_->id());
165
166        return $msg;
167    }
168    //! Set the autocommit status.
169
170    /*! The default of true commits after every query.
171     * If set to false the queries will not be commited until autocommit is set
172     * to true.
173     *  \param $commit The new autocommit status.
174     *  \return true on success and false on failure.
175     */
176    function autocommit($commit = true)
177    {
178        return $this->dbconn_->id()->autocommit($commit);
179    }
180    //! Rollback all queries in the current transaction.
181    function rollback()
182    {
183        return mysqli_rollback($this->dbconn_->id());
184    }
185}
186//! mysqli file-cached query class. Manages SQL queries to a MySQL DB using mysqli.
187class DBCachedQuery_mysqli
188{
189    //! Set up a mysqli cached query object with default values.
190    function DBCachedQuery_mysqli()
191    {
192        static $totalexectime = 0;
193                $this->totalexectime_ = &$totalexectime;
194        $this->executed_ = false;
195        $this->_cache = array();
196        $this->_cached = false;
197
198        // this is the minimum runtime a query has to run to be
199        // eligible for caching in seconds
200        $this->_minruntime = 0.1;
201
202        // maximum size of a cached result set (512kB)
203        $this->_maxcachesize = 524288;
204        $this->d = true;
205    }
206    //! Check if this query has been cached and the cache valid.
207
208    /*
209     * \return true if this query has been cached and the cache is valid.
210     */
211    function checkCache()
212    {
213        // only cache selects
214        // we don't use select ... into so there is no problem
215        $this->_sql = str_replace(array("\r\n", "\n"), ' ', $this->_sql);
216        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
217        {
218            // this is no select, update the table
219            $this->markAffectedTables();
220            return false;
221        }
222
223        if (file_exists(KB_CACHEDIR.'/qcache_qry_'.$this->_hash))
224        {
225            $this->_mtime = filemtime(KB_CACHEDIR.'/qcache_qry_'.$this->_hash);
226            /// Remove cached queries more than an hour old.
227            if (time() - $this->_mtime > 3600 )
228            {
229                unlink(KB_CACHEDIR.'/qcache_qry_'.$this->_hash);
230                return false;
231            }
232            if ($this->isCacheValid())
233            {
234                return true;
235            }
236        }
237
238        return false;
239    }
240
241    //! Extract all tables affected by a database modification.
242
243    //! The resulting list is set internally to this object.
244    function parseSQL()
245    {
246        // gets all involved tables for a select statement
247        $text = strtolower($this->_sql).' ';
248
249        // we try to get the text from 'from' to 'where' because all involved
250        // tables are declared in that part
251        $from = strpos($text, 'from')+5;
252        if (!$to = strpos($text, 'where'))
253        {
254            $to = strlen($text);
255        }
256        $parse = trim(substr($text, $from, $to-$from));
257
258        $tables = array();
259        if (strpos($parse, ',') !== false)
260        {
261            // , is a synonym for join so we'll replace them
262            $parse = str_replace(',', ' join ', $parse);
263        }
264
265        $parse = 'join '.$parse;
266        if (strpos($parse, 'join'))
267        {
268            // if this query is a join we parse it with regexp to get all tables
269            preg_match_all('/join (.*?) /', $parse, $match);
270            $tables = $match[1];
271        }
272        else
273        {
274            // no join so it is hopefully a simple table select
275            $tables[] = $parse;
276        }
277
278        $this->_usedtables = $tables;
279    }
280    //! Check if the cached query is valid.
281
282    /*! Determines whether the tables used by a query have been modified
283     * since the query was cached
284     */
285    function isCacheValid()
286    {
287        // check if cachefiles are still valid
288
289        // first, we need to get all involved tables
290        $this->parseSQL();
291
292        foreach ($this->_usedtables as $table)
293        {
294            $file = KB_CACHEDIR.'/qcache_tbl_'.trim($table);
295            if (file_exists($file))
296            {
297                // if one of the tables is outdated, the query is outdated
298                if ($this->_mtime < filemtime($file))
299                {
300                    return false;
301                }
302            }
303        }
304        return true;
305    }
306    //! Marks all tables affected by a database modification
307    function markAffectedTables()
308    {
309        // this function invalidates cache files for touched tables
310        $text = trim(strtolower($this->_sql));
311        $text = str_replace(array('ignore','`', "\r\n", "\n"), '', $text);
312        $text = str_replace('(', ' (', $text);
313        $ta = preg_split('/\s/', $text, 0, PREG_SPLIT_NO_EMPTY);
314
315        // check for sql keywords and get the table from the appropriate position
316        $tables = array();
317        if ($ta[0] == 'update')
318        {
319            $tables[] = $ta[1];
320        }
321        elseif ($ta[0] == 'insert')
322        {
323            $tables[] = $ta[2];
324        }
325        elseif ($ta[0] == 'replace')
326        {
327            $tables[] = $ta[2];
328        }
329        elseif ($ta[0] == 'delete')
330        {
331            $tables[] = $ta[2];
332        }elseif ($ta[0] == 'drop')
333        {
334            $tables[] = $ta[2];
335        }
336        elseif ($ta[0] == 'alter')
337        {
338            return false;
339        }
340        elseif ($ta[0] == 'create')
341        {
342            return false;
343        }
344        else
345        {
346            var_dump($ta);
347            trigger_error('No suitable handler for query found.',E_USER_WARNING);
348            return false;
349        }
350
351        foreach ($tables as $table)
352        {
353            $file = KB_CACHEDIR.'/qcache_tbl_'.$table;
354            @touch($file);
355        }
356        // refresh php's filestatcache so we dont get wrong timestamps on changed files
357        clearstatcache();
358    }
359    //! Generate the query cache.
360
361    //! Serialise a query and write to file.
362    function genCache()
363    {
364        // this function fetches all rows and writes the data into a textfile
365        // don't attemp to cache updates!
366        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
367        {
368            return false;
369        }
370
371        $bsize = 0;
372        while ($row = $this->getRow())
373        {
374            $this->_cache[] = $row;
375
376            // if the bytesize of the table exceeds the limit we'll abort
377            // the cache generation and leave this query unbuffered
378            $bsize += strlen(join('', $row));
379            if ($bsize > $this->_maxcachesize)
380            {
381                $this->_cache[] = array();
382                $this->_cached = false;
383                $this->rewind();
384                return false;
385            }
386        }
387
388        // write data into textfile
389        file_put_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash, serialize($this->_cache));
390
391        $this->_cached = true;
392        $this->_currrow = 0;
393        $this->executed_ = true;
394    }
395    //! Read a cached query from file.
396    function loadCache()
397    {
398        // loads the cachefile into the memory
399        $this->_cache = unserialize(file_get_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash));
400
401        $this->_cached = true;
402        $this->_currrow = 0;
403        $this->executed_ = true;
404    }
405
406    //! Execute an SQL string.
407
408    /*
409     * If DB_HALTONERROR is set then this will exit on an error.
410     * \return false on error or true if successful.
411     */
412    function execute($sql)
413    {
414        $this->_sql = trim($sql);
415        $this->_hash = md5($this->_sql);
416        $this->_cache = array();
417        $this->_cached = false;
418
419        if ($this->checkCache())
420        {
421            $this->loadCache();
422            $this->queryCachedCount(true);
423            return true;
424        }
425
426        // we got no or no valid cache so open the connection and run the query
427        $this->dbconn_ = new DBConnection_mysqli();
428                if(isset($this->resid_)) $this->resid_->free();
429
430                $t1 = strtok(microtime(), ' ') + strtok('');
431
432        $this->resid_ = mysqli_query($this->dbconn_->id(), $sql);
433
434        if (!$this->resid_ || $this->dbconn_->id()->errno)
435        {
436            if(defined('KB_PROFILE'))
437                        {
438                                DBDebug::recordError("Database error: ".$this->dbconn_->id()->error);
439                                DBDebug::recordError("SQL: ".$this->_sql);
440                        }
441            if (DB_HALTONERROR === true)
442            {
443                echo "Database error: ".$this->dbconn_->id()->error."<br/>";
444                echo "SQL: ".$this->_sql."<br/>";
445                exit;
446            }
447            else
448            {
449                return false;
450            }
451        }
452
453        $this->exectime_ = strtok(microtime(), ' ') + strtok('') - $t1;
454        $this->totalexectime_ += $this->exectime_;
455        $this->executed_ = true;
456
457        if(defined('KB_PROFILE')) DBDebug::profile($sql);
458
459        // if the query was too slow we'll fetch all rows and run it cached
460        if ($this->exectime_ > $this->_minruntime)
461        {
462            $this->genCache();
463                        // We will use the cached version now so free the mysqli resource.
464                        if($this->_cached)
465                        {
466                                $this->resid_->free();
467                                unset($this->resid_);
468                        }
469        }
470
471        $this->queryCount(true);
472        return true;
473    }
474
475    //! Return the count of queries performed.
476
477    /*!
478     * \param $increase if true then increment the count.
479     * \return the count of queries so far.
480     */
481    function queryCount($increase = false)
482    {
483        static $count;
484
485        if ($increase)
486        {
487            $count++;
488        }
489
490        return $count;
491    }
492    //! Return the count of cached queries performed.
493
494    /*!
495     * \param $increase if true then increment the count.
496     * \return the count of queries so far.
497     */
498    function queryCachedCount($increase = false)
499    {
500        static $count;
501
502        if ($increase)
503        {
504            $count++;
505        }
506
507        return $count;
508    }
509
510    //! Return the number of rows returned by the last query.
511    function recordCount()
512    {
513        if ($this->_cached)
514        {
515            return count($this->_cache);
516        }
517        elseif ($this->resid_)
518        {
519            return $this->resid_->num_rows;
520        }
521        return false;
522    }
523
524    //! Return the next row of results from the last query.
525    function getRow()
526    {
527        if ($this->_cached)
528        {
529            if (!isset($this->_cache[$this->_currrow]))
530            {
531                return false;
532            }
533            // return the current row and increase the pointer by one
534            return $this->_cache[$this->_currrow++];
535        }
536        if ($this->resid_)
537        {
538            return $this->resid_->fetch_assoc();
539        }
540        return false;
541    }
542
543    //! Reset list of results to return the first row from the last query.
544    function rewind()
545    {
546        if ($this->_cached)
547        {
548            $this->_currrow = 0;
549        }
550                @mysqli_data_seek($this->resid_, 0);
551    }
552
553    //! Return the auto-increment ID from the last insert operation.
554    function getInsertID()
555    {
556        return $this->dbconn_->id()->insert_id;
557    }
558
559    //! Return the execution time of the last query.
560    function execTime()
561    {
562        return $this->exectime_;
563    }
564
565    //! Return true if a query has been executed or false if none has been.
566    function executed()
567    {
568        return $this->executed_;
569    }
570
571    //! Return the most recent error message for the DB connection.
572    function getErrorMsg()
573    {
574        $msg = $this->sql_."<br>";
575        $msg .= "Query failed. ".mysqli_error($this->dbconn_->id());
576
577        return $msg;
578    }
579
580    //! Set the autocommit status.
581
582    /*! The default of true commits after every query.
583     * If set to false the queries will not be commited until autocommit is set
584     * to true.
585     *  \param $commit The new autocommit status.
586     *  \return true on success and false on failure.
587     */
588    function autocommit($commit = true)
589    {
590        if(!$this->dbconn_) $this->dbconn_ = new DBConnection_mysqli();
591        return $this->dbconn_->id()->autocommit($commit);
592    }
593
594    //! Rollback all queries in the current transaction.
595    function rollback()
596    {
597        // if there's no connection to the db then there's nothing to roll back
598        if(!$this->dbconn_) return true;
599        return $this->dbconn_->id()->rollback();
600    }
601}
602
603//! mysqli memcached query class. Manages SQL queries to a MySQL DB using mysqli.
604class DBMemcachedQuery_mysqli
605{
606    function DBMemcachedQuery_mysqli()
607    {
608        static $totalexectime = 0;
609                $this->totalexectime_ = &$totalexectime;
610        $this->executed_ = false;
611        $this->_cache = array();
612        $this->_cached = false;
613
614        // this is the minimum runtime a query has to run to be
615        // eligible for caching in seconds
616        $this->_minruntime = 0.1;
617
618        // maximum size of a cached result set (512kB)
619        $this->_maxcachesize = 524288;
620        $this->d = true;
621    }
622
623    //! Check if this query has been cached.
624
625    /*
626     * \return true if this query has been cached.
627     */
628    function checkCache()
629    {
630        global $mc;
631
632        // only cache selects
633        // we don't use select ... into so there is no problem
634        $this->_sql = str_replace(array("\r\n", "\n"), ' ', $this->_sql);
635        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
636        return false;
637
638        $cached = $mc->get(KB_SITE . '_sql_' . $this->_hash);
639        if($cached) {
640            return true;
641        }
642
643        return false;
644    }
645    //! Extract all tables affected by a database modification.
646
647    //! The resulting list is set internally to this object.
648    function parseSQL()
649    {
650        // gets all involved tables for a select statement
651        $text = strtolower($this->_sql).' ';
652
653        // we try to get the text from 'from' to 'where' because all involved
654        // tables are declared in that part
655        $from = strpos($text, 'from')+5;
656        if (!$to = strpos($text, 'where'))
657        {
658            $to = strlen($text);
659        }
660        $parse = trim(substr($text, $from, $to-$from));
661
662        $tables = array();
663        if (strpos($parse, ',') !== false)
664        {
665            // , is a synonym for join so we'll replace them
666            $parse = str_replace(',', ' join ', $parse);
667        }
668
669        $parse = 'join '.$parse;
670        if (strpos($parse, 'join'))
671        {
672            // if this query is a join we parse it with regexp to get all tables
673            preg_match_all('/join (.*?) /', $parse, $match);
674            $tables = $match[1];
675        }
676        else
677        {
678            // no join so it is hopefully a simple table select
679            $tables[] = $parse;
680        }
681
682        $this->_usedtables = $tables;
683    }
684
685    function genCache()
686    {
687        global $mc;
688
689        // this function fetches all rows and writes the data into a textfile
690
691        // don't attemp to cache updates!
692        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
693        {
694            return false;
695        }
696
697        $bsize = 0;
698        while ($row = $this->getRow())
699        {
700            $this->_cache[] = $row;
701
702            $bsize += strlen(join('', $row));
703            if ($bsize > $this->_maxcachesize)
704            {
705                $this->_cache[] = array();
706                $this->_cached = false;
707                $this->rewind();
708                return false;
709            }
710
711        }
712
713        // write data into textfile
714        $mc->set(KB_SITE . '_sql_' . $this->_hash, $this->_cache, 0, 600);
715
716        $this->_cached = true;
717        $this->_currrow = 0;
718        $this->executed_ = true;
719    }
720
721    //! Execute an SQL string.
722
723    /*
724     * If DB_HALTONERROR is set then this will exit on an error.
725     * \return false on error or true if successful.
726     */
727    function execute($sql)
728    {
729        global $mc;
730
731        $this->_sql = trim($sql);
732        $this->_hash = md5($this->_sql);
733        $this->_cache = array();
734        $this->_cached = false;
735
736        $cached = $mc->get(KB_SITE . '_sql_' . $this->_hash);
737        if($cached) {
738            $this->_cache = $cached;
739            $this->_cached = true;
740            $this->_currrow = 0;
741            $this->executed_ = true;
742            $this->queryCachedCount(true);
743            return true;
744        }
745
746        // we got no or no valid cache so open the connection and run the query
747        $this->dbconn_ = new DBConnection_mysqli;
748                if(isset($this->resid_)) $this->resid_->free();
749
750        $t1 = strtok(microtime(), ' ') + strtok('');
751
752        $this->resid_ = $this->dbconn_->id()->query($sql);
753
754        if (!$this->resid_ || $this->dbconn_->id()->errno)
755        {
756            if(defined('KB_PROFILE'))
757                        {
758                                DBDebug::recordError("Database error: ".$this->dbconn_->id()->error);
759                                DBDebug::recordError("SQL: ".$this->_sql);
760                        }
761            if (DB_HALTONERROR === true)
762            {
763                echo "Database error: ".$this->dbconn_->id()->error."<br/>";
764                echo "SQL: ".$this->_sql."<br/>";
765                exit;
766            }
767            else
768            {
769                return false;
770            }
771        }
772
773        $this->exectime_ = strtok(microtime(), ' ') + strtok('') - $t1;
774        $this->totalexectime_ += $this->exectime_;
775        $this->executed_ = true;
776
777        if(defined('KB_PROFILE')) DBDebug::profile($sql);
778
779        // if the query was too slow we'll fetch all rows and run it cached
780        $this->genCache();
781
782        $this->queryCount(true);
783        return true;
784    }
785
786    //! Return the count of queries performed.
787
788    /*!
789     * \param $increase if true then increment the count.
790     * \return the count of queries so far.
791     */
792    function queryCount($increase = false)
793    {
794        static $count;
795
796        if ($increase)
797        {
798            $count++;
799        }
800
801        return $count;
802    }
803
804    //! Return the count of cached queries performed.
805
806    /*!
807     * \param $increase if true then increment the count.
808     * \return the count of queries so far.
809     */
810    function queryCachedCount($increase = false)
811    {
812        static $count;
813
814        if ($increase)
815        {
816            $count++;
817        }
818
819        return $count;
820    }
821
822    //! Return the number of rows returned by the last query.
823    function recordCount()
824    {
825        if ($this->_cached)
826        {
827            return count($this->_cache);
828        }
829        elseif ($this->resid_)
830        {
831            return $this->resid_->num_rows;
832        }
833        return false;
834    }
835
836    //! Return the next row of results from the last query.
837    function getRow()
838    {
839        if ($this->_cached)
840        {
841            if (!isset($this->_cache[$this->_currrow]))
842            {
843                return false;
844            }
845            // return the current row and increase the pointer by one
846            return $this->_cache[$this->_currrow++];
847        }
848        if ($this->resid_)
849        {
850            return $this->resid_->fetch_assoc();
851        }
852        return false;
853    }
854
855    //! Reset list of results to return the first row from the last query.
856    function rewind()
857    {
858        if ($this->_cached)
859        {
860            $this->_currrow = 0;
861        }
862        @mysqli_data_seek($this->resid_, 0);
863    }
864
865    //! Return the auto-increment ID from the last insert operation.
866    function getInsertID()
867    {
868        return $this->dbconn_->id()->insert_id;
869    }
870
871    //! Return the execution time of the last query.
872    function execTime()
873    {
874        return $this->exectime_;
875    }
876
877    //! Return true if a query has been executed or false if none has been.
878    function executed()
879    {
880        return $this->executed_;
881    }
882
883    //! Return the most recent error message for the DB connection.
884    function getErrorMsg()
885    {
886        $msg = $this->sql_."<br>";
887        $msg .= "Query failed. ".mysqli_error($this->dbconn_->id());
888
889        return $msg;
890    }
891
892    //! Set the autocommit status.
893
894    /*! The default of true commits after every query.
895     * If set to false the queries will not be commited until autocommit is set
896     * to true.
897     *  \param $commit The new autocommit status.
898     *  \return true on success and false on failure.
899     */
900    function autocommit($commit = true)
901    {
902        if(!$this->dbconn_) $this->dbconn_ = new DBConnection_mysqli();
903        return $this->dbconn_->id()->autocommit($commit);
904    }
905
906    //! Rollback all queries in the current transaction.
907    function rollback()
908    {
909        // if there's no connection to the db then there's nothing to roll back
910        if(!$this->dbconn_) return true;
911        return $this->dbconn_->id()->rollback();
912    }
913}
914?>
Note: See TracBrowser for help on using the browser.