Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php |
— | — | @@ -779,7 +779,7 @@ |
780 | 780 | 's_id' => $bnode, |
781 | 781 | 'p_id' => $pid, |
782 | 782 | 'value_unit' => $dv->getUnit(), |
783 | | - 'value_xsd' => $dv[0], |
| 783 | + 'value_xsd' => $keys[0], |
784 | 784 | 'value_num' => $dv->getNumericValue() ); |
785 | 785 | break; |
786 | 786 | } |
— | — | @@ -1068,23 +1068,25 @@ |
1069 | 1069 | global $wgDBtype; |
1070 | 1070 | $this->reportProgress("Setting up standard database configuration for SMW ...\n\n",$verbose); |
1071 | 1071 | $this->reportProgress("Selected storage engine is \"SMWSQLStore2\" (or an extension thereof)\n\n",$verbose); |
1072 | | - if ($wgDBtype === 'postgres') { |
1073 | | - $this->reportProgress("Sorry, Postgres is currently not supported. Please contact the\ndevelopers if you wish to help fixing this.\n",$verbose); |
1074 | | - return; |
1075 | | - } |
1076 | 1072 | $db =& wfGetDB( DB_MASTER ); |
1077 | 1073 | extract( $db->tableNames('smw_ids','smw_rels2','smw_atts2','smw_text2', |
1078 | 1074 | 'smw_spec2','smw_subs2','smw_redi2','smw_inst2', |
1079 | 1075 | 'smw_conc2','smw_conccache') ); |
| 1076 | + $reportTo = $verbose?$this:NULL; // use $this to report back from static SMWSQLHelpers |
| 1077 | + // repeatedly used DB field types defined here for convenience |
| 1078 | + $dbt_id = SMWSQLHelpers::getStandardDBType('id'); |
| 1079 | + $dbt_namespace = SMWSQLHelpers::getStandardDBType('namespace'); |
| 1080 | + $dbt_title = SMWSQLHelpers::getStandardDBType('title'); |
| 1081 | + $dbt_iw = SMWSQLHelpers::getStandardDBType('iw'); |
| 1082 | + $dbt_blob = SMWSQLHelpers::getStandardDBType('blob'); |
1080 | 1083 | |
1081 | | - $this->setupTable($smw_ids, // internal IDs used in this store |
1082 | | - array('smw_id' => 'INT(8) UNSIGNED NOT NULL KEY AUTO_INCREMENT', |
1083 | | - 'smw_namespace' => 'INT(11) NOT NULL', |
1084 | | - 'smw_title' => 'VARCHAR(255) binary NOT NULL', |
1085 | | - 'smw_iw' => 'CHAR(32)', |
1086 | | - 'smw_sortkey' => 'VARCHAR(255) binary NOT NULL' |
1087 | | - ), $db, $verbose); |
1088 | | - $this->setupIndex($smw_ids, array('smw_id','smw_title,smw_namespace,smw_iw', 'smw_sortkey'), $db); |
| 1084 | + SMWSQLHelpers::setupTable($smw_ids, // internal IDs used in this store |
| 1085 | + array('smw_id' => $dbt_id . ' NOT NULL' . ($wgDBtype=='postgres'?' PRIMARY KEY':' KEY AUTO_INCREMENT'), |
| 1086 | + 'smw_namespace' => $dbt_namespace . ' NOT NULL', |
| 1087 | + 'smw_title' => $dbt_title . ' NOT NULL', |
| 1088 | + 'smw_iw' => $dbt_iw, |
| 1089 | + 'smw_sortkey' => $dbt_title . ' NOT NULL'), $db, $reportTo); |
| 1090 | + SMWSQLHelpers::setupIndex($smw_ids, array('smw_id','smw_title,smw_namespace,smw_iw', 'smw_sortkey'), $db); |
1089 | 1091 | // NOTE: smw_ids is normally used to store references to wiki pages (possibly with some external |
1090 | 1092 | // interwiki prefix). There are, however, some special objects that are also stored therein. These |
1091 | 1093 | // are marked by special interwiki prefixes (iw) that cannot occcur in real life: |
— | — | @@ -1102,67 +1104,71 @@ |
1103 | 1105 | // are reserved for hardcoded ids built into SMW) and normal entries. It is no object, but makes sure that |
1104 | 1106 | // SQL's auto increment counter is high enough to not add any objects before that marked "border". |
1105 | 1107 | |
1106 | | - $this->setupTable($smw_redi2, // fast redirect resolution |
1107 | | - array('s_title' => 'VARCHAR(255) binary NOT NULL', |
1108 | | - 's_namespace' => 'INT(11) NOT NULL', |
1109 | | - 'o_id' => 'INT(8) UNSIGNED NOT NULL',), $db, $verbose); |
1110 | | - $this->setupIndex($smw_redi2, array('s_title,s_namespace','o_id'), $db); |
| 1108 | + SMWSQLHelpers::setupTable($smw_redi2, // fast redirect resolution |
| 1109 | + array('s_title' => $dbt_title . ' NOT NULL', |
| 1110 | + 's_namespace' => $dbt_namespace . ' NOT NULL', |
| 1111 | + 'o_id' => $dbt_id . ' NOT NULL'), $db, $reportTo); |
| 1112 | + SMWSQLHelpers::setupIndex($smw_redi2, array('s_title,s_namespace','o_id'), $db); |
1111 | 1113 | |
1112 | | - $this->setupTable($smw_rels2, // properties with other pages as values ("relations") |
1113 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1114 | | - 'p_id' => 'INT(8) UNSIGNED NOT NULL', |
1115 | | - 'o_id' => 'INT(8) UNSIGNED NOT NULL'), $db, $verbose); |
1116 | | - $this->setupIndex($smw_rels2, array('s_id','p_id','o_id'), $db); |
| 1114 | + SMWSQLHelpers::setupTable($smw_rels2, // properties with other pages as values ("relations") |
| 1115 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1116 | + 'p_id' => $dbt_id . ' NOT NULL', |
| 1117 | + 'o_id' => $dbt_id . ' NOT NULL'), $db, $reportTo); |
| 1118 | + SMWSQLHelpers::setupIndex($smw_rels2, array('s_id','p_id','o_id'), $db); |
1117 | 1119 | |
1118 | | - $this->setupTable($smw_atts2, // most standard properties ("attributes") |
1119 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1120 | | - 'p_id' => 'INT(8) UNSIGNED NOT NULL', |
1121 | | - 'value_unit' => 'VARCHAR(63) binary', |
1122 | | - 'value_xsd' => 'VARCHAR(255) binary NOT NULL', |
1123 | | - 'value_num' => 'DOUBLE'), $db, $verbose); |
1124 | | - $this->setupIndex($smw_atts2, array('s_id','p_id','value_num','value_xsd'), $db); |
| 1120 | + SMWSQLHelpers::setupTable($smw_atts2, // most standard properties ("attributes") |
| 1121 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1122 | + 'p_id' => $dbt_id . ' NOT NULL', |
| 1123 | + 'value_unit' => ($wgDBtype=='postgres'?'TEXT':'VARCHAR(63) binary'), |
| 1124 | + 'value_xsd' => $dbt_title . ' NOT NULL', |
| 1125 | + 'value_num' => ($wgDBtype=='postgres'?'DOUBLE PRECISION':'DOUBLE')), $db, $reportTo); |
| 1126 | + SMWSQLHelpers::setupIndex($smw_atts2, array('s_id','p_id','value_num','value_xsd'), $db); |
1125 | 1127 | |
1126 | | - $this->setupTable($smw_text2, // properties with long strings as values |
1127 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1128 | | - 'p_id' => 'INT(8) UNSIGNED NOT NULL', |
1129 | | - 'value_blob' => 'MEDIUMBLOB'), $db, $verbose); |
1130 | | - $this->setupIndex($smw_text2, array('s_id','p_id'), $db); |
| 1128 | + SMWSQLHelpers::setupTable($smw_text2, // properties with long strings as values |
| 1129 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1130 | + 'p_id' => $dbt_id . ' NOT NULL', |
| 1131 | + 'value_blob' => $dbt_blob), $db, $reportTo); |
| 1132 | + SMWSQLHelpers::setupIndex($smw_text2, array('s_id','p_id'), $db); |
1131 | 1133 | |
1132 | 1134 | // field renaming between SMW 1.3 and SMW 1.4: |
1133 | 1135 | if ( ($db->tableExists($smw_spec2)) && ($db->fieldExists($smw_spec2, 'sp_id', 'SMWSQLStore2::setup')) ) { |
1134 | | - $db->query("ALTER TABLE $smw_spec2 CHANGE `sp_id` `p_id` INT(8) UNSIGNED NOT NULL", 'SMWSQLStore2::setup'); |
| 1136 | + if ($wgDBtype=='postgres') { |
| 1137 | + $db->query("ALTER TABLE $smw_spec2 ALTER COLUMN sp_id RENAME TO p_id", 'SMWSQLStore2::setup'); |
| 1138 | + } else { |
| 1139 | + $db->query("ALTER TABLE $smw_spec2 CHANGE `sp_id` `p_id` $dbt_id NOT NULL", 'SMWSQLStore2::setup'); |
| 1140 | + } |
1135 | 1141 | } |
1136 | | - $this->setupTable($smw_spec2, // very important special properties, for faster access |
1137 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1138 | | - 'p_id' => 'INT(8) UNSIGNED NOT NULL', |
1139 | | - 'value_string' => 'VARCHAR(255) binary NOT NULL'), $db, $verbose); |
1140 | | - $this->setupIndex($smw_spec2, array('s_id', 'p_id', 's_id,p_id'), $db); |
| 1142 | + SMWSQLHelpers::setupTable($smw_spec2, // very important special properties, for faster access |
| 1143 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1144 | + 'p_id' => $dbt_id . ' NOT NULL', |
| 1145 | + 'value_string' => $dbt_title . ' NOT NULL'), $db, $reportTo); |
| 1146 | + SMWSQLHelpers::setupIndex($smw_spec2, array('s_id', 'p_id', 's_id,p_id'), $db); |
1141 | 1147 | |
1142 | | - $this->setupTable($smw_subs2, // subproperty/subclass relationships |
1143 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1144 | | - 'o_id' => 'INT(8) UNSIGNED NOT NULL',), $db, $verbose); |
1145 | | - $this->setupIndex($smw_subs2, array('s_id', 'o_id'), $db); |
| 1148 | + SMWSQLHelpers::setupTable($smw_subs2, // subproperty/subclass relationships |
| 1149 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1150 | + 'o_id' => $dbt_id . ' NOT NULL'), $db, $reportTo); |
| 1151 | + SMWSQLHelpers::setupIndex($smw_subs2, array('s_id', 'o_id'), $db); |
1146 | 1152 | |
1147 | | - $this->setupTable($smw_inst2, // class instances (s_id the element, o_id the class) |
1148 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1149 | | - 'o_id' => 'INT(8) UNSIGNED NOT NULL',), $db, $verbose); |
1150 | | - $this->setupIndex($smw_inst2, array('s_id', 'o_id'), $db); |
| 1153 | + SMWSQLHelpers::setupTable($smw_inst2, // class instances (s_id the element, o_id the class) |
| 1154 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1155 | + 'o_id' => $dbt_id . ' NOT NULL',), $db, $reportTo); |
| 1156 | + SMWSQLHelpers::setupIndex($smw_inst2, array('s_id', 'o_id'), $db); |
1151 | 1157 | |
1152 | | - $this->setupTable($smw_conc2, // concept descriptions |
1153 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL KEY', |
1154 | | - 'concept_txt' => 'MEDIUMBLOB', |
1155 | | - 'concept_docu' => 'MEDIUMBLOB', |
1156 | | - 'concept_features' => 'INT(8)', |
1157 | | - 'concept_size' => 'INT(8)', |
1158 | | - 'concept_depth' => 'INT(8)', |
1159 | | - 'cache_date' => 'INT(8) UNSIGNED', |
1160 | | - 'cache_count' => 'INT(8) UNSIGNED' ), $db, $verbose); |
1161 | | - $this->setupIndex($smw_conc2, array('s_id'), $db); |
| 1158 | + SMWSQLHelpers::setupTable($smw_conc2, // concept descriptions |
| 1159 | + array('s_id' => $dbt_id . ' NOT NULL' . ($wgDBtype=='postgres'?' PRIMARY KEY':' KEY'), |
| 1160 | + 'concept_txt' => $dbt_blob, |
| 1161 | + 'concept_docu' => $dbt_blob, |
| 1162 | + 'concept_features' => ($wgDBtype=='postgres'?'INTEGER':'INT(8)'), |
| 1163 | + 'concept_size' => ($wgDBtype=='postgres'?'INTEGER':'INT(8)'), |
| 1164 | + 'concept_depth' => ($wgDBtype=='postgres'?'INTEGER':'INT(8)'), |
| 1165 | + 'cache_date' => ($wgDBtype=='postgres'?'INTEGER':'INT(8) UNSIGNED'), |
| 1166 | + 'cache_count' => ($wgDBtype=='postgres'?'INTEGER':'INT(8) UNSIGNED'), ), $db, $reportTo); |
| 1167 | + SMWSQLHelpers::setupIndex($smw_conc2, array('s_id'), $db); |
1162 | 1168 | |
1163 | | - $this->setupTable($smw_conccache, // concept cache: member elements (s)->concepts (o) |
1164 | | - array('s_id' => 'INT(8) UNSIGNED NOT NULL', |
1165 | | - 'o_id' => 'INT(8) UNSIGNED NOT NULL'), $db, $verbose); |
1166 | | - $this->setupIndex($smw_conccache, array('o_id'), $db); |
| 1169 | + SMWSQLHelpers::setupTable($smw_conccache, // concept cache: member elements (s)->concepts (o) |
| 1170 | + array('s_id' => $dbt_id . ' NOT NULL', |
| 1171 | + 'o_id' => $dbt_id . ' NOT NULL'), $db, $reportTo); |
| 1172 | + SMWSQLHelpers::setupIndex($smw_conccache, array('o_id'), $db); |
1167 | 1173 | |
1168 | 1174 | $this->reportProgress("Database initialised successfully.\n\n",$verbose); |
1169 | 1175 | $this->reportProgress("Setting up internal property indices ...\n",$verbose); |
— | — | @@ -1185,13 +1191,14 @@ |
1186 | 1192 | $this->reportProgress(" ... writing entries for internal properties.\n",$verbose); |
1187 | 1193 | foreach (SMWSQLStore2::$special_ids as $prop => $id) { |
1188 | 1194 | $p = SMWPropertyValue::makeProperty($prop); |
1189 | | - $db->replace('smw_ids', array(), array('smw_id' => $id, 'smw_title' => $p->getDBkey(), 'smw_namespace' => SMW_NS_PROPERTY, 'smw_iw' => $this->getPropertyInterwiki($p), 'smw_sortkey' => $p->getDBkey()), 'SMW::setup'); |
| 1195 | + $db->replace('smw_ids', array('smw_id'), array('smw_id' => $id, 'smw_title' => $p->getDBkey(), 'smw_namespace' => SMW_NS_PROPERTY, 'smw_iw' => $this->getPropertyInterwiki($p), 'smw_sortkey' => $p->getDBkey()), 'SMW::setup'); |
1190 | 1196 | } |
1191 | 1197 | $this->reportProgress("Internal properties initialised successfully.\n",$verbose); |
1192 | 1198 | return true; |
1193 | 1199 | } |
1194 | 1200 | |
1195 | 1201 | function drop($verbose = true) { |
| 1202 | + global $wgDBtype; |
1196 | 1203 | $this->reportProgress("Deleting all database content and tables generated by SMW ...\n\n",$verbose); |
1197 | 1204 | $db =& wfGetDB( DB_MASTER ); |
1198 | 1205 | $tables = array('smw_rels2', 'smw_atts2', 'smw_text2', 'smw_spec2', |
— | — | @@ -1199,7 +1206,7 @@ |
1200 | 1207 | 'smw_conc2'); |
1201 | 1208 | foreach ($tables as $table) { |
1202 | 1209 | $name = $db->tableName($table); |
1203 | | - $db->query("DROP TABLE IF EXISTS $name", 'SMWSQLStore2::drop'); |
| 1210 | + $db->query('DROP TABLE' . ($wgDBtype=='postgres'?'':' IF EXISTS'). $name, 'SMWSQLStore2::drop'); |
1204 | 1211 | $this->reportProgress(" ... dropped table $name.\n", $verbose); |
1205 | 1212 | } |
1206 | 1213 | $this->reportProgress("All data removed successfully.\n",$verbose); |
— | — | @@ -1478,131 +1485,11 @@ |
1479 | 1486 | return $result; |
1480 | 1487 | } |
1481 | 1488 | |
1482 | | - |
1483 | 1489 | /** |
1484 | | - * Make sure the table of the given name has the given fields, provided |
1485 | | - * as an array with entries fieldname => typeparams. typeparams should be |
1486 | | - * in a normalised form and order to match to existing values. |
1487 | | - * |
1488 | | - * The function returns an array that includes all columns that have been |
1489 | | - * changed. For each such column, the array contains an entry |
1490 | | - * columnname => action, where action is one of 'up', 'new', or 'del' |
1491 | | - * If the table was already fine or was created completely anew, an empty |
1492 | | - * array is returned (assuming that both cases require no action). |
1493 | | - * |
1494 | | - * @note The function partly ignores the order in which fields are set up. |
1495 | | - * Only if the type of some field changes will its order be adjusted explicitly. |
1496 | | - */ |
1497 | | - protected function setupTable($table, $fields, $db, $verbose) { |
1498 | | - global $wgDBname; |
1499 | | - $this->reportProgress("Setting up table $table ...\n",$verbose); |
1500 | | - if ($db->tableExists($table) === false) { // create new table |
1501 | | - $sql = 'CREATE TABLE `' . $wgDBname . '`.' . $table . ' ('; |
1502 | | - $first = true; |
1503 | | - foreach ($fields as $name => $type) { |
1504 | | - if ($first) { |
1505 | | - $first = false; |
1506 | | - } else { |
1507 | | - $sql .= ','; |
1508 | | - } |
1509 | | - $sql .= $name . ' ' . $type; |
1510 | | - } |
1511 | | - $sql .= ') TYPE=innodb'; |
1512 | | - $db->query( $sql, 'SMWSQLStore2::setupTable' ); |
1513 | | - $this->reportProgress(" ... new table created\n",$verbose); |
1514 | | - return array(); |
1515 | | - } else { // check table signature |
1516 | | - $this->reportProgress(" ... table exists already, checking structure ...\n",$verbose); |
1517 | | - $res = $db->query( 'DESCRIBE ' . $table, 'SMWSQLStore2::setupTable' ); |
1518 | | - $curfields = array(); |
1519 | | - $result = array(); |
1520 | | - while ($row = $db->fetchObject($res)) { |
1521 | | - $type = strtoupper($row->Type); |
1522 | | - if (substr($type,0,8) == 'VARCHAR(') { |
1523 | | - $type .= ' binary'; // just assume this to be the case for VARCHAR, avoid collation checks |
1524 | | - } |
1525 | | - if ($row->Null != 'YES') { |
1526 | | - $type .= ' NOT NULL'; |
1527 | | - } |
1528 | | - if ($row->Key == 'PRI') { /// FIXME: updating "KEY" is not possible, the below query will fail in this case. |
1529 | | - $type .= ' KEY'; |
1530 | | - } |
1531 | | - if ($row->Extra == 'auto_increment') { |
1532 | | - $type .= ' AUTO_INCREMENT'; |
1533 | | - } |
1534 | | - $curfields[$row->Field] = $type; |
1535 | | - } |
1536 | | - $position = 'FIRST'; |
1537 | | - foreach ($fields as $name => $type) { |
1538 | | - if ( !array_key_exists($name,$curfields) ) { |
1539 | | - $this->reportProgress(" ... creating column $name ... ",$verbose); |
1540 | | - $db->query("ALTER TABLE $table ADD `$name` $type $position", 'SMWSQLStore2::setupTable'); |
1541 | | - $result[$name] = 'new'; |
1542 | | - $this->reportProgress("done \n",$verbose); |
1543 | | - } elseif ($curfields[$name] != $type) { |
1544 | | - $this->reportProgress(" ... changing type of column $name from '$curfields[$name]' to '$type' ... ",$verbose); |
1545 | | - $db->query("ALTER TABLE $table CHANGE `$name` `$name` $type $position", 'SMWSQLStore2::setupTable'); |
1546 | | - $result[$name] = 'up'; |
1547 | | - $curfields[$name] = false; |
1548 | | - $this->reportProgress("done.\n",$verbose); |
1549 | | - } else { |
1550 | | - $this->reportProgress(" ... column $name is fine\n",$verbose); |
1551 | | - $curfields[$name] = false; |
1552 | | - } |
1553 | | - $position = "AFTER $name"; |
1554 | | - } |
1555 | | - foreach ($curfields as $name => $value) { |
1556 | | - if ($value !== false) { // not encountered yet --> delete |
1557 | | - $this->reportProgress(" ... deleting obsolete column $name ... ",$verbose); |
1558 | | - $db->query("ALTER TABLE $table DROP COLUMN `$name`", 'SMWSQLStore2::setupTable'); |
1559 | | - $result[$name] = 'del'; |
1560 | | - $this->reportProgress("done.\n",$verbose); |
1561 | | - } |
1562 | | - } |
1563 | | - $this->reportProgress(" ... table $table set up successfully.\n",$verbose); |
1564 | | - return $result; |
1565 | | - } |
1566 | | - } |
1567 | | - |
1568 | | - /** |
1569 | | - * Make sure that each of the column descriptions in the given array is indexed by *one* index |
1570 | | - * in the given DB table. |
1571 | | - */ |
1572 | | - protected function setupIndex($table, $columns, $db) { |
1573 | | - $table = $db->tableName($table); |
1574 | | - $res = $db->query( 'SHOW INDEX FROM ' . $table , 'SMW::SetupIndex'); |
1575 | | - if ( !$res ) { |
1576 | | - return false; |
1577 | | - } |
1578 | | - $indexes = array(); |
1579 | | - while ( $row = $db->fetchObject( $res ) ) { |
1580 | | - if (!array_key_exists($row->Key_name, $indexes)) { |
1581 | | - $indexes[$row->Key_name] = array(); |
1582 | | - } |
1583 | | - $indexes[$row->Key_name][$row->Seq_in_index] = $row->Column_name; |
1584 | | - } |
1585 | | - foreach ($indexes as $key => $index) { // clean up existing indexes |
1586 | | - $id = array_search(implode(',', $index), $columns ); |
1587 | | - if ( $id !== false ) { |
1588 | | - $columns[$id] = false; |
1589 | | - } else { // duplicate or unrequired index |
1590 | | - $db->query( 'DROP INDEX ' . $key . ' ON ' . $table, 'SMW::SetupIndex'); |
1591 | | - } |
1592 | | - } |
1593 | | - |
1594 | | - foreach ($columns as $key => $column) { // add remaining indexes |
1595 | | - if ($column != false) { |
1596 | | - $db->query( "ALTER TABLE $table ADD INDEX ( $column )", 'SMW::SetupIndex'); |
1597 | | - } |
1598 | | - } |
1599 | | - return true; |
1600 | | - } |
1601 | | - |
1602 | | - /** |
1603 | 1490 | * Print some output to indicate progress. The output message is given by |
1604 | 1491 | * $msg, while $verbose indicates whether or not output is desired at all. |
1605 | 1492 | */ |
1606 | | - protected function reportProgress($msg, $verbose) { |
| 1493 | + public function reportProgress($msg, $verbose = true) { |
1607 | 1494 | if (!$verbose) { |
1608 | 1495 | return; |
1609 | 1496 | } |
— | — | @@ -1709,13 +1596,14 @@ |
1710 | 1597 | * the title is a redirect target (we do not want chains of redirects). |
1711 | 1598 | */ |
1712 | 1599 | protected function makeSMWPageID($title, $namespace, $iw, $canonical=true, $sortkey = '') { |
| 1600 | + global $wgDBtype; |
1713 | 1601 | wfProfileIn('SMWSQLStore2::makeSMWPageID (SMW)'); |
1714 | 1602 | $oldsort = ''; |
1715 | 1603 | $id = $this->getSMWPageIDandSort($title, $namespace, $iw, $oldsort, $canonical); |
1716 | 1604 | if ($id == 0) { |
1717 | 1605 | $db =& wfGetDB( DB_MASTER ); |
1718 | 1606 | $sortkey = $sortkey?$sortkey:(str_replace('_',' ',$title)); |
1719 | | - $db->insert('smw_ids', array('smw_id' => 0, 'smw_title' => $title, 'smw_namespace' => $namespace, 'smw_iw' => $iw, 'smw_sortkey' => $sortkey), 'SMW::makeSMWPageID'); |
| 1607 | + $db->insert('smw_ids', array('smw_id' => (($wgDBtype=='postgres')?($db->nextSequenceValue('smw_ids_smw_id_seq')):0), 'smw_title' => $title, 'smw_namespace' => $namespace, 'smw_iw' => $iw, 'smw_sortkey' => $sortkey), 'SMW::makeSMWPageID'); |
1720 | 1608 | $id = $db->insertId(); |
1721 | 1609 | $this->m_ids["$iw $namespace $title -"] = $id; // fill that cache, even if canonical was given |
1722 | 1610 | // This ID is also authorative for the canonical version. |
— | — | @@ -1797,6 +1685,7 @@ |
1798 | 1686 | * a new bnode id! |
1799 | 1687 | */ |
1800 | 1688 | protected function makeSMWBnodeID($sid) { |
| 1689 | + global $wgDBtype; |
1801 | 1690 | $db =& wfGetDB( DB_MASTER ); |
1802 | 1691 | $id = 0; |
1803 | 1692 | // check if there is an unused bnode to take: |
— | — | @@ -1813,7 +1702,7 @@ |
1814 | 1703 | } |
1815 | 1704 | // if no node was found yet, make a new one: |
1816 | 1705 | if ($id == 0) { |
1817 | | - $db->insert('smw_ids', array('smw_id' => 0, 'smw_title' => '', 'smw_namespace' => $sid, 'smw_iw' => SMW_SQL2_SMWIW), 'SMW::makeSMWBnodeID'); |
| 1706 | + $db->insert('smw_ids', array('smw_id' => (($wgDBtype=='postgres')?($db->nextSequenceValue('smw_ids_smw_id_seq')):0), 'smw_title' => '', 'smw_namespace' => $sid, 'smw_iw' => SMW_SQL2_SMWIW), 'SMW::makeSMWBnodeID'); |
1818 | 1707 | $id = $db->insertId(); |
1819 | 1708 | } |
1820 | 1709 | return $id; |
— | — | @@ -1827,11 +1716,12 @@ |
1828 | 1717 | * to an id that is still in use somewhere). |
1829 | 1718 | */ |
1830 | 1719 | protected function moveID($curid, $targetid = 0) { |
| 1720 | + global $wgDBtype; |
1831 | 1721 | $db =& wfGetDB( DB_MASTER ); |
1832 | 1722 | $row = $db->selectRow('smw_ids', array('smw_id', 'smw_namespace', 'smw_title', 'smw_iw', 'smw_sortkey'), array('smw_id' => $curid), 'SMWSQLStore2::moveID'); |
1833 | 1723 | if ($row === false) return; // no id at current position, ignore |
1834 | 1724 | if ($targetid == 0) { |
1835 | | - $db->insert('smw_ids', array('smw_id' => 0, 'smw_title' => $row->smw_title, 'smw_namespace' => $row->smw_namespace, 'smw_iw' => $row->smw_iw, 'smw_sortkey' => $row->smw_sortkey), 'SMW::moveID'); |
| 1725 | + $db->insert('smw_ids', array('smw_id' => (($wgDBtype=='postgres')?($db->nextSequenceValue('smw_ids_smw_id_seq')):0), 'smw_title' => $row->smw_title, 'smw_namespace' => $row->smw_namespace, 'smw_iw' => $row->smw_iw, 'smw_sortkey' => $row->smw_sortkey), 'SMW::moveID'); |
1836 | 1726 | $targetid = $db->insertId(); |
1837 | 1727 | } else { |
1838 | 1728 | $db->insert('smw_ids', array('smw_id' => $targetid, 'smw_title' => $row->smw_title, 'smw_namespace' => $row->smw_namespace, 'smw_iw' => $row->smw_iw, 'smw_sortkey' => $row->smw_sortkey), 'SMW::moveID'); |
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLHelpers.php |
— | — | @@ -0,0 +1,287 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Some static helper functions that SMW uses for setting up |
| 5 | + * SQL databases. |
| 6 | + * |
| 7 | + * @author Markus Krötzsch |
| 8 | + * @author Marcel Gsteiger |
| 9 | + * @file |
| 10 | + * @ingroup SMWStore |
| 11 | + */ |
| 12 | + |
| 13 | +/** |
| 14 | + * Static class to collect some helper functions that SMW uses |
| 15 | + * for settnig up SQL databases. |
| 16 | + * @ingroup SMWStore |
| 17 | + */ |
| 18 | +class SMWSQLHelpers { |
| 19 | + |
| 20 | + /** |
| 21 | + * Database backends often have different types that need to be used |
| 22 | + * repeatedly in (Semantic) MediaWiki. This function provides the preferred |
| 23 | + * type (as a string) for various common kinds of columns. The input |
| 24 | + * is one of the following strings: 'id' (page id numbers or similar), |
| 25 | + * 'title' (title strings or similar), 'namespace' (namespace numbers), |
| 26 | + * 'blob' (longer text blobs), 'iw' (interwiki prefixes). |
| 27 | + */ |
| 28 | + static public function getStandardDBType($input) { |
| 29 | + global $wgDBtype; |
| 30 | + switch ($input) { |
| 31 | + case 'id': return $wgDBtype=='postgres'?'SERIAL':'INT(8) UNSIGNED'; // like page_id in MW page table |
| 32 | + case 'namespace': return $wgDBtype=='postgres'?'BIGINT':'INT(11)'; // like page_namespace in MW page table |
| 33 | + case 'title': return $wgDBtype=='postgres'?'TEXT':'VARCHAR(255) binary'; // like page_title in MW page table |
| 34 | + case 'iw': return $wgDBtype=='postgres'?'TEXT':'VARCHAR(32) binary'; // like iw_prefix in MW interwiki table |
| 35 | + case 'blob': return $wgDBtype=='postgres'?'BYTEA':'MEDIUMBLOB'; // larger blobs of character data, usually not subject to SELECT conditions |
| 36 | + } |
| 37 | + return false; |
| 38 | + } |
| 39 | + |
| 40 | + /** |
| 41 | + * Generic creation and updating function for database tables. Ideally, it |
| 42 | + * would be able to modify a table's signature in arbitrary ways, but it will |
| 43 | + * fail for some changes. Its string-based interface is somewhat too |
| 44 | + * impoverished for a permanent solution. It would be possible to go for update |
| 45 | + * scripts (specific to each change) in the style of MediaWiki instead. |
| 46 | + * |
| 47 | + * Make sure the table of the given name has the given fields, provided |
| 48 | + * as an array with entries fieldname => typeparams. typeparams should be |
| 49 | + * in a normalised form and order to match to existing values. |
| 50 | + * |
| 51 | + * The function returns an array that includes all columns that have been |
| 52 | + * changed. For each such column, the array contains an entry |
| 53 | + * columnname => action, where action is one of 'up', 'new', or 'del' |
| 54 | + * If the table was already fine or was created completely anew, an empty |
| 55 | + * array is returned (assuming that both cases require no action). |
| 56 | + * |
| 57 | + * If progress reports during this operation are desired, then the parameter $reportTo should |
| 58 | + * be given an object that has a method reportProgress(string) for doing so. |
| 59 | + * |
| 60 | + * @note The function partly ignores the order in which fields are set up. |
| 61 | + * Only if the type of some field changes will its order be adjusted explicitly. |
| 62 | + */ |
| 63 | + public static function setupTable($table, $fields, $db, $reportTo = NULL) { |
| 64 | + global $wgDBname, $wgDBtype; |
| 65 | + $fname = 'SMWSQLHelpers::setupTable'; |
| 66 | + |
| 67 | + SMWSQLHelpers::reportProgress("Setting up table $table ...\n",$reportTo); |
| 68 | + if ($db->tableExists($table) === false) { // create new table |
| 69 | + $sql = 'CREATE TABLE ' . ($wgDBtype=='postgres'?'': "`$wgDBname`.") . $table . ' ('; |
| 70 | + $first = true; |
| 71 | + foreach ($fields as $name => $type) { |
| 72 | + if ($first) { |
| 73 | + $first = false; |
| 74 | + } else { |
| 75 | + $sql .= ','; |
| 76 | + } |
| 77 | + $sql .= $name . ' ' . $type; |
| 78 | + } |
| 79 | + $sql .= ') ' . ($wgDBtype=='postgres'?'':'TYPE=innodb'); |
| 80 | + $db->query( $sql, $fname ); |
| 81 | + SMWSQLHelpers::reportProgress(" ... new table created\n",$reportTo); |
| 82 | + return array(); |
| 83 | + } else { // check table signature |
| 84 | + SMWSQLHelpers::reportProgress(" ... table exists already, checking structure ...\n",$reportTo); |
| 85 | + if ($wgDBtype=='postgres') { // postgresql |
| 86 | + // use the data dictionary in postgresql to get an output comparable to DESCRIBE |
| 87 | + // To find out what kind of magic takes place here (and to remove the bugs included), simply use: |
| 88 | + // psql |
| 89 | + // \set ECHO_HIDDEN |
| 90 | + // \d <tablename> |
| 91 | + $sql = 'SELECT a.attname as "Field", ' |
| 92 | + .' upper(pg_catalog.format_type(a.atttypid, a.atttypmod)) as "Type", ' |
| 93 | + .' (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128) ' |
| 94 | + .' FROM pg_catalog.pg_attrdef d ' |
| 95 | + .' WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) as "Extra", ' |
| 96 | + .' case when a.attnotnull THEN \'NO\'::text else \'YES\'::text END as "Null", a.attnum ' |
| 97 | + .' FROM pg_catalog.pg_attribute a ' |
| 98 | + .' WHERE a.attrelid = (' |
| 99 | + .' SELECT c.oid ' |
| 100 | + .' FROM pg_catalog.pg_class c ' |
| 101 | + .' LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace ' |
| 102 | + .' WHERE c.relname ~ \'^(' . $table . ')$\' ' |
| 103 | + .' AND pg_catalog.pg_table_is_visible(c.oid) ' |
| 104 | + .' LIMIT 1 ' |
| 105 | + .' ) AND a.attnum > 0 AND NOT a.attisdropped ' |
| 106 | + .' ORDER BY a.attnum'; |
| 107 | + } else { // mysql |
| 108 | + $sql = 'DESCRIBE ' . $table; |
| 109 | + } |
| 110 | + $res = $db->query($sql, $fname); |
| 111 | + $curfields = array(); |
| 112 | + $result = array(); |
| 113 | + while ($row = $db->fetchObject($res)) { |
| 114 | + $type = strtoupper($row->Type); |
| 115 | + if ($wgDBtype=='postgres') { // postgresql |
| 116 | + if (eregi('^nextval\\(.+\\)$',$row->Extra)) { |
| 117 | + $type = 'SERIAL NOT NULL'; |
| 118 | + } elseif ($row->Null != 'YES') { |
| 119 | + $type .= ' NOT NULL'; |
| 120 | + } |
| 121 | + } else { // mysql |
| 122 | + if (substr($type,0,8) == 'VARCHAR(') { |
| 123 | + $type .= ' binary'; // just assume this to be the case for VARCHAR, though DESCRIBE will not tell us |
| 124 | + } |
| 125 | + if ($row->Null != 'YES') { |
| 126 | + $type .= ' NOT NULL'; |
| 127 | + } |
| 128 | + if ($row->Key == 'PRI') { /// FIXME: updating "KEY" is not possible, the below query will fail in this case. |
| 129 | + $type .= ' KEY'; |
| 130 | + } |
| 131 | + if ($row->Extra == 'auto_increment') { |
| 132 | + $type .= ' AUTO_INCREMENT'; |
| 133 | + } |
| 134 | + } |
| 135 | + $curfields[$row->Field] = $type; |
| 136 | + } |
| 137 | + |
| 138 | + if ($wgDBtype=='postgres') { // postgresql |
| 139 | + foreach ($fields as $name => $type) { |
| 140 | + $keypos = strpos($type,' PRIMARY KEY'); |
| 141 | + if ($keypos > 0) { |
| 142 | + $type=substr($type,0,$keypos); |
| 143 | + } |
| 144 | + if ( !array_key_exists($name,$curfields) ) { |
| 145 | + SMWSQLHelpers::reportProgress(" ... creating column $name ... ",$reportTo); |
| 146 | + $db->query("ALTER TABLE $table ADD \"" . $name . "\" $type", $fname); |
| 147 | + $result[$name] = 'new'; |
| 148 | + SMWSQLHelpers::reportProgress("done \n",$reportTo); |
| 149 | + } elseif ($curfields[$name] != $type) { |
| 150 | + SMWSQLHelpers::reportProgress(" ... changing type of column $name from '$curfields[$name]' to '$type' ... ",$reportTo); |
| 151 | + $notnullposnew = strpos($type,' NOT NULL'); |
| 152 | + if ($notnullposnew > 0) { |
| 153 | + $type=substr($type,0,$notnullposnew); |
| 154 | + } |
| 155 | + $notnullposold = strpos($curfields[$name],' NOT NULL'); |
| 156 | + $typeold = ($notnullposold > 0)?substr($curfields[$name],0,$notnullposold):$curfields[$name]; |
| 157 | + if ($typeold!=$type) { |
| 158 | + $db->query("ALTER TABLE \"".$table."\" ALTER COLUMN \"".$name."\" TYPE " . $type, $fname); |
| 159 | + } |
| 160 | + if ($notnullposold!=$notnullposnew) { |
| 161 | + $db->query("ALTER TABLE \"". $table . "\" ALTER COLUMN \"".$name."\" ".($notnullposnew>0?'SET':'DROP'). " NOT NULL", $fname); |
| 162 | + } |
| 163 | + $result[$name] = 'up'; |
| 164 | + $curfields[$name] = false; |
| 165 | + SMWSQLHelpers::reportProgress("done.\n",$reportTo); |
| 166 | + } else { |
| 167 | + SMWSQLHelpers::reportProgress(" ... column $name is fine\n",$reportTo); |
| 168 | + $curfields[$name] = false; |
| 169 | + } |
| 170 | + } |
| 171 | + foreach ($curfields as $name => $value) { |
| 172 | + if ($value !== false) { |
| 173 | + SMWSQLHelpers::reportProgress(" ... deleting obsolete column $name ... ",$reportTo); |
| 174 | + $db->query("ALTER TABLE \"". $table . "\" DROP COLUMN \"" . $name . "\"", $fname); |
| 175 | + $result[$name] = 'del'; |
| 176 | + SMWSQLHelpers::reportProgress("done.\n",$reportTo); |
| 177 | + } |
| 178 | + } |
| 179 | + } else { // mysql |
| 180 | + $position = 'FIRST'; |
| 181 | + foreach ($fields as $name => $type) { |
| 182 | + if ( !array_key_exists($name,$curfields) ) { |
| 183 | + SMWSQLHelpers::reportProgress(" ... creating column $name ... ",$reportTo); |
| 184 | + $db->query("ALTER TABLE $table ADD `$name` $type $position", $fname); |
| 185 | + $result[$name] = 'new'; |
| 186 | + SMWSQLHelpers::reportProgress("done \n",$reportTo); |
| 187 | + } elseif ($curfields[$name] != $type) { |
| 188 | + SMWSQLHelpers::reportProgress(" ... changing type of column $name from '$curfields[$name]' to '$type' ... ",$reportTo); |
| 189 | + $db->query("ALTER TABLE $table CHANGE `$name` `$name` $type $position", $fname); |
| 190 | + $result[$name] = 'up'; |
| 191 | + $curfields[$name] = false; |
| 192 | + SMWSQLHelpers::reportProgress("done.\n",$reportTo); |
| 193 | + } else { |
| 194 | + SMWSQLHelpers::reportProgress(" ... column $name is fine\n",$reportTo); |
| 195 | + $curfields[$name] = false; |
| 196 | + } |
| 197 | + $position = "AFTER $name"; |
| 198 | + } |
| 199 | + foreach ($curfields as $name => $value) { |
| 200 | + if ($value !== false) { // not encountered yet --> delete |
| 201 | + SMWSQLHelpers::reportProgress(" ... deleting obsolete column $name ... ",$reportTo); |
| 202 | + $db->query("ALTER TABLE $table DROP COLUMN `$name`", $fname); |
| 203 | + $result[$name] = 'del'; |
| 204 | + SMWSQLHelpers::reportProgress("done.\n",$reportTo); |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + SMWSQLHelpers::reportProgress(" ... table $table set up successfully.\n",$reportTo); |
| 210 | + return $result; |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + /** |
| 215 | + * Make sure that each of the column descriptions in the given array is indexed by *one* index |
| 216 | + * in the given DB table. |
| 217 | + */ |
| 218 | + public static function setupIndex($table, $columns, $db, $reportTo = NULL) { |
| 219 | + global $wgDBtype,$verbose; |
| 220 | + $table = $db->tableName($table); |
| 221 | + $fname = 'SMWSQLHelpers::setupIndex'; |
| 222 | + |
| 223 | + if ($wgDBtype=='postgres') { // postgresql |
| 224 | + $sql= "SELECT i.relname AS indexname," |
| 225 | + . " pg_get_indexdef(i.oid) AS indexdef, " |
| 226 | + . " replace(substring(pg_get_indexdef(i.oid) from '\\\\((.*)\\\\)'),' ','') AS indexcolumns" |
| 227 | + . " FROM pg_index x" |
| 228 | + . " JOIN pg_class c ON c.oid = x.indrelid" |
| 229 | + ." JOIN pg_class i ON i.oid = x.indexrelid" |
| 230 | + ." LEFT JOIN pg_namespace n ON n.oid = c.relnamespace" |
| 231 | + ." LEFT JOIN pg_tablespace t ON t.oid = i.reltablespace" |
| 232 | + ." WHERE c.relkind = 'r'::\"char\" AND i.relkind = 'i'::\"char\"" |
| 233 | + ." AND c.relname = '" . $table . "'" |
| 234 | + ." AND NOT pg_get_indexdef(i.oid) ~ '^CREATE UNIQUE INDEX'"; |
| 235 | + $res = $db->query($sql,$fname); |
| 236 | + if ( !$res ) { |
| 237 | + return false; |
| 238 | + } |
| 239 | + $indexes = array(); |
| 240 | + while ( $row = $db->fetchObject( $res ) ) { |
| 241 | + // remove unneeded indexes, let indexes alone that already exist in the correct fashion |
| 242 | + if (array_key_exists($row->indexcolumns,$columns)) { |
| 243 | + $columns[$row->indexcolumns]=false; |
| 244 | + } else { |
| 245 | + $db->query('DROP INDEX IF EXISTS ' . $row->indexname, $fname); |
| 246 | + } |
| 247 | + } |
| 248 | + foreach ($columns as $key => $column) { // add remaining indexes |
| 249 | + if ($column != false) { |
| 250 | + $db->query("CREATE INDEX " . $table . "_index" . $key . " ON " . $table . " USING btree(" . $column . ")", $fname); |
| 251 | + } |
| 252 | + } |
| 253 | + } else { // mysql |
| 254 | + $res = $db->query( 'SHOW INDEX FROM ' . $table , $fname); |
| 255 | + if ( !$res ) { |
| 256 | + return false; |
| 257 | + } |
| 258 | + $indexes = array(); |
| 259 | + while ( $row = $db->fetchObject( $res ) ) { |
| 260 | + if (!array_key_exists($row->Key_name, $indexes)) { |
| 261 | + $indexes[$row->Key_name] = array(); |
| 262 | + } |
| 263 | + $indexes[$row->Key_name][$row->Seq_in_index] = $row->Column_name; |
| 264 | + } |
| 265 | + foreach ($indexes as $key => $index) { // clean up existing indexes |
| 266 | + $id = array_search(implode(',', $index), $columns ); |
| 267 | + if ( $id !== false ) { |
| 268 | + $columns[$id] = false; |
| 269 | + } else { // duplicate or unrequired index |
| 270 | + $db->query( 'DROP INDEX ' . $key . ' ON ' . $table, $fname); |
| 271 | + } |
| 272 | + } |
| 273 | + |
| 274 | + foreach ($columns as $key => $column) { // add remaining indexes |
| 275 | + if ($column != false) { |
| 276 | + $db->query( "ALTER TABLE $table ADD INDEX ( $column )", $fname); |
| 277 | + } |
| 278 | + } |
| 279 | + } |
| 280 | + return true; |
| 281 | + } |
| 282 | + |
| 283 | + /// If a receiver is given, report the given message to its reportProgress method. |
| 284 | + protected static function reportProgress($msg, $receiver = NULL) { |
| 285 | + if ($receiver !== NULL) $receiver->reportProgress($msg); |
| 286 | + } |
| 287 | + |
| 288 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLHelpers.php |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 289 | + native |
Index: trunk/extensions/SemanticMediaWiki/includes/SMW_GlobalFunctions.php |
— | — | @@ -14,7 +14,7 @@ |
15 | 15 | * @defgroup SMW Semantic MediaWiki |
16 | 16 | */ |
17 | 17 | |
18 | | -define('SMW_VERSION','1.5c-SVN'); |
| 18 | +define('SMW_VERSION','1.5d-SVN'); |
19 | 19 | |
20 | 20 | // constants for displaying the factbox |
21 | 21 | define('SMW_FACTBOX_HIDDEN', 1); |
— | — | @@ -169,6 +169,7 @@ |
170 | 170 | $wgAutoloadClasses['SMWDisjunction'] = $smwgIP . '/includes/storage/SMW_Description.php'; |
171 | 171 | $wgAutoloadClasses['SMWSomeProperty'] = $smwgIP . '/includes/storage/SMW_Description.php'; |
172 | 172 | $wgAutoloadClasses['SMWSQLStore2'] = $smwgIP . '/includes/storage/SMW_SQLStore2.php'; |
| 173 | + $wgAutoloadClasses['SMWSQLHelpers'] = $smwgIP . '/includes/storage/SMW_SQLHelpers.php'; |
173 | 174 | // Do not autoload RAPStore, since some special pages load all autoloaded classes, which causes |
174 | 175 | // troubles with RAP store if RAP is not installed (require_once fails). |
175 | 176 | //$wgAutoloadClasses['SMWRAPStore'] = $smwgIP . '/includes/storage/SMW_RAPStore.php'; |