]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/backend/PearDB_mysql.php
Reformat code
[SourceForge/phpwiki.git] / lib / WikiDB / backend / PearDB_mysql.php
1 <?php
2
3 require_once 'lib/WikiDB/backend/PearDB.php';
4
5 // See http://sql-info.de/mysql/gotchas.html for mysql specific quirks.
6
7 // The slowest function overall is mysql_connect with [680ms]
8 // 2nd is db_mysql::simpleQuery with [257ms]
9 class WikiDB_backend_PearDB_mysql
10     extends WikiDB_backend_PearDB
11 {
12     /**
13      * Constructor.
14      */
15     function WikiDB_backend_PearDB_mysql($dbparams)
16     {
17         $this->WikiDB_backend_PearDB($dbparams);
18         if (DB::isError($this->_dbh)) return;
19         //$this->_serverinfo = $this->_dbh->ServerInfo();
20         $row = $this->_dbh->GetOne("SELECT version()");
21         if (!DB::isError($row) and !empty($row)) {
22             $arr = explode('.', $row);
23             $this->_serverinfo['version'] = (string)(($arr[0] * 100) + $arr[1]) .
24                 "." . (integer)$arr[2];
25             if ($this->_serverinfo['version'] < 323.0) {
26                 // Older MySQL's don't have CASE WHEN ... END
27                 $this->_expressions['maxmajor'] = "MAX(IF(minor_edit=0,version,0))";
28                 $this->_expressions['maxminor'] = "MAX(IF(minor_edit<>0,version,0))";
29             }
30             // esp. needed for utf databases
31             if ($this->_serverinfo['version'] > 401.0) {
32                 global $charset;
33                 $aliases = array('iso-8859-1' => 'latin1',
34                     'utf-8' => 'utf8');
35                 //http://dev.mysql.com/doc/mysql/en/charset-connection.html
36                 if (isset($aliases[strtolower($charset)])) {
37                     // mysql needs special unusual names and doesn't resolve aliases
38                     mysql_query("SET NAMES '" . $aliases[strtolower($charset)] . "'");
39                 } else {
40                     mysql_query("SET NAMES '$charset'");
41                 }
42             }
43         }
44     }
45
46     /**
47      * Kill timed out processes. ( so far only called on about every 50-th save. )
48      */
49     function _timeout()
50     {
51         if (empty($this->_dbparams['timeout'])) return;
52         $result = mysql_query("SHOW processlist");
53         while ($row = mysql_fetch_array($result)) {
54             if ($row["db"] == $this->_dbh->dsn['database']
55                 and $row["User"] == $this->_dbh->dsn['username']
56                     and $row["Time"] > $this->_dbparams['timeout']
57                         and $row["Command"] == "Sleep"
58             ) {
59                 $process_id = $row["Id"];
60                 mysql_query("KILL $process_id");
61             }
62         }
63     }
64
65     /**
66      * Create a new revision of a page.
67      */
68     function set_versiondata($pagename, $version, $data)
69     {
70         $dbh = &$this->_dbh;
71         $version_tbl = $this->_table_names['version_tbl'];
72
73         $minor_edit = (int)!empty($data['is_minor_edit']);
74         unset($data['is_minor_edit']);
75
76         $mtime = (int)$data['mtime'];
77         unset($data['mtime']);
78         assert(!empty($mtime));
79
80         @$content = (string)$data['%content'];
81         unset($data['%content']);
82         unset($data['%pagedata']);
83
84         $this->lock();
85         $id = $this->_get_pageid($pagename, true);
86         // requires PRIMARY KEY (id,version)!
87         // VALUES supported since mysql-3.22.5
88         $dbh->query(sprintf("REPLACE INTO $version_tbl"
89                 . " (id,version,mtime,minor_edit,content,versiondata)"
90                 . " VALUES(%d,%d,%d,%d,'%s','%s')",
91             $id, $version, $mtime, $minor_edit,
92             $dbh->escapeSimple($content),
93             $dbh->escapeSimple($this->_serialize($data))
94         ));
95         // real binding (prepare,execute) only since mysqli + PHP5
96         $this->_update_recent_table($id);
97         $this->_update_nonempty_table($id);
98         $this->unlock();
99     }
100
101     function _update_recent_table($pageid = false)
102     {
103         $dbh = &$this->_dbh;
104         extract($this->_table_names);
105         extract($this->_expressions);
106
107         $pageid = (int)$pageid;
108
109         // optimized: mysql can do this with one REPLACE INTO.
110         // supported in every (?) mysql version
111         // requires PRIMARY KEY (id)!
112         $dbh->query("REPLACE INTO $recent_tbl"
113             . " (id, latestversion, latestmajor, latestminor)"
114             . " SELECT id, $maxversion, $maxmajor, $maxminor"
115             . " FROM $version_tbl"
116             . ($pageid ? " WHERE id=$pageid" : "")
117             . " GROUP BY id");
118     }
119
120     /* ISNULL is mysql specific */
121     function wanted_pages($exclude_from = '', $exclude = '', $sortby = '', $limit = '')
122     {
123         $dbh = &$this->_dbh;
124         extract($this->_table_names);
125         if ($orderby = $this->sortby($sortby, 'db', array('pagename', 'wantedfrom')))
126             $orderby = 'ORDER BY ' . $orderby;
127
128         if ($exclude_from) // array of pagenames
129             $exclude_from = " AND pp.pagename NOT IN " . $this->_sql_set($exclude_from);
130         if ($exclude) // array of pagenames
131             $exclude = " AND p.pagename NOT IN " . $this->_sql_set($exclude);
132
133         $sql = "SELECT p.pagename, pp.pagename as wantedfrom"
134             . " FROM $page_tbl p, $link_tbl linked"
135             . " LEFT JOIN $page_tbl pp ON (linked.linkto = pp.id)"
136             . " LEFT JOIN $nonempty_tbl ne ON (linked.linkto = ne.id)"
137             . " WHERE ISNULL(ne.id)"
138             . " AND p.id = linked.linkfrom"
139             . $exclude_from
140             . $exclude
141             . $orderby;
142         if ($limit) {
143             list($from, $count) = $this->limit($limit);
144             $result = $dbh->limitQuery($sql, $from, $count * 3);
145         } else {
146             $result = $dbh->query($sql);
147         }
148         return new WikiDB_backend_PearDB_generic_iter($this, $result);
149     }
150
151     /* // REPLACE will not delete empy pages, so it was removed --ru
152     function _update_nonempty_table($pageid = false) {
153         $dbh = &$this->_dbh;
154         extract($this->_table_names);
155
156         $pageid = (int)$pageid;
157
158         // Optimized: mysql can do this with one REPLACE INTO.
159         // supported in every (?) mysql version
160         // requires PRIMARY KEY (id)
161         $dbh->query("REPLACE INTO $nonempty_tbl (id)"
162                     . " SELECT $recent_tbl.id"
163                     . " FROM $recent_tbl, $version_tbl"
164                     . " WHERE $recent_tbl.id=$version_tbl.id"
165                     . "       AND version=latestversion"
166                     . "  AND content<>''"
167                     . ( $pageid ? " AND $recent_tbl.id=$pageid" : ""));
168     }
169     */
170
171     /**
172      * Pack tables.
173      */
174     function optimize()
175     {
176         $dbh = &$this->_dbh;
177         $this->_timeout();
178         foreach ($this->_table_names as $table) {
179             $dbh->query("OPTIMIZE TABLE $table");
180         }
181         return 1;
182     }
183
184     /**
185      * Lock tables.
186      */
187     function _lock_tables($write_lock = true)
188     {
189         $lock_type = $write_lock ? "WRITE" : "READ";
190         foreach ($this->_table_names as $table) {
191             $tables[] = "$table $lock_type";
192         }
193         $this->_dbh->query("LOCK TABLES " . join(",", $tables));
194     }
195
196     /**
197      * Release all locks.
198      */
199     function _unlock_tables()
200     {
201         $this->_dbh->query("UNLOCK TABLES");
202     }
203
204     function increaseHitCount($pagename)
205     {
206         $dbh = &$this->_dbh;
207         // Hits is the only thing we can update in a fast manner.
208         // Note that this will fail silently if the page does not
209         // have a record in the page table.  Since it's just the
210         // hit count, who cares?
211         // LIMIT since 3.23
212         $dbh->query(sprintf("UPDATE LOW_PRIORITY %s SET hits=hits+1 WHERE pagename='%s' %s",
213             $this->_table_names['page_tbl'],
214             $dbh->escapeSimple($pagename),
215             ($this->_serverinfo['version'] >= 323.0) ? "LIMIT 1" : ""));
216         return;
217     }
218
219 }
220
221 ;
222
223 class WikiDB_backend_PearDB_mysql_search
224     extends WikiDB_backend_PearDB_search
225 {
226     function _pagename_match_clause($node)
227     {
228         $word = $node->sql();
229         if ($node->op == 'REGEX') { // posix regex extensions
230             return "pagename REGEXP '$word'";
231         } else {
232             return ($this->_case_exact
233                 ? "pagename LIKE '$word'"
234                 : "LOWER(pagename) LIKE '$word'");
235         }
236     }
237 }
238
239 // Local Variables:
240 // mode: php
241 // tab-width: 8
242 // c-basic-offset: 4
243 // c-hanging-comment-ender-p: nil
244 // indent-tabs-mode: nil
245 // End: