Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
57.97% covered (warning)
57.97%
291 / 502
31.25% covered (danger)
31.25%
10 / 32
CRAP
0.00% covered (danger)
0.00%
0 / 1
SeedDMS_Core_DatabaseAccess
57.97% covered (warning)
57.97%
291 / 502
31.25% covered (danger)
31.25%
10 / 32
3139.64
0.00% covered (danger)
0.00%
0 / 1
 TableList
62.50% covered (warning)
62.50%
10 / 16
0.00% covered (danger)
0.00%
0 / 1
7.90
 hasTable
53.33% covered (warning)
53.33%
8 / 15
0.00% covered (danger)
0.00%
0 / 1
9.66
 ViewList
62.50% covered (warning)
62.50%
10 / 16
0.00% covered (danger)
0.00%
0 / 1
7.90
 __construct
82.61% covered (warning)
82.61%
19 / 23
0.00% covered (danger)
0.00%
0 / 1
4.08
 getDriver
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 useViews
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __destruct
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
5.67
 setLogFp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 connect
59.38% covered (warning)
59.38%
19 / 32
0.00% covered (danger)
0.00%
0 / 1
27.14
 ensureConnected
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 qstr
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 rbt
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 concat
55.56% covered (warning)
55.56%
5 / 9
0.00% covered (danger)
0.00%
0 / 1
5.40
 getResultArray
76.92% covered (warning)
76.92%
10 / 13
0.00% covered (danger)
0.00%
0 / 1
7.60
 getResult
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
9.36
 startTransaction
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
4.13
 rollbackTransaction
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
4.13
 commitTransaction
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
4.13
 inTransaction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInsertID
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getErrorMsg
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getErrorNo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __createTemporaryTable
49.29% covered (danger)
49.29%
69 / 140
0.00% covered (danger)
0.00%
0 / 1
260.26
 __dropTemporaryTable
61.11% covered (warning)
61.11%
11 / 18
0.00% covered (danger)
0.00%
0 / 1
13.76
 __createView
50.83% covered (warning)
50.83%
61 / 120
0.00% covered (danger)
0.00%
0 / 1
240.79
 createTemporaryTable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 dropTemporaryTable
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getDateExtract
36.36% covered (danger)
36.36%
4 / 11
0.00% covered (danger)
0.00%
0 / 1
15.28
 getCurrentDatetime
44.44% covered (danger)
44.44%
4 / 9
0.00% covered (danger)
0.00%
0 / 1
6.74
 getCurrentTimestamp
44.44% covered (danger)
44.44%
4 / 9
0.00% covered (danger)
0.00%
0 / 1
6.74
 castToText
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
2.26
 createDump
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2/**
3 * Implementation of database access using PDO
4 *
5 * @category   DMS
6 * @package    SeedDMS_Core
7 * @license    GPL 2
8 * @version    @version@
9 * @author     Uwe Steinmann <uwe@steinmann.cx>
10 * @copyright  Copyright (C) 2012 Uwe Steinmann
11 * @version    Release: @package_version@
12 */
13/** @noinspection PhpUndefinedClassInspection */
14
15/**
16 * Class to represent the database access for the document management
17 * This class uses PDO for the actual database access.
18 *
19 * @category   DMS
20 * @package    SeedDMS_Core
21 * @author     Uwe Steinmann <uwe@steinmann.cx>
22 * @copyright  Copyright (C) 2012 Uwe Steinmann
23 * @version    Release: @package_version@
24 */
25class SeedDMS_Core_DatabaseAccess {
26    /**
27     * @var boolean set to true for debug mode
28     */
29    public $_debug;
30
31    /**
32     * @var string name of database driver (mysql or sqlite)
33     */
34    protected $_driver;
35
36    /**
37     * @var string name of hostname
38     */
39    protected $_hostname;
40
41    /**
42     * @var int port number of database
43     */
44    protected $_port;
45
46    /**
47     * @var string name of database
48     */
49    protected $_database;
50
51    /**
52     * @var string name of database user
53     */
54    protected $_user;
55
56    /**
57     * @var string password of database user
58     */
59    protected $_passw;
60
61    /**
62     * @var object internal database connection
63     */
64    private $_conn;
65
66    /**
67     * @var boolean set to true if connection to database is established
68     */
69    private $_connected;
70
71    /**
72     * @var boolean set to true if temp. table for tree view has been created
73     */
74    private  $_ttreviewid;
75
76    /**
77     * @var boolean set to true if temp. table for approvals has been created
78     */
79    private $_ttapproveid;
80
81    /**
82     * @var boolean set to true if temp. table for doc status has been created
83     */
84    private $_ttstatid;
85
86    /**
87     * @var boolean set to true if temp. table for doc content has been created
88     */
89    private $_ttcontentid;
90
91    /**
92     * @var boolean set to true if in a database transaction
93     */
94    private $_intransaction;
95
96    /**
97     * @var string set a valid file name for logging all sql queries
98     */
99    private $_logfile;
100
101    /**
102     * @var resource file pointer of log file
103     */
104    private $_logfp;
105
106    /**
107     * @var boolean set to true if views instead of temp. tables shall be used
108     */
109    private $_useviews;
110
111    /**
112     * Return list of all database tables
113     *
114     * This function is used to retrieve a list of database tables for backup
115     *
116     * @return string[]|bool list of table names
117     */
118    function TableList() { /* {{{ */
119        switch($this->_driver) {
120            case 'mysql':
121                $sql = "SELECT `TABLE_NAME` AS `name` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA`='".$this->_database."' AND `TABLE_TYPE`='BASE TABLE'";
122                break;
123            case 'sqlite':
124                $sql = "SELECT tbl_name AS name FROM sqlite_master WHERE type='table'";
125                break;
126            case 'pgsql':
127                $sql = "select tablename as name from pg_catalog.pg_tables where schemaname='public'";
128                break;
129            default:
130                return false;
131        }
132        $arr = $this->getResultArray($sql);
133        $res = array();
134        foreach($arr as $tmp)
135            $res[] = $tmp['name'];
136        return $res;
137    }    /* }}} */
138
139    /**
140     * Check if database has a table
141     *
142     * This function will check if the database has a table with the given table name
143     *
144     * @return bool true if table exists, otherwise false
145     */
146    function hasTable($name) { /* {{{ */
147        switch($this->_driver) {
148            case 'mysql':
149                $sql = "SELECT `TABLE_NAME` AS `name` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA`='".$this->_database."' AND `TABLE_TYPE`='BASE TABLE' AND `TABLE_NAME`=".$this->qstr($name);
150                break;
151            case 'sqlite':
152                $sql = "SELECT tbl_name AS name FROM sqlite_master WHERE type='table' AND `tbl_name`=".$this->qstr($name);
153                break;
154            case 'pgsql':
155                $sql = "SELECT tablename AS name FROM pg_catalog.pg_tables WHERE schemaname='public' AND tablename=".$this->qstr($name);
156                break;
157            default:
158                return false;
159        }
160        $arr = $this->getResultArray($sql);
161        if($arr)
162            return true;
163        return false;
164    }    /* }}} */
165
166    /**
167     * Return list of all database views
168     *
169     * This function is used to retrieve a list of database views
170     *
171     * @return array list of view names
172     */
173    public function ViewList() { /* {{{ */
174        switch($this->_driver) {
175            case 'mysql':
176                $sql = "select TABLE_NAME as name from information_schema.views where TABLE_SCHEMA='".$this->_database."'";
177                break;
178            case 'sqlite':
179                $sql = "select tbl_name as name from sqlite_master where type='view'";
180                break;
181            case 'pgsql':
182                $sql = "select viewname as name from pg_catalog.pg_views where schemaname='public'";
183                break;
184            default:
185                return false;
186        }
187        $arr = $this->getResultArray($sql);
188        $res = array();
189        foreach($arr as $tmp)
190            $res[] = $tmp['name'];
191        return $res;
192    }    /* }}} */
193
194    /**
195     * Constructor of SeedDMS_Core_DatabaseAccess
196     *
197     * Sets all database parameters but does not connect.
198     *
199     * @param string $driver the database type e.g. mysql, sqlite
200     * @param string $hostname host of database server
201     * @param string $user name of user having access to database
202     * @param string $passw password of user
203     * @param bool|string $database name of database
204     */
205    function __construct($driver, $hostname, $user, $passw, $database = false) { /* {{{ */
206        $this->_driver = $driver;
207        $tmp = explode(":", $hostname);
208        $this->_hostname = $tmp[0];
209        $this->_port = null;
210        if(!empty($tmp[1]))
211            $this->_port = $tmp[1];
212        $this->_database = $database;
213        $this->_user = $user;
214        $this->_passw = $passw;
215        $this->_connected = false;
216        $this->_intransaction = 0;
217        $this->_logfile = '';
218        if($this->_logfile) {
219            $this->_logfp = fopen($this->_logfile, 'a+');
220            if($this->_logfp)
221                fwrite($this->_logfp, microtime(true)."    BEGIN ".$_SERVER['REQUEST_URI']." ------------------------------------------\n");
222        } else
223            $this->_logfp = null;
224        // $tt*****id is a hack to ensure that we do not try to create the
225        // temporary table twice during a single connection. Can be fixed by
226        // using Views (MySQL 5.0 onward) instead of temporary tables.
227        // CREATE ... IF NOT EXISTS cannot be used because it has the
228        // unpleasant side-effect of performing the insert again even if the
229        // table already exists.
230        //
231        // See createTemporaryTable() method for implementation.
232        $this->_ttreviewid = false;
233        $this->_ttapproveid = false;
234        $this->_ttstatid = false;
235        $this->_ttcontentid = false;
236        $this->_useviews = false; // turn off views, because they are much slower then temp. tables. They also break the transaction management, because dropping a view will commit the current transaction.
237        $this->_debug = false;
238    } /* }}} */
239
240    /**
241     * Return driver
242     *
243     * @return string name of driver as set in constructor
244     */
245    public function getDriver() { /* {{{ */
246        return $this->_driver;
247    } /* }}} */
248
249    /**
250     * Turn on views instead of temp. tables
251     *
252     * @param bool $onoff turn use of views instead of temp. table on/off
253     */
254    function useViews($onoff) { /* {{{ */
255        $this->_useviews = $onoff;
256    } /* }}} */
257
258    /**
259     * Destructor of SeedDMS_Core_DatabaseAccess
260     */
261    function __destruct() { /* {{{ */
262        if($this->_logfile && $this->_logfp) {
263            fwrite($this->_logfp, microtime(true)."    END --------------------------------------------\n");
264            fclose($this->_logfp);
265        }
266    } /* }}} */
267
268    /**
269     * Set the file pointer to a log file
270     *
271     * Once it is set, all queries will be logged into this file
272     */
273    function setLogFp($fp) { /* {{{ */
274        $this->_logfp = $fp;
275    } /* }}} */
276
277    /**
278     * Connect to database
279     *
280     * @return boolean true if connection could be established, otherwise false
281     */
282    function connect() { /* {{{ */
283        switch($this->_driver) {
284            case 'mysql':
285            case 'mysqli':
286            case 'mysqlnd':
287            case 'pgsql':
288                $dsn = $this->_driver.":dbname=".$this->_database.";host=".$this->_hostname;
289                if($this->_port)
290                    $dsn .= ";port=".$this->_port;
291                break;
292            case 'sqlite':
293                $dsn = $this->_driver.":".$this->_database;
294                break;
295        }
296        try {
297            /** @noinspection PhpUndefinedVariableInspection */
298            $this->_conn = new PDO($dsn, $this->_user, $this->_passw);
299            if (!$this->_conn)
300                return false;
301            /* Prevent PDO from throwing an exception because the code currently
302             * cannot handle it. PDO::ERRMODE_EXCEPTION became the default as of php 8.0.0
303             * PDO::ERRMODE_SILENT was the default before php 8.0.0
304             */
305            $this->_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
306
307            switch($this->_driver) {
308                case 'mysql':
309                    $this->_conn->exec('SET NAMES utf8');
310//                    $this->_conn->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE);
311                    /* Turn this on if you want strict checking of default values, etc. */
312                    /* $this->_conn->exec("SET SESSION sql_mode = 'STRICT_TRANS_TABLES'"); */
313                    /* The following is the default on Ubuntu 16.04 */
314                    /* $this->_conn->exec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'"); */
315                    break;
316                case 'sqlite':
317                    $this->_conn->exec('PRAGMA foreign_keys = ON');
318                    break;
319            }
320        } catch (Exception $e) {
321            return false;
322        }
323        if($this->_useviews) {
324            $tmp = $this->ViewList();
325            foreach(array('ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid') as $viewname) {
326                if(in_array($viewname, $tmp)) {
327                    $this->{"_".$viewname} = true;
328                }
329            }
330        }
331
332        $this->_connected = true;
333        return true;
334    } /* }}} */
335
336    /**
337     * Make sure a database connection exisits
338     *
339     * This function checks for a database connection. If it does not exists
340     * it will reconnect.
341     *
342     * @return boolean true if connection is established, otherwise false
343     */
344    function ensureConnected() { /* {{{ */
345        if (!$this->_connected) return $this->connect();
346        else return true;
347    } /* }}} */
348
349    /**
350     * Sanitize String used in database operations
351     *
352     * @param string $text
353     * @return string sanitized string
354     */
355    function qstr($text) { /* {{{ */
356        return $this->_conn->quote($text);
357    } /* }}} */
358
359    /**
360     * Replace back ticks by '"'
361     *
362     * @param string $text
363     * @return string sanitized string
364     */
365    function rbt($text) { /* {{{ */
366        return str_replace('`', '"', $text);
367    } /* }}} */
368
369    /**
370     * Return sql to concat strings or fields
371     *
372     * @param array $arr list of field names or strings
373     * @return string concated string
374     */
375    function concat($arr) { /* {{{ */
376        switch($this->_driver) {
377        case 'mysql':
378            return 'concat('.implode(',', $arr).')';
379            break;
380        case 'pgsql':
381            return implode(' || ', $arr);
382            break;
383        case 'sqlite':
384            return implode(' || ', $arr);
385            break;
386        }
387        return '';
388    } /* }}} */
389
390    /**
391     * Execute SQL query and return result
392     *
393     * Call this function only with sql query which return data records.
394     *
395     * @param string $queryStr sql query
396     * @param bool $retick
397     * @return array|bool data if query could be executed otherwise false
398     */
399    function getResultArray($queryStr, $retick=true) { /* {{{ */
400        $resArr = array();
401        
402        if($retick && $this->_driver == 'pgsql') {
403            $queryStr = $this->rbt($queryStr);
404        }
405
406        if($this->_logfp) {
407            fwrite($this->_logfp, microtime(true)."    ".($this->_conn->inTransaction() ? '*' : ' ')." ".$queryStr."\n");
408        }
409        $res = $this->_conn->query($queryStr);
410        if ($res === false) {
411            if($this->_debug) {
412                echo "error: ".$queryStr."<br />";
413                print_r($this->_conn->errorInfo());
414            }
415            return false;
416        }
417        $resArr = $res->fetchAll(PDO::FETCH_ASSOC);
418//        $res->Close();
419        return $resArr;
420    } /* }}} */
421
422    /**
423     * Execute SQL query
424     *
425     * Call this function only with sql query which do not return data records.
426     *
427     * @param string $queryStr sql query
428     * @param boolean $retick replace all '`' by '"'
429     * @return boolean true if query could be executed otherwise false
430     */
431    function getResult($queryStr, $retick=true) { /* {{{ */
432        if($retick && $this->_driver == 'pgsql') {
433            $queryStr = $this->rbt($queryStr);
434        }
435
436        if($this->_logfp) {
437            fwrite($this->_logfp, microtime(true)."    ".($this->_conn->inTransaction() ? '*' : ' ')." ".$queryStr."\n");
438        }
439        $res = $this->_conn->exec($queryStr);
440        if($res === false) {
441            if($this->_debug) {
442                echo "error: ".$queryStr."<br />";
443                print_r($this->_conn->errorInfo());
444            }
445            return false;
446        } else
447            return true;
448
449        return $res;
450    } /* }}} */
451
452    function startTransaction() { /* {{{ */
453        if(!$this->_intransaction) {
454            $this->_conn->beginTransaction();
455        }
456        $this->_intransaction++;
457        if($this->_logfp) {
458            fwrite($this->_logfp, microtime(true)."    ".($this->_conn->inTransaction() ? '*' : ' ')." START ".$this->_intransaction."\n");
459        }
460    } /* }}} */
461
462    function rollbackTransaction() { /* {{{ */
463        if($this->_logfp) {
464            fwrite($this->_logfp, microtime(true)."    ".($this->_conn->inTransaction() ? '*' : ' ')." ROLLBACK ".$this->_intransaction."\n");
465        }
466        if($this->_intransaction == 1) {
467            $this->_conn->rollBack();
468        }
469        $this->_intransaction--;
470    } /* }}} */
471
472    function commitTransaction() { /* {{{ */
473        if($this->_logfp) {
474            fwrite($this->_logfp, microtime(true)."    ".($this->_conn->inTransaction() ? '*' : ' ')." COMMIT ".$this->_intransaction."\n");
475        }
476        if($this->_intransaction == 1) {
477            $this->_conn->commit();
478        }
479        $this->_intransaction--;
480    } /* }}} */
481
482    function inTransaction() { /* {{{ */
483        return $this->_conn->inTransaction();
484    } /* }}} */
485
486    /**
487     * Return the id of the last instert record
488     *
489     * @param string $tablename
490     * @param string $fieldname
491     * @return int id used in last autoincrement
492     */
493    function getInsertID($tablename='', $fieldname='id') { /* {{{ */
494        if($this->_driver == 'pgsql')
495            return $this->_conn->lastInsertId('"'.$tablename.'_'.$fieldname.'_seq"');
496        else
497            return $this->_conn->lastInsertId();
498    } /* }}} */
499
500    function getErrorMsg() { /* {{{ */
501        $info = $this->_conn->errorInfo();
502        return($info[2]);
503    } /* }}} */
504
505    function getErrorNo() { /* {{{ */
506        return $this->_conn->errorCode();
507    } /* }}} */
508
509    /**
510     * Create various temporary tables to speed up and simplify sql queries
511     *
512     * @param string $tableName
513     * @param bool $override
514     * @return bool
515     */
516    private function __createTemporaryTable($tableName, $override=false) { /* {{{ */
517        if (!strcasecmp($tableName, "ttreviewid")) {
518            switch($this->_driver) {
519                case 'sqlite':
520                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttreviewid` AS ".
521                        "SELECT `tblDocumentReviewLog`.`reviewID` AS `reviewID`, ".
522                        "MAX(`tblDocumentReviewLog`.`reviewLogID`) AS `maxLogID` ".
523                        "FROM `tblDocumentReviewLog` ".
524                        "GROUP BY `tblDocumentReviewLog`.`reviewID` "; //.
525//                        "ORDER BY `maxLogID`";
526                    $dropStr = "DROP TABLE IF EXISTS `ttreviewid`";
527                break;
528                case 'pgsql':
529                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttreviewid` (`reviewID` INTEGER, `maxLogID` INTEGER, PRIMARY KEY (`reviewID`));".
530                        "INSERT INTO `ttreviewid` SELECT `tblDocumentReviewLog`.`reviewID`, ".
531                        "MAX(`tblDocumentReviewLog`.`reviewLogID`) AS `maxLogID` ".
532                        "FROM `tblDocumentReviewLog` ".
533                        "GROUP BY `tblDocumentReviewLog`.`reviewID` ";//.
534//                        "ORDER BY `maxLogID`";
535                    $dropStr = "DROP TABLE IF EXISTS `ttreviewid`";
536                break;
537                default:
538                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttreviewid` (PRIMARY KEY (`reviewID`), INDEX (`maxLogID`)) ".
539                        "SELECT `tblDocumentReviewLog`.`reviewID`, ".
540                        "MAX(`tblDocumentReviewLog`.`reviewLogID`) AS `maxLogID` ".
541                        "FROM `tblDocumentReviewLog` ".
542                        "GROUP BY `tblDocumentReviewLog`.`reviewID` "; //.
543//                        "ORDER BY `maxLogID`";
544                    $dropStr = "DROP TEMPORARY TABLE IF EXISTS `ttreviewid`";
545            }
546            if (!$this->_ttreviewid) {
547                if (!$this->getResult($queryStr))
548                    return false;
549                $this->_ttreviewid=true;
550            }
551            else {
552                if (is_bool($override) && $override) {
553                    if (!$this->getResult($dropStr))
554                        return false;
555                    if (!$this->getResult($queryStr))
556                        return false;
557                }
558            }
559            return $this->_ttreviewid;
560        }
561        elseif (!strcasecmp($tableName, "ttapproveid")) {
562            switch($this->_driver) {
563                case 'sqlite':
564                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttapproveid` AS ".
565                        "SELECT `tblDocumentApproveLog`.`approveID` AS `approveID`, ".
566                        "MAX(`tblDocumentApproveLog`.`approveLogID`) AS `maxLogID` ".
567                        "FROM `tblDocumentApproveLog` ".
568                        "GROUP BY `tblDocumentApproveLog`.`approveID` "; //.
569//                        "ORDER BY `maxLogID`";
570                    $dropStr = "DROP TABLE IF EXISTS `ttapproveid`";
571                    break;
572                case 'pgsql':
573                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttapproveid` (`approveID` INTEGER, `maxLogID` INTEGER, PRIMARY KEY (`approveID`));".
574                        "INSERT INTO `ttapproveid` SELECT `tblDocumentApproveLog`.`approveID`, ".
575                        "MAX(`tblDocumentApproveLog`.`approveLogID`) AS `maxLogID` ".
576                        "FROM `tblDocumentApproveLog` ".
577                        "GROUP BY `tblDocumentApproveLog`.`approveID` "; //.
578//                        "ORDER BY `maxLogID`";
579                    $dropStr = "DROP TABLE IF EXISTS `ttapproveid`";
580                    break;
581                default:
582                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttapproveid` (PRIMARY KEY (`approveID`), INDEX (`maxLogID`)) ".
583                        "SELECT `tblDocumentApproveLog`.`approveID`, ".
584                        "MAX(`tblDocumentApproveLog`.`approveLogID`) AS `maxLogID` ".
585                        "FROM `tblDocumentApproveLog` ".
586                        "GROUP BY `tblDocumentApproveLog`.`approveID` "; //.
587//                        "ORDER BY `maxLogID`";
588                    $dropStr = "DROP TEMPORARY TABLE IF EXISTS `ttapproveid`";
589            }
590            if (!$this->_ttapproveid) {
591                if (!$this->getResult($queryStr))
592                    return false;
593                $this->_ttapproveid=true;
594            }
595            else {
596                if (is_bool($override) && $override) {
597                    if (!$this->getResult($dropStr))
598                        return false;
599                    if (!$this->getResult($queryStr))
600                        return false;
601                }
602            }
603            return $this->_ttapproveid;
604        }
605        elseif (!strcasecmp($tableName, "ttstatid")) {
606            switch($this->_driver) {
607                case 'sqlite':
608                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttstatid` AS ".
609                        "SELECT `tblDocumentStatusLog`.`statusID` AS `statusID`, ".
610                        "MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID` ".
611                        "FROM `tblDocumentStatusLog` ".
612                        "GROUP BY `tblDocumentStatusLog`.`statusID` "; //.
613//                        "ORDER BY `maxLogID`";
614                    $dropStr = "DROP TABLE IF EXISTS `ttstatid`";
615                    break;
616                case 'pgsql':
617                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttstatid` (`statusID` INTEGER, `maxLogID` INTEGER, PRIMARY KEY (`statusID`));".
618                        "INSERT INTO `ttstatid` SELECT `tblDocumentStatusLog`.`statusID`, ".
619                        "MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID` ".
620                        "FROM `tblDocumentStatusLog` ".
621                        "GROUP BY `tblDocumentStatusLog`.`statusID` "; //.
622//                        "ORDER BY `maxLogID`";
623                    $dropStr = "DROP TABLE IF EXISTS `ttstatid`";
624                    break;
625                default:
626                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttstatid` (PRIMARY KEY (`statusID`), INDEX (`maxLogID`)) ".
627                        "SELECT `tblDocumentStatusLog`.`statusID`, ".
628                        "MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID` ".
629                        "FROM `tblDocumentStatusLog` ".
630                        "GROUP BY `tblDocumentStatusLog`.`statusID` "; //.
631//                        "ORDER BY `maxLogID`";
632                    $dropStr = "DROP TEMPORARY TABLE IF EXISTS `ttstatid`";
633            }
634            if (!$this->_ttstatid) {
635                if (!$this->getResult($queryStr))
636                    return false;
637                $this->_ttstatid=true;
638            }
639            else {
640                if (is_bool($override) && $override) {
641                    if (!$this->getResult($dropStr))
642                        return false;
643                    if (!$this->getResult($queryStr))
644                        return false;
645                }
646            }
647            return $this->_ttstatid;
648        }
649        elseif (!strcasecmp($tableName, "ttcontentid")) {
650            switch($this->_driver) {
651                case 'sqlite':
652                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttcontentid` AS ".
653                        "SELECT `tblDocumentContent`.`document` AS `document`, ".
654                        "MAX(`tblDocumentContent`.`version`) AS `maxVersion` ".
655                        "FROM `tblDocumentContent` ".
656                        "GROUP BY `tblDocumentContent`.`document` ".
657                        "ORDER BY `tblDocumentContent`.`document`";
658                    $dropStr = "DROP TABLE IF EXISTS `ttcontentid`";
659                    break;
660                case 'pgsql':
661                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttcontentid` (`document` INTEGER, `maxVersion` INTEGER, PRIMARY KEY (`document`)); ".
662                        "INSERT INTO `ttcontentid` SELECT `tblDocumentContent`.`document` AS `document`, ".
663                        "MAX(`tblDocumentContent`.`version`) AS `maxVersion` ".
664                        "FROM `tblDocumentContent` ".
665                        "GROUP BY `tblDocumentContent`.`document` ".
666                        "ORDER BY `tblDocumentContent`.`document`";
667                    $dropStr = "DROP TABLE IF EXISTS `ttcontentid`";
668                    break;
669                default:
670                    $queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttcontentid` (PRIMARY KEY (`document`), INDEX (`maxVersion`)) ".
671                        "SELECT `tblDocumentContent`.`document`, ".
672                        "MAX(`tblDocumentContent`.`version`) AS `maxVersion` ".
673                        "FROM `tblDocumentContent` ".
674                        "GROUP BY `tblDocumentContent`.`document` ".
675                        "ORDER BY `tblDocumentContent`.`document`";
676                    $dropStr = "DROP TEMPORARY TABLE IF EXISTS `ttcontentid`";
677            }
678            if (!$this->_ttcontentid) {
679                if (!$this->getResult($queryStr))
680                    return false;
681                $this->_ttcontentid=true;
682            }
683            else {
684                if (is_bool($override) && $override) {
685                    if (!$this->getResult($dropStr))
686                        return false;
687                    if (!$this->getResult($queryStr))
688                        return false;
689                }
690            }
691            return $this->_ttcontentid;
692        }
693        return false;
694    } /* }}} */
695
696    /**
697     * Drop various temporary tables to enforce recreation when needed
698     *
699     * @param string $tableName
700     *
701     * @return bool
702     */
703    private function __dropTemporaryTable($tableName) { /* {{{ */
704        $queryStr = '';
705        if($this->_driver == 'sqlite' || $this->_driver == 'pgsql')
706            $t = '';
707        else
708            $t = 'TEMPORARY';
709        if (!strcasecmp($tableName, "ttreviewid")) {
710            $queryStr = "DROP ".$t." TABLE IF EXISTS `ttreviewid`";
711        } elseif (!strcasecmp($tableName, "ttapproveid")) {
712            $queryStr = "DROP ".$t." TABLE IF EXISTS `ttapproveid`";
713        } elseif (!strcasecmp($tableName, "ttstatid")) {
714            $queryStr = "DROP ".$t." TABLE IF EXISTS `ttstatid`";
715        } elseif (!strcasecmp($tableName, "ttcontentid")) {
716            $queryStr = "DROP ".$t." TABLE IF EXISTS `ttcontentid`";
717        }
718        if($queryStr) {
719            if (!$this->getResult($queryStr))
720                return false;
721            else {
722                $this->{'_'.$tableName} = false;
723                return true;
724            }
725        }
726        return false;
727    } /* }}} */
728
729    /**
730     * Create various views to speed up and simplify sql queries
731     *
732     * @param string $tableName
733     * @param bool $override
734     *
735     * @return bool
736     */
737    private function __createView($tableName, $override=false) { /* {{{ */
738        if (!strcasecmp($tableName, "ttreviewid")) {
739            switch($this->_driver) {
740                case 'sqlite':
741                    $queryStr = "CREATE VIEW IF NOT EXISTS `ttreviewid` AS ".
742                        "SELECT `tblDocumentReviewLog`.`reviewID` AS `reviewID`, ".
743                        "MAX(`tblDocumentReviewLog`.`reviewLogID`) AS `maxLogID` ".
744                        "FROM `tblDocumentReviewLog` ".
745                        "GROUP BY `tblDocumentReviewLog`.`reviewID` "; //.
746                break;
747                case 'pgsql':
748                    $queryStr = "CREATE VIEW `ttreviewid` AS ".
749                        "SELECT `tblDocumentReviewLog`.`reviewID` AS `reviewID`, ".
750                        "MAX(`tblDocumentReviewLog`.`reviewLogID`) AS `maxLogID` ".
751                        "FROM `tblDocumentReviewLog` ".
752                        "GROUP BY `tblDocumentReviewLog`.`reviewID` ";
753                break;
754                default:
755                    $queryStr = "CREATE".($override ? " OR REPLACE" : "")." VIEW `ttreviewid` AS ".
756                        "SELECT `tblDocumentReviewLog`.`reviewID` AS `reviewID`, ".
757                        "MAX(`tblDocumentReviewLog`.`reviewLogID`) AS `maxLogID` ".
758                        "FROM `tblDocumentReviewLog` ".
759                        "GROUP BY `tblDocumentReviewLog`.`reviewID` ";
760            }
761            if (!$this->_ttreviewid) {
762                if (!$this->getResult($queryStr))
763                    return false;
764                $this->_ttreviewid=true;
765            }
766            else {
767                if (is_bool($override) && $override) {
768//                    if (!$this->getResult("DROP VIEW `ttreviewid`"))
769//                        return false;
770                    if (!$this->getResult($queryStr))
771                        return false;
772                }
773            }
774            return $this->_ttreviewid;
775        }
776        elseif (!strcasecmp($tableName, "ttapproveid")) {
777            switch($this->_driver) {
778                case 'sqlite':
779                    $queryStr = "CREATE VIEW IF NOT EXISTS `ttapproveid` AS ".
780                        "SELECT `tblDocumentApproveLog`.`approveID` AS `approveID`, ".
781                        "MAX(`tblDocumentApproveLog`.`approveLogID`) AS `maxLogID` ".
782                        "FROM `tblDocumentApproveLog` ".
783                        "GROUP BY `tblDocumentApproveLog`.`approveID` "; //.
784                    break;
785                case 'pgsql':
786                    $queryStr = "CREATE VIEW `ttapproveid` AS ".
787                        "SELECT `tblDocumentApproveLog`.`approveID` AS `approveID`, ".
788                        "MAX(`tblDocumentApproveLog`.`approveLogID`) AS `maxLogID` ".
789                        "FROM `tblDocumentApproveLog` ".
790                        "GROUP BY `tblDocumentApproveLog`.`approveID` ";
791                    break;
792                default:
793                    $queryStr = "CREATE".($override ? " OR REPLACE" : "")." VIEW `ttapproveid` AS ".
794                        "SELECT `tblDocumentApproveLog`.`approveID`, ".
795                        "MAX(`tblDocumentApproveLog`.`approveLogID`) AS `maxLogID` ".
796                        "FROM `tblDocumentApproveLog` ".
797                        "GROUP BY `tblDocumentApproveLog`.`approveID` ";
798            }
799            if (!$this->_ttapproveid) {
800                if (!$this->getResult($queryStr))
801                    return false;
802                $this->_ttapproveid=true;
803            }
804            else {
805                if (is_bool($override) && $override) {
806//                    if (!$this->getResult("DROP VIEW `ttapproveid`"))
807//                        return false;
808                    if (!$this->getResult($queryStr))
809                        return false;
810                }
811            }
812            return $this->_ttapproveid;
813        }
814        elseif (!strcasecmp($tableName, "ttstatid")) {
815            switch($this->_driver) {
816                case 'sqlite':
817                    $queryStr = "CREATE VIEW IF NOT EXISTS `ttstatid` AS ".
818                        "SELECT `tblDocumentStatusLog`.`statusID` AS `statusID`, ".
819                        "MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID` ".
820                        "FROM `tblDocumentStatusLog` ".
821                        "GROUP BY `tblDocumentStatusLog`.`statusID` ";
822                    break;
823                case 'pgsql':
824                    $queryStr = "CREATE VIEW `ttstatid` AS ".
825                        "SELECT `tblDocumentStatusLog`.`statusID` AS `statusID`, ".
826                        "MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID` ".
827                        "FROM `tblDocumentStatusLog` ".
828                        "GROUP BY `tblDocumentStatusLog`.`statusID` ";
829                    break;
830                default:
831                    $queryStr = "CREATE".($override ? " OR REPLACE" : "")." VIEW `ttstatid` AS ".
832                        "SELECT `tblDocumentStatusLog`.`statusID`, ".
833                        "MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID` ".
834                        "FROM `tblDocumentStatusLog` ".
835                        "GROUP BY `tblDocumentStatusLog`.`statusID` ";
836            }
837            if (!$this->_ttstatid) {
838                if (!$this->getResult($queryStr))
839                    return false;
840                $this->_ttstatid=true;
841            }
842            else {
843                if (is_bool($override) && $override) {
844//                    if (!$this->getResult("DROP VIEW `ttstatid`"))
845//                        return false;
846                    if (!$this->getResult($queryStr))
847                        return false;
848                }
849            }
850            return $this->_ttstatid;
851        }
852        elseif (!strcasecmp($tableName, "ttcontentid")) {
853            switch($this->_driver) {
854                case 'sqlite':
855                    $queryStr = "CREATE VIEW IF NOT EXISTS `ttcontentid` AS ".
856                        "SELECT `tblDocumentContent`.`document` AS `document`, ".
857                        "MAX(`tblDocumentContent`.`version`) AS `maxVersion` ".
858                        "FROM `tblDocumentContent` ".
859                        "GROUP BY `tblDocumentContent`.`document` ".
860                        "ORDER BY `tblDocumentContent`.`document`";
861                    break;
862                case 'pgsql':
863                    $queryStr = "CREATE VIEW `ttcontentid` AS ".
864                        "SELECT `tblDocumentContent`.`document` AS `document`, ".
865                        "MAX(`tblDocumentContent`.`version`) AS `maxVersion` ".
866                        "FROM `tblDocumentContent` ".
867                        "GROUP BY `tblDocumentContent`.`document` ".
868                        "ORDER BY `tblDocumentContent`.`document`";
869                    break;
870                default:
871                    $queryStr = "CREATE".($override ? " OR REPLACE" : "")." VIEW `ttcontentid` AS ".
872                        "SELECT `tblDocumentContent`.`document`, ".
873                        "MAX(`tblDocumentContent`.`version`) AS `maxVersion` ".
874                        "FROM `tblDocumentContent` ".
875                        "GROUP BY `tblDocumentContent`.`document` ".
876                        "ORDER BY `tblDocumentContent`.`document`";
877            }
878            if (!$this->_ttcontentid) {
879                if (!$this->getResult($queryStr))
880                    return false;
881                $this->_ttcontentid=true;
882            }
883            else {
884                if (is_bool($override) && $override) {
885//                    if (!$this->getResult("DROP VIEW `ttcontentid`"))
886//                        return false;
887                    if (!$this->getResult($queryStr))
888                        return false;
889                }
890            }
891            return $this->_ttcontentid;
892        }
893        return false;
894    } /* }}} */
895
896    /**
897     * Create various temporary tables or view to speed up and simplify sql queries
898     *
899     * @param string $tableName
900     * @param bool $override
901     *
902     * @return bool
903     */
904    public function createTemporaryTable($tableName, $override=false) { /* {{{ */
905        if($this->_useviews)
906            return $this->__createView($tableName, $override);
907        else
908            return $this->__createTemporaryTable($tableName, $override);
909    } /* }}} */
910
911    /**
912     * Drop various temporary tables to force recreation when next time needed
913     *
914     * @param string $tableName
915     *
916     * @return bool
917     */
918    public function dropTemporaryTable($tableName) { /* {{{ */
919        if($this->_useviews)
920            return true; // No need to recreate a view
921        else
922            return $this->__dropTemporaryTable($tableName);
923    } /* }}} */
924
925    /**
926     * Return sql statement for extracting the date part from a field
927     * containing a unix timestamp
928     *
929     * @param string $fieldname name of field containing the timestamp
930     * @param string $format
931     * @return string sql code
932     */
933    function getDateExtract($fieldname, $format='%Y-%m-%d') { /* {{{ */
934        switch($this->_driver) {
935            case 'mysql':
936                return "from_unixtime(`".$fieldname."`, ".$this->qstr($format).")";
937                break;
938            case 'sqlite':
939                return "strftime(".$this->qstr($format).", `".$fieldname."`, 'unixepoch')";
940                break;
941            case 'pgsql':
942                switch($format) {
943                case '%Y-%m':
944                    return "to_char(to_timestamp(`".$fieldname."`), 'YYYY-MM')";
945                    break;
946                default:
947                    return "to_char(to_timestamp(`".$fieldname."`), 'YYYY-MM-DD')";
948                    break;
949                }
950                break;
951        }
952        return '';
953    } /* }}} */
954
955    /**
956     * Return sql statement for returning the current date and time
957     * in format Y-m-d H:i:s
958     *
959     * @return string sql code
960     */
961    function getCurrentDatetime() { /* {{{ */
962        switch($this->_driver) {
963            case 'mysql':
964                return "CURRENT_TIMESTAMP";
965                break;
966            case 'sqlite':
967                return "datetime('now', 'localtime')";
968                break;
969            case 'pgsql':
970                return "now()";
971                break;
972        }
973        return '';
974    } /* }}} */
975
976    /**
977     * Return sql statement for returning the current timestamp
978     *
979     * @return string sql code
980     */
981    function getCurrentTimestamp() { /* {{{ */
982        switch($this->_driver) {
983            case 'mysql':
984                return "UNIX_TIMESTAMP()";
985                break;
986            case 'sqlite':
987                return "strftime('%s', 'now')";
988                break;
989            case 'pgsql':
990                return "date_part('epoch',CURRENT_TIMESTAMP)::int";
991                break;
992        }
993        return '';
994    } /* }}} */
995
996    /**
997     * Return sql statement for returning the current timestamp
998     *
999     * @param $field
1000     * @return string sql code
1001     */
1002    function castToText($field) { /* {{{ */
1003        switch($this->_driver) {
1004            case 'pgsql':
1005                return $field."::TEXT";
1006                break;
1007        }
1008        return $field;
1009    } /* }}} */
1010
1011    /**
1012     * Create an sql dump of the complete database
1013     *
1014     * @param resource $fp name of dump file
1015     * @return bool
1016     */
1017    function createDump($fp) { /* {{{ */
1018        $tables = $this->TableList('TABLES');
1019        foreach($tables as $table) {
1020            if($table == 'sqlite_sequence')
1021                continue;
1022            $query = "SELECT * FROM `".$table."`";
1023            $records = $this->getResultArray($query);
1024            fwrite($fp,"\n-- TABLE: ".$table."--\n\n");
1025            foreach($records as $record) {
1026                $values="";
1027                $i = 1;
1028                foreach ($record as $column) {
1029                    if (is_numeric($column)) $values .= $column;
1030                    else $values .= $this->qstr($column);
1031
1032                    if ($i<(count($record))) $values .= ",";
1033                    $i++;
1034                }
1035
1036                fwrite($fp, "INSERT INTO `".$table."` VALUES (".$values.");\n");
1037            }
1038        }
1039        return true;
1040    } /* }}} */
1041}