]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/backend/ADODB_mysql.php
Reformat code
[SourceForge/phpwiki.git] / lib / WikiDB / backend / ADODB_mysql.php
1 <?php
2
3 require_once 'lib/WikiDB/backend/ADODB.php';
4
5 /*
6  * PROBLEM: mysql seems to be the simpliest (or most stupid) db on earth.
7  * (tested with 4.0.18)
8  * See http://sql-info.de/mysql/gotchas.html for mysql specific quirks.
9  *
10  * Whenever a table is write-locked, you cannot even write to other unrelated
11  * tables. So it seems that we have to lock all tables!
12  * As workaround we try it with application locks, uniquely named locks,
13  * to prevent from concurrent writes of locks with the same name.
14  * The lock name is a strcat of the involved tables.
15  *
16  * See also http://use.perl.org/~Smylers/journal/34246 for strict mode and warnings.
17  */
18 define('DO_APP_LOCK', true);
19 define('DO_FULL_LOCK', false);
20
21 /**
22  * WikiDB layer for ADODB-mysql, called by lib/WikiDB/ADODB.php.
23  * Now with support for the newer ADODB library, the ADODB extension library
24  * and more database drivers.
25  * To use transactions use the mysqlt driver: "mysqlt:..."
26  *
27  * @author: Lawrence Akka, Reini Urban
28  */
29 class WikiDB_backend_ADODB_mysql
30     extends WikiDB_backend_ADODB
31 {
32     /**
33      * Constructor.
34      */
35     function WikiDB_backend_ADODB_mysql($dbparams)
36     {
37         $this->WikiDB_backend_ADODB($dbparams);
38         if (!$this->_dbh->_connectionID) return;
39
40         $this->_serverinfo = $this->_dbh->ServerInfo();
41         if (!empty($this->_serverinfo['version'])) {
42             $arr = explode('.', $this->_serverinfo['version']);
43             $this->_serverinfo['version'] = (string)(($arr[0] * 100) + $arr[1]) . "." . (integer)$arr[2];
44         }
45         if ($this->_serverinfo['version'] < 323.0) {
46             // Older MySQL's don't have CASE WHEN ... END
47             $this->_expressions['maxmajor'] = "MAX(IF(minor_edit=0,version,0))";
48             $this->_expressions['maxminor'] = "MAX(IF(minor_edit<>0,version,0))";
49         }
50
51         // esp. needed for utf databases
52         if ($this->_serverinfo['version'] > 401.0) {
53             global $charset;
54             $aliases = array('iso-8859-1' => 'latin1',
55                 'utf-8' => 'utf8');
56             //http://dev.mysql.com/doc/mysql/en/charset-connection.html
57             if (isset($aliases[strtolower($charset)])) {
58                 // mysql needs special unusual names and doesn't resolve aliases
59                 mysql_query("SET NAMES '" . $aliases[$charset] . "'");
60             } else {
61                 mysql_query("SET NAMES '$charset'");
62             }
63         }
64     }
65
66     /**
67      * Kill timed out processes. ( so far only called on about every 50-th save. )
68      */
69     function _timeout()
70     {
71         if (empty($this->_dbparams['timeout'])) return;
72         $result = mysql_query("SHOW processlist");
73         while ($row = mysql_fetch_array($result)) {
74             if ($row["db"] == $this->_dsn['database']
75                 and $row["User"] == $this->_dsn['username']
76                     and $row["Time"] > $this->_dbparams['timeout']
77                         and $row["Command"] == "Sleep"
78             ) {
79                 $process_id = $row["Id"];
80                 mysql_query("KILL $process_id");
81             }
82         }
83     }
84
85     /**
86      * Pack tables.
87      */
88     function optimize()
89     {
90         $dbh = &$this->_dbh;
91         $this->_timeout();
92         foreach ($this->_table_names as $table) {
93             $dbh->Execute("OPTIMIZE TABLE $table");
94         }
95         return 1;
96     }
97
98     /**
99      * Lock tables. As fine-grained application lock, which locks only the
100      * same transaction (conflicting updates and edits), and as full table
101      * write lock.
102      *
103      * New: which tables as params,
104      *      support nested locks via app locks
105      */
106     function _lock_tables($tables, $write_lock = true)
107     {
108         if (!$tables) return;
109         if (DO_APP_LOCK) {
110             $lock = join('-', $tables);
111             $result = $this->_dbh->GetRow("SELECT GET_LOCK('$lock',10)");
112             if (!$result or $result[0] == 0) {
113                 trigger_error("WARNING: Couldn't obtain application lock " . $lock . "\n<br />",
114                     E_USER_WARNING);
115                 return;
116             }
117         }
118         if (DO_FULL_LOCK) {
119             // if this is not enough:
120             $lock_type = $write_lock ? "WRITE" : "READ";
121             foreach ($this->_table_names as $key => $table) {
122                 $locks[] = "$table $lock_type";
123             }
124             $this->_dbh->Execute("LOCK TABLES " . join(",", $locks));
125         }
126     }
127
128     /**
129      * Release the locks.
130      * Support nested locks
131      */
132     function _unlock_tables($tables)
133     {
134         if (!$tables) {
135             $this->_dbh->Execute("UNLOCK TABLES");
136             return;
137         }
138         if (DO_APP_LOCK) {
139             $lock = join('-', $tables);
140             $result = $this->_dbh->Execute("SELECT RELEASE_LOCK('$lock')");
141         }
142         if (DO_FULL_LOCK) {
143             // if this is not enough:
144             $this->_dbh->Execute("UNLOCK TABLES");
145         }
146     }
147
148     function increaseHitCount($pagename)
149     {
150         $dbh = &$this->_dbh;
151         // Hits is the only thing we can update in a fast manner.
152         // Note that this will fail silently if the page does not
153         // have a record in the page table.  Since it's just the
154         // hit count, who cares?
155         // LIMIT since 3.23
156         $dbh->Execute(sprintf("UPDATE LOW_PRIORITY %s SET hits=hits+1 WHERE pagename=%s %s",
157                 $this->_table_names['page_tbl'],
158                 $dbh->qstr($pagename),
159                 ($this->_serverinfo['version'] >= 323.0) ? "LIMIT 1" : "")
160         );
161         return;
162     }
163
164     function _get_pageid($pagename, $create_if_missing = false)
165     {
166
167         // check id_cache
168         global $request;
169         $cache =& $request->_dbi->_cache->_id_cache;
170         if (isset($cache[$pagename])) {
171             if ($cache[$pagename] or !$create_if_missing) {
172                 return $cache[$pagename];
173             }
174         }
175
176         // attributes play this game.
177         if ($pagename === '') return 0;
178
179         $dbh = &$this->_dbh;
180         $page_tbl = $this->_table_names['page_tbl'];
181         $query = sprintf("SELECT id FROM $page_tbl WHERE pagename=%s",
182             $dbh->qstr($pagename));
183         if (!$create_if_missing) {
184             $row = $dbh->GetRow($query);
185             return $row ? $row[0] : false;
186         }
187         $row = $dbh->GetRow($query);
188         if (!$row) {
189             // have auto-incrementing, atomic version
190             $rs = $dbh->Execute(sprintf("INSERT INTO $page_tbl"
191                     . " (id,pagename)"
192                     . " VALUES(NULL,%s)",
193                 $dbh->qstr($pagename)));
194             $id = $dbh->_insertid();
195         } else {
196             $id = $row[0];
197         }
198         assert($id);
199         return $id;
200     }
201
202     /**
203      * Create a new revision of a page.
204      */
205     function set_versiondata($pagename, $version, $data)
206     {
207         $dbh = &$this->_dbh;
208         $version_tbl = $this->_table_names['version_tbl'];
209
210         $minor_edit = (int)!empty($data['is_minor_edit']);
211         unset($data['is_minor_edit']);
212
213         $mtime = (int)$data['mtime'];
214         unset($data['mtime']);
215         assert(!empty($mtime));
216
217         @$content = (string)$data['%content'];
218         unset($data['%content']);
219         unset($data['%pagedata']);
220
221         $this->lock(array('page', 'recent', 'version', 'nonempty'));
222         $dbh->BeginTrans();
223         $dbh->CommitLock($version_tbl);
224         $id = $this->_get_pageid($pagename, true);
225         $backend_type = $this->backendType();
226         // optimize: mysql can do this with one REPLACE INTO.
227         $rs = $dbh->Execute(sprintf("REPLACE INTO $version_tbl"
228                 . " (id,version,mtime,minor_edit,content,versiondata)"
229                 . " VALUES(%d,%d,%d,%d,%s,%s)",
230             $id, $version, $mtime, $minor_edit,
231             $dbh->qstr($content),
232             $dbh->qstr($this->_serialize($data))));
233         $this->_update_recent_table($id);
234         $this->_update_nonempty_table($id);
235         if ($rs) $dbh->CommitTrans();
236         else $dbh->RollbackTrans();
237         $this->unlock(array('page', 'recent', 'version', 'nonempty'));
238     }
239
240 }
241
242 ;
243
244 // Local Variables:
245 // mode: php
246 // tab-width: 8
247 // c-basic-offset: 4
248 // c-hanging-comment-ender-p: nil
249 // indent-tabs-mode: nil
250 // End: