root/dev/common/includes/class.db_cache.php @ 323

Revision 323, 8.7 KB (checked in by ralle030583, 14 years ago)

- DB Update004 wasn't active
- pageslitter was missing in kills / losses page
- parser fix for german killmails (Allianz : Nichts)
- Update from history.xml for history mod...

Line 
1<?php
2
3class DBCachedQuery
4{
5    function DBCachedQuery()
6    {
7        $this->executed_ = false;
8        $this->_cache = array();
9        $this->_cached = false;
10
11        // this is the minimum runtime a query has to run to be
12        // eligible for caching in seconds
13        $this->_minruntime = 0.1;
14
15        // maximum size of a cached result set (512kB)
16        $this->_maxcachesize = 524288;
17        $this->d = true;
18    }
19
20    function checkCache()
21    {
22        // only cache selects
23        // we don't use select ... into so there is no problem
24        $this->_sql = str_replace(array("\r\n", "\n"), ' ', $this->_sql);
25        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
26        {
27            // this is no select, update the table
28            $this->markAffectedTables();
29            return false;
30        }
31
32        if (file_exists(KB_CACHEDIR.'/qcache_qry_'.$this->_hash))
33        {
34            $this->_mtime = filemtime(KB_CACHEDIR.'/qcache_qry_'.$this->_hash);
35            if ($this->isCacheValid())
36            {
37                return true;
38            }
39        }
40
41        return false;
42    }
43
44    function parseSQL()
45    {
46        // gets all involved tables for a select statement
47        $text = strtolower($this->_sql).' ';
48
49        // we try to get the text from 'from' to 'where' because all involved
50        // tables are declared in that part
51        $from = strpos($text, 'from')+5;
52        if (!$to = strpos($text, 'where'))
53        {
54            $to = strlen($text);
55        }
56        $parse = trim(substr($text, $from, $to-$from));
57
58        $tables = array();
59        if (strpos($parse, ',') !== false)
60        {
61            // , is a synonym for join so we'll replace them
62            $parse = str_replace(',', ' join ', $parse);
63        }
64
65        $parse = 'join '.$parse;
66        if (strpos($parse, 'join'))
67        {
68            // if this query is a join we parse it with regexp to get all tables
69            preg_match_all('/join (.*?) /', $parse, $match);
70            $tables = $match[1];
71        }
72        else
73        {
74            // no join so it is hopefully a simple table select
75            $tables[] = $parse;
76        }
77
78        $this->_usedtables = $tables;
79    }
80
81    function isCacheValid()
82    {
83        // check if cachefiles are stil valid
84
85        // first, we need to get all involved tables
86        $this->parseSQL();
87
88        foreach ($this->_usedtables as $table)
89        {
90            $file = KB_CACHEDIR.'/qcache_tbl_'.trim($table);
91            if (file_exists($file))
92            {
93                // if one of the tables is outdated, the query is outdated
94                if ($this->_mtime < filemtime($file))
95                {
96                    return false;
97                }
98            }
99        }
100        return true;
101    }
102
103    function markAffectedTables()
104    {
105        // this function invalidates cache files for touched tables
106        $text = trim(strtolower($this->_sql));
107        $text = str_replace(array('ignore','`', "\r\n", "\n"), '', $text);
108        $ta = preg_split('/ /', $text, 0, PREG_SPLIT_NO_EMPTY);
109
110        // check for sql keywords and get the table from the appropriate position
111        $tables = array();
112        if ($ta[0] == 'update')
113        {
114            $tables[] = $ta[1];
115        }
116        elseif ($ta[0] == 'insert')
117        {
118            $tables[] = $ta[2];
119        }
120        elseif ($ta[0] == 'replace')
121        {
122            $tables[] = $ta[2];
123        }
124        elseif ($ta[0] == 'delete')
125        {
126            $tables[] = $ta[2];
127        }elseif ($ta[0] == 'drop')
128                {
129                        $tables[] = $ta[2];
130                }
131        elseif ($ta[0] == 'alter')
132        {
133            return false;
134        }
135        elseif ($ta[0] == 'create')
136        {
137            return false;
138        }
139        else
140        {
141            var_dump($ta);
142            trigger_error('No suitable handler for query found.',E_USER_WARNING);
143            return false;
144        }
145
146        foreach ($tables as $table)
147        {
148            $file = KB_CACHEDIR.'/qcache_tbl_'.$table;
149            touch($file);
150        }
151        // refresh php's filestatcache so we dont get wrong timestamps on changed files
152        clearstatcache();
153    }
154
155    function genCache()
156    {
157        // this function fetches all rows and writes the data into a textfile
158
159        // don't attemp to cache updates!
160        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
161        {
162            return false;
163        }
164
165        $bsize = 0;
166        while ($row = $this->getRow())
167        {
168            $this->_cache[] = $row;
169
170            // if the bytesize of the table exceeds the limit we'll abort
171            // the cache generation and leave this query unbuffered
172            $bsize += join('', $row);
173            if ($bsize > $this->_maxcachesize)
174            {
175                $this->_cache[] = array();
176                $this->_cached = false;
177                $this->rewind();
178                return false;
179            }
180        }
181
182        // write data into textfile
183        file_put_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash, serialize($this->_cache));
184
185        $this->_cached = true;
186        $this->_currrow = 0;
187        $this->executed_ = true;
188    }
189
190    function loadCache()
191    {
192        // loads the cachefile into the memory
193        $this->_cache = unserialize(file_get_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash));
194
195        $this->_cached = true;
196        $this->_currrow = 0;
197        $this->executed_ = true;
198    }
199
200    function execute($sql)
201    {
202        $this->_sql = trim($sql);
203        $this->_hash = md5($this->_sql);
204        $this->_cache = array();
205        $this->_cached = false;
206
207        if ($this->checkCache())
208        {
209            $this->loadCache();
210            $this->queryCachedCount(true);
211            return true;
212        }
213
214        // we got no or no valid cache so open the connection and run the query
215        $this->dbconn_ = new DBConnection;
216
217        $t1 = strtok(microtime(), ' ') + strtok('');
218
219        $this->resid_ = mysql_query($sql, $this->dbconn_->id());
220
221        if ($this->resid_ == false)
222        {
223            if (DB_HALTONERROR === true)
224            {
225                echo "Database error: ".mysql_error($this->dbconn_->id())."<br/>";
226                echo "SQL: ".$this->_sql."<br/>";
227                exit;
228            }
229            else
230            {
231                return false;
232            }
233        }
234
235        $this->exectime_ = strtok(microtime(), ' ') + strtok('') - $t1;
236        $this->executed_ = true;
237
238        if (KB_PROFILE == 2)
239        {
240            file_put_contents('/tmp/profile.lst', $sql."\nExecution time: ".$this->exectime_."\n", FILE_APPEND);
241        }
242
243        // if the query was too slow we'll fetch all rows and run it cached
244        if ($this->exectime_ > $this->_minruntime)
245        {
246            $this->genCache();
247        }
248
249        $this->queryCount(true);
250        return true;
251    }
252
253    function queryCount($increase = false)
254    {
255        static $count;
256
257        if ($increase)
258        {
259            $count++;
260        }
261
262        return $count;
263    }
264
265    function queryCachedCount($increase = false)
266    {
267        static $count;
268
269        if ($increase)
270        {
271            $count++;
272        }
273
274        return $count;
275    }
276
277    function recordCount()
278    {
279        if ($this->_cached)
280        {
281            return count($this->_cache);
282        }
283        return mysql_num_rows($this->resid_);
284    }
285
286    function getRow()
287    {
288        if ($this->_cached)
289        {
290            if (!isset($this->_cache[$this->_currrow]))
291            {
292                return false;
293            }
294            // return the current row and increase the pointer by one
295            return $this->_cache[$this->_currrow++];
296        }
297        if (is_resource($this->resid_))
298        {
299            return mysql_fetch_assoc($this->resid_);
300        }
301        return false;
302    }
303
304    function rewind()
305    {
306        if ($this->_cached)
307        {
308            $this->_currrow = 0;
309        }
310        @mysql_data_seek($this->resid_, 0);
311    }
312
313    function getInsertID()
314    {
315        return mysql_insert_id();
316    }
317
318    function execTime()
319    {
320        return $this->exectime_;
321    }
322
323    function executed()
324    {
325        return $this->executed_;
326    }
327
328    function getErrorMsg()
329    {
330        $msg = $this->sql_."<br>";
331        $msg .= "Query failed. ".mysql_error($this->dbconn_->id());
332
333        return $msg;
334    }
335}
336?>
Note: See TracBrowser for help on using the browser.