PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/dbstruct/SpotStruct_mysql.php

https://github.com/elstenaar86/spotweb
PHP | 355 lines | 246 code | 49 blank | 60 comment | 37 complexity | f782b61ee32ead48bfedfa2905f78994 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, MPL-2.0-no-copyleft-exception, 0BSD, Apache-2.0
  1. <?php
  2. class SpotStruct_mysql extends SpotStruct_abs {
  3. /*
  4. * Optimize / analyze (database specific) a number of hightraffic
  5. * tables.
  6. * This function does not modify any schema or data
  7. */
  8. function analyze() {
  9. $this->_dbcon->rawExec("ANALYZE TABLE spots");
  10. $this->_dbcon->rawExec("ANALYZE TABLE spotsfull");
  11. $this->_dbcon->rawExec("ANALYZE TABLE commentsxover");
  12. $this->_dbcon->rawExec("ANALYZE TABLE commentsfull");
  13. $this->_dbcon->rawExec("ANALYZE TABLE spotstatelist");
  14. $this->_dbcon->rawExec("ANALYZE TABLE sessions");
  15. $this->_dbcon->rawExec("ANALYZE TABLE filters");
  16. $this->_dbcon->rawExec("ANALYZE TABLE spotteridblacklist");
  17. $this->_dbcon->rawExec("ANALYZE TABLE filtercounts");
  18. $this->_dbcon->rawExec("ANALYZE TABLE users");
  19. $this->_dbcon->rawExec("ANALYZE TABLE cache");
  20. } # analyze
  21. /*
  22. * Converts a 'spotweb' internal datatype to a
  23. * database specific datatype
  24. */
  25. function swDtToNative($colType) {
  26. switch(strtoupper($colType)) {
  27. case 'INTEGER' : $colType = 'int(11)'; break;
  28. case 'UNSIGNED INTEGER' : $colType = 'int(10) unsigned'; break;
  29. case 'BIGINTEGER' : $colType = 'bigint(20)'; break;
  30. case 'UNSIGNED BIGINTEGER' : $colType = 'bigint(20) unsigned'; break;
  31. case 'BOOLEAN' : $colType = 'tinyint(1)'; break;
  32. case 'MEDIUMBLOB' : $colType = 'mediumblob'; break;
  33. } # switch
  34. return $colType;
  35. } # swDtToNative
  36. /*
  37. * Converts a database native datatype to a spotweb native
  38. * datatype
  39. */
  40. function nativeDtToSw($colInfo) {
  41. switch(strtolower($colInfo)) {
  42. case 'int(11)' : $colInfo = 'INTEGER'; break;
  43. case 'int(10) unsigned' : $colInfo = 'UNSIGNED INTEGER'; break;
  44. case 'bigint(20)' : $colInfo = 'BIGINTEGER'; break;
  45. case 'bigint(20) unsigned' : $colInfo = 'UNSIGNED BIGINTEGER'; break;
  46. case 'tinyint(1)' : $colInfo = 'BOOLEAN'; break;
  47. case 'mediumblob' : $colInfo = 'MEDIUMBLOB'; break;
  48. } # switch
  49. return $colInfo;
  50. } # nativeDtToSw
  51. /* checks if an index exists */
  52. function indexExists($idxname, $tablename) {
  53. $q = $this->_dbcon->arrayQuery("SHOW INDEXES FROM " . $tablename . " WHERE key_name = '%s'", Array($idxname));
  54. return !empty($q);
  55. } # indexExists
  56. /* checks if a column exists */
  57. function columnExists($tablename, $colname) {
  58. $q = $this->_dbcon->arrayQuery("SHOW COLUMNS FROM " . $tablename . " WHERE Field = '%s'", Array($colname));
  59. return !empty($q);
  60. } # columnExists
  61. /*
  62. * Adds an index, but first checks if the index doesn't
  63. * exist already.
  64. *
  65. * $idxType can be either 'UNIQUE', '' or 'FULLTEXT'
  66. */
  67. function addIndex($idxname, $idxType, $tablename, $colList) {
  68. if (!$this->indexExists($idxname, $tablename)) {
  69. if ($idxType == "UNIQUE") {
  70. $this->_dbcon->rawExec("ALTER IGNORE TABLE " . $tablename . " ADD " . $idxType . " INDEX " . $idxname . "(" . implode(",", $colList) . ");");
  71. } else {
  72. $this->_dbcon->rawExec("ALTER TABLE " . $tablename . " ADD " . $idxType . " INDEX " . $idxname . "(" . implode(",", $colList) . ");");
  73. } # else
  74. } # if
  75. } # addIndex
  76. /* checks if a fts text index exists */
  77. function ftsExists($ftsname, $tablename, $colList) {
  78. foreach($colList as $num => $col) {
  79. $indexInfo = $this->getIndexInfo($ftsname . '_' . $num, $tablename);
  80. if ((empty($indexInfo)) || (strtolower($indexInfo[0]['column_name']) != strtolower($col))) {
  81. return false;
  82. } # if
  83. } # foreach
  84. return true;
  85. } # ftsExists
  86. /* creates a full text index */
  87. function createFts($ftsname, $tablename, $colList) {
  88. foreach($colList as $num => $col) {
  89. $indexInfo = $this->getIndexInfo($ftsname . '_' . $num, $tablename);
  90. if ((empty($indexInfo)) || (strtolower($indexInfo[0]['column_name']) != strtolower($col))) {
  91. $this->dropIndex($ftsname . '_' . $num, $tablename);
  92. $this->addIndex($ftsname . '_' . $num, 'FULLTEXT', $tablename, array($col));
  93. } # if
  94. } # foreach
  95. } # createFts
  96. /* drops a fulltext index */
  97. function dropFts($ftsname, $tablename, $colList) {
  98. foreach($colList as $num => $col) {
  99. $this->dropIndex($ftsname . '_' . $num, $tablename);
  100. } # foreach
  101. } # dropFts
  102. /* returns FTS info */
  103. function getFtsInfo($ftsname, $tablename, $colList) {
  104. $ftsList = array();
  105. foreach($colList as $num => $col) {
  106. $tmpIndex = $this->getIndexInfo($ftsname . '_' . $num, $tablename);
  107. if (!empty($tmpIndex)) {
  108. $ftsList[] = $tmpIndex[0];
  109. } # if
  110. } # foreach
  111. return $ftsList;
  112. } # getFtsInfo
  113. /* drops an index if it exists */
  114. function dropIndex($idxname, $tablename) {
  115. /*
  116. * Make sure the table exists, else this will return an error
  117. * and return a fatal
  118. */
  119. if (!$this->tableExists($tablename)) {
  120. return ;
  121. } # if
  122. if ($this->indexExists($idxname, $tablename)) {
  123. $this->_dbcon->rawExec("DROP INDEX " . $idxname . " ON " . $tablename);
  124. } # if
  125. } # dropIndex
  126. /* adds a column if the column doesn't exist yet */
  127. function addColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation) {
  128. if (!$this->columnExists($tablename, $colName)) {
  129. # set the DEFAULT value
  130. if (strlen($colDefault) != 0) {
  131. $colDefault = 'DEFAULT ' . $colDefault;
  132. } # if
  133. # Convert the column type to a type we use in MySQL
  134. $colType = $this->swDtToNative($colType);
  135. # change the collation to a MySQL type
  136. switch(strtolower($collation)) {
  137. case 'utf8' : $colSetting = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci'; break;
  138. case 'ascii' : $colSetting = 'CHARACTER SET ascii'; break;
  139. case 'ascii_bin' : $colSetting = 'CHARACTER SET ascii COLLATE ascii_bin'; break;
  140. case '' : $colSetting = ''; break;
  141. default : throw new Exception("Invalid collation setting");
  142. } # switch
  143. # and define the 'NOT NULL' part
  144. switch($notNull) {
  145. case true : $nullStr = 'NOT NULL'; break;
  146. default : $nullStr = '';
  147. } # switch
  148. $this->_dbcon->rawExec("ALTER TABLE " . $tablename .
  149. " ADD COLUMN(" . $colName . " " . $colType . " " . $colSetting . " " . $colDefault . " " . $nullStr . ")");
  150. } # if
  151. } # addColumn
  152. /* alters a column - does not check if the column doesn't adhere to the given definition */
  153. function modifyColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation, $what) {
  154. # set the DEFAULT value
  155. if (strlen($colDefault) != 0) {
  156. $colDefault = 'DEFAULT ' . $colDefault;
  157. } # if
  158. # Convert the column type to a type we use in MySQL
  159. $colType = $this->swDtToNative($colType);
  160. # change the collation to a MySQL type
  161. switch(strtolower($collation)) {
  162. case 'utf8' : $colSetting = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci'; break;
  163. case 'ascii' : $colSetting = 'CHARACTER SET ascii'; break;
  164. case 'ascii_bin' : $colSetting = 'CHARACTER SET ascii COLLATE ascii_bin'; break;
  165. case '' : $colSetting = ''; break;
  166. default : throw new Exception("Invalid collation setting");
  167. } # switch
  168. # and define the 'NOT NULL' part
  169. switch($notNull) {
  170. case true : $nullStr = 'NOT NULL'; break;
  171. default : $nullStr = '';
  172. } # switch
  173. $this->_dbcon->rawExec("ALTER TABLE " . $tablename .
  174. " MODIFY COLUMN " . $colName . " " . $colType . " " . $colSetting . " " . $colDefault . " " . $nullStr);
  175. } # modifyColumn
  176. /* drops a column */
  177. function dropColumn($colName, $tablename) {
  178. if ($this->columnExists($tablename, $colName)) {
  179. $this->_dbcon->rawExec("ALTER TABLE " . $tablename . " DROP COLUMN " . $colName);
  180. } # if
  181. } # dropColumn
  182. /* checks if a table exists */
  183. function tableExists($tablename) {
  184. $q = $this->_dbcon->arrayQuery("SHOW TABLES LIKE '" . $tablename . "'");
  185. return !empty($q);
  186. } # tableExists
  187. /* creates an empty table with only an ID field. Collation should be either UTF8 or ASCII */
  188. function createTable($tablename, $collation) {
  189. if (!$this->tableExists($tablename)) {
  190. switch(strtolower($collation)) {
  191. case 'utf8' : $colSetting = 'CHARSET=utf8 COLLATE=utf8_unicode_ci'; break;
  192. case 'ascii' : $colSetting = 'CHARSET=ascii'; break;
  193. default : throw new Exception("Invalid collation setting");
  194. } # switch
  195. $this->_dbcon->rawExec("CREATE TABLE " . $tablename . " (id INTEGER PRIMARY KEY AUTO_INCREMENT) " . $colSetting);
  196. } # if
  197. } # createTable
  198. /* drop a table */
  199. function dropTable($tablename) {
  200. if ($this->tableExists($tablename)) {
  201. $this->_dbcon->rawExec("DROP TABLE " . $tablename);
  202. } # if
  203. } # dropTable
  204. /* alters a storage engine (only mysql knows something about store engines, but well :P ) */
  205. function alterStorageEngine($tablename, $engine) {
  206. $q = $this->_dbcon->singleQuery("SELECT ENGINE
  207. FROM information_schema.TABLES
  208. WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '" . $tablename . "'");
  209. if (strtolower($q) != strtolower($engine)) {
  210. $this->_dbcon->rawExec("ALTER TABLE " . $tablename . " ENGINE=" . $engine);
  211. } # if
  212. } # alterStorageEngine
  213. /* rename a table */
  214. function renameTable($tablename, $newTableName) {
  215. $this->_dbcon->rawExec("RENAME TABLE " . $tablename . " TO " . $newTableName);
  216. } # renameTable
  217. /* drop a foreign key constraint */
  218. function dropForeignKey($tablename, $colname, $reftable, $refcolumn, $action) {
  219. $q = $this->_dbcon->arrayQuery("SELECT CONSTRAINT_NAME FROM information_schema.key_column_usage
  220. WHERE TABLE_SCHEMA = DATABASE()
  221. AND TABLE_NAME = '" . $tablename . "'
  222. AND COLUMN_NAME = '" . $colname . "'
  223. AND REFERENCED_TABLE_NAME = '" . $reftable . "'
  224. AND REFERENCED_COLUMN_NAME = '" . $refcolumn . "'");
  225. if (!empty($q)) {
  226. foreach($q as $res) {
  227. $this->_dbcon->rawExec("ALTER TABLE " . $tablename . " DROP FOREIGN KEY " . $res['CONSTRAINT_NAME']);
  228. } # foreach
  229. } # if
  230. } # dropForeignKey
  231. /* creates a foreign key constraint */
  232. function addForeignKey($tablename, $colname, $reftable, $refcolumn, $action) {
  233. $q = $this->_dbcon->arrayQuery("SELECT * FROM information_schema.key_column_usage
  234. WHERE TABLE_SCHEMA = DATABASE()
  235. AND TABLE_NAME = '" . $tablename . "'
  236. AND COLUMN_NAME = '" . $colname . "'
  237. AND REFERENCED_TABLE_NAME = '" . $reftable . "'
  238. AND REFERENCED_COLUMN_NAME = '" . $refcolumn . "'");
  239. if (empty($q)) {
  240. $this->_dbcon->rawExec("ALTER TABLE " . $tablename . " ADD FOREIGN KEY (" . $colname . ")
  241. REFERENCES " . $reftable . " (" . $refcolumn . ") " . $action);
  242. } # if
  243. } # addForeignKey
  244. /* Returns in a fixed format, column information */
  245. function getColumnInfo($tablename, $colname) {
  246. $q = $this->_dbcon->arrayQuery("SELECT COLUMN_NAME,
  247. COLUMN_DEFAULT,
  248. IS_NULLABLE,
  249. COLUMN_TYPE,
  250. CHARACTER_SET_NAME,
  251. COLLATION_NAME
  252. FROM information_schema.COLUMNS
  253. WHERE TABLE_NAME = '" . $tablename . "'
  254. AND COLUMN_NAME = '" . $colname . "'
  255. AND TABLE_SCHEMA = DATABASE()");
  256. if (!empty($q)) {
  257. $q = $q[0];
  258. $q['NOTNULL'] = ($q['IS_NULLABLE'] != 'YES');
  259. /*
  260. * MySQL's boolean type secretly is a tinyint, but in Spotweb we
  261. * use an actual boolean type. We secretly convert all tinyint(1)'s
  262. * to boolean types.
  263. */
  264. if (strtolower($q['COLUMN_TYPE']) == 'tinyint(1)') {
  265. if (is_numeric($q['COLUMN_DEFAULT'])) {
  266. if ($q['COLUMN_DEFAULT']) {
  267. $q['COLUMN_DEFAULT'] = '1';
  268. } else {
  269. $q['COLUMN_DEFAULT'] = '0';
  270. } # if
  271. } # if
  272. } # if
  273. /*
  274. * We do not properly distinguish between character sets and
  275. * collations in the spotweb system, so we mangle them a bit
  276. */
  277. if (is_string($q['COLLATION_NAME'])) {
  278. switch($q['COLLATION_NAME']) {
  279. case 'ascii_general_ci' : $q['COLLATION_NAME'] = 'ascii'; break;
  280. case 'ascii_bin' : $q['COLLATION_NAME'] = 'ascii_bin'; break;
  281. case 'utf8_unicode_ci' : $q['COLLATION_NAME'] = 'utf8'; break;
  282. case 'utf8_general_ci' : $q['COLLATION_NAME'] = 'utf8'; break;
  283. default : throw new Exception("Invalid collation setting for varchar: " . $q['COLLATION_NAME']);
  284. } # switch
  285. } # if
  286. # a default value has to given, so make it compareable to what we define
  287. if ((strlen($q['COLUMN_DEFAULT']) == 0) && (is_string($q['COLUMN_DEFAULT']))) {
  288. $q['COLUMN_DEFAULT'] = "''";
  289. } # if
  290. } # if
  291. return $q;
  292. } # getColumnInfo
  293. /* Returns in a fixed format, index information */
  294. function getIndexInfo($idxname, $tablename) {
  295. $q = $this->_dbcon->arrayQuery("SELECT
  296. column_name,
  297. non_unique,
  298. lower(index_type) as index_type
  299. FROM information_schema.STATISTICS
  300. WHERE TABLE_SCHEMA = DATABASE()
  301. AND table_name = '" . $tablename . "'
  302. AND index_name = '" . $idxname . "'
  303. ORDER BY seq_in_index");
  304. return $q;
  305. } # getIndexInfo
  306. } # class