mirror of
https://github.com/cosmocode/dokuwiki-plugin-struct.git
synced 2025-08-15 21:43:17 +00:00
use one multivalue table per schema
This commit is contained in:
@ -7,6 +7,7 @@ spl_autoload_register(array('action_plugin_struct_autoloader', 'autoloader'));
|
||||
|
||||
use plugin\struct\meta\SchemaBuilder;
|
||||
use plugin\struct\meta\Schema;
|
||||
use plugin\struct\meta;
|
||||
|
||||
/**
|
||||
* Class SchemaData for testing
|
||||
@ -72,40 +73,33 @@ class schemaDataDB_struct_test extends \DokuWikiTest {
|
||||
$builder->build();
|
||||
|
||||
// revision 1
|
||||
/** @noinspection SqlResolve */
|
||||
$this->sqlite->query("INSERT INTO data_testtable (pid, rev, col1) VALUES (?,?,?)", array('testpage', 123, 'value1',));
|
||||
$this->sqlite->query("INSERT INTO multivals (tbl, colref, pid, rev, row, value) VALUES (?,?,?,?,?,?)",
|
||||
array('testtable',2,'testpage',123,1,'value2.1',));
|
||||
$this->sqlite->query("INSERT INTO multivals (tbl, colref, pid, rev, row, value) VALUES (?,?,?,?,?,?)",
|
||||
array('testtable',2,'testpage',123,2,'value2.2',));
|
||||
/** @noinspection SqlResolve */
|
||||
$this->sqlite->query("INSERT INTO multi_testtable (colref, pid, rev, row, value) VALUES (?,?,?,?,?)",
|
||||
array(2,'testpage',123,1,'value2.1',));
|
||||
/** @noinspection SqlResolve */
|
||||
$this->sqlite->query("INSERT INTO multi_testtable (colref, pid, rev, row, value) VALUES (?,?,?,?,?)",
|
||||
array(2,'testpage',123,2,'value2.2',));
|
||||
|
||||
|
||||
// revision 2
|
||||
/** @noinspection SqlResolve */
|
||||
$this->sqlite->query("INSERT INTO data_testtable (pid, rev, col1) VALUES (?,?,?)", array('testpage', 789, 'value1a',));
|
||||
$this->sqlite->query("INSERT INTO multivals (tbl, colref, pid, rev, row, value) VALUES (?,?,?,?,?,?)",
|
||||
array('testtable',2,'testpage',789,1,'value2.1a',));
|
||||
$this->sqlite->query("INSERT INTO multivals (tbl, colref, pid, rev, row, value) VALUES (?,?,?,?,?,?)",
|
||||
array('testtable',2,'testpage',789,2,'value2.2a',));
|
||||
/** @noinspection SqlResolve */
|
||||
$this->sqlite->query("INSERT INTO multi_testtable (colref, pid, rev, row, value) VALUES (?,?,?,?,?)",
|
||||
array(2,'testpage',789,1,'value2.1a',));
|
||||
/** @noinspection SqlResolve */
|
||||
$this->sqlite->query("INSERT INTO multi_testtable (colref, pid, rev, row, value) VALUES (?,?,?,?,?)",
|
||||
array(2,'testpage',789,2,'value2.2a',));
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
|
||||
$res = $this->sqlite->query("SELECT name FROM sqlite_master WHERE type='table'");
|
||||
$tableNames = $this->sqlite->res2arr($res);
|
||||
$tableNames = array_map(function ($value) { return $value['name'];},$tableNames);
|
||||
$this->sqlite->res_close($res);
|
||||
|
||||
foreach ($tableNames as $tableName) {
|
||||
if ($tableName == 'opts') continue;
|
||||
$this->sqlite->query('DROP TABLE ?;', $tableName);
|
||||
}
|
||||
|
||||
$this->sqlite->query("CREATE TABLE schema_assignments ( assign NOT NULL, tbl NOT NULL, PRIMARY KEY(assign, tbl) );");
|
||||
$this->sqlite->query("CREATE TABLE schema_cols ( sid INTEGER REFERENCES schemas (id), colref INTEGER NOT NULL, enabled BOOLEAN DEFAULT 1, tid INTEGER REFERENCES types (id), sort INTEGER NOT NULL, PRIMARY KEY ( sid, colref) )");
|
||||
$this->sqlite->query("CREATE TABLE schemas ( id INTEGER PRIMARY KEY AUTOINCREMENT, tbl NOT NULL, ts INT NOT NULL, chksum DEFAULT '' )");
|
||||
$this->sqlite->query("CREATE TABLE sqlite_sequence(name,seq)");
|
||||
$this->sqlite->query("CREATE TABLE types ( id INTEGER PRIMARY KEY AUTOINCREMENT, class NOT NULL, ismulti BOOLEAN DEFAULT 0, label DEFAULT '', config DEFAULT '' )");
|
||||
$this->sqlite->query("CREATE TABLE multivals ( tbl NOT NULL, colref INTEGER NOT NULL, pid NOT NULL, rev INTEGER NOT NULL, row INTEGER NOT NULL, value, PRIMARY KEY(tbl, colref, pid, rev, row) )");
|
||||
/** @var \helper_plugin_struct_db $sqlite */
|
||||
$sqlite = plugin_load('helper', 'struct_db');
|
||||
$sqlite->resetDB();
|
||||
}
|
||||
|
||||
public function test_getDataFromDB_currentRev() {
|
||||
@ -195,11 +189,12 @@ class schemaDataDB_struct_test extends \DokuWikiTest {
|
||||
);
|
||||
|
||||
// act
|
||||
$schemaData = new \plugin\struct\meta\SchemaData('testtable','testpage', "");
|
||||
$schemaData = new meta\SchemaData('testtable','testpage', "");
|
||||
$result = $schemaData->saveData($testdata);
|
||||
|
||||
// assert
|
||||
$res = $this->sqlite->query("SELECT pid, col1, col2 FROM data_testtable WHERE pid = ? ORDER BY rev DESC LIMIT 1",array('testpage'));
|
||||
/** @noinspection SqlResolve */
|
||||
$res = $this->sqlite->query("SELECT pid, col1, col2 FROM data_testtable WHERE pid = ? ORDER BY rev DESC LIMIT 1", array('testpage'));
|
||||
$actual_saved_single = $this->sqlite->res2row($res);
|
||||
$expected_saved_single = array(
|
||||
'pid' => 'testpage',
|
||||
@ -207,7 +202,8 @@ class schemaDataDB_struct_test extends \DokuWikiTest {
|
||||
'col2' => ''
|
||||
);
|
||||
|
||||
$res = $this->sqlite->query("SELECT colref, row, value FROM multivals WHERE pid = ? AND tbl = ? ORDER BY rev DESC LIMIT 3",array('testpage', 'testtable'));
|
||||
/** @noinspection SqlResolve */
|
||||
$res = $this->sqlite->query("SELECT colref, row, value FROM multi_testtable WHERE pid = ? ORDER BY rev DESC LIMIT 3", array('testpage'));
|
||||
$actual_saved_multi = $this->sqlite->res2arr($res);
|
||||
$expected_saved_multi = array(
|
||||
array(
|
||||
|
@ -74,10 +74,9 @@ class schemaDataSQL_struct_test extends \DokuWikiTest {
|
||||
),
|
||||
"SELECT col1,col2,M3.value AS col3
|
||||
FROM data_testtable DATA
|
||||
LEFT OUTER JOIN multivals M3
|
||||
LEFT OUTER JOIN multi_testtable M3
|
||||
ON DATA.pid = M3.pid
|
||||
AND DATA.rev = M3.rev
|
||||
AND M3.tbl = 'testtable'
|
||||
AND M3.colref = 3
|
||||
WHERE DATA.pid = ?
|
||||
AND DATA.rev = ?",
|
||||
|
@ -1 +1 @@
|
||||
3
|
||||
4
|
||||
|
1
db/update0004.sql
Normal file
1
db/update0004.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE multivals;
|
@ -13,7 +13,19 @@ class helper_plugin_struct_db extends DokuWiki_Plugin {
|
||||
/** @var helper_plugin_sqlite */
|
||||
protected $sqlite;
|
||||
|
||||
/**
|
||||
* helper_plugin_struct_db constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function init() {
|
||||
/** @var helper_plugin_sqlite $sqlite */
|
||||
$this->sqlite = plugin_load('helper', 'sqlite');
|
||||
if(!$this->sqlite) {
|
||||
@ -47,6 +59,19 @@ class helper_plugin_struct_db extends DokuWiki_Plugin {
|
||||
return $this->sqlite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely remove the database and reinitialize it
|
||||
*
|
||||
* You do not want to call this except for testing!
|
||||
*/
|
||||
public function resetDB() {
|
||||
if(!$this->sqlite) return;
|
||||
$file = $this->sqlite->getAdapter()->getDbFile();
|
||||
if(!$file) return;
|
||||
unlink($file);
|
||||
clearstatcache(true, $file);
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4:sw=4:et:
|
||||
|
@ -200,22 +200,36 @@ class SchemaBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a completely new data table with columns yet
|
||||
* Create a completely new data table with no columns yet also create the appropriate
|
||||
* multi value table for the schema
|
||||
*
|
||||
* @todo how do we want to handle indexes?
|
||||
* @return bool
|
||||
*/
|
||||
protected function newDataTable() {
|
||||
$tbl = 'data_' . $this->table;
|
||||
$ok = true;
|
||||
|
||||
$tbl = 'data_' . $this->table;
|
||||
$sql = "CREATE TABLE $tbl (
|
||||
pid NOT NULL,
|
||||
rev INTEGER NOT NULL,
|
||||
latest BOOLEAN NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(pid, rev)
|
||||
)";
|
||||
$ok = $ok && (bool) $this->sqlite->query($sql);
|
||||
|
||||
return (bool) $this->sqlite->query($sql);
|
||||
$tbl = 'multi_' . $this->table;
|
||||
$sql = "CREATE TABLE $tbl (
|
||||
colref INTEGER NOT NULL,
|
||||
pid NOT NULL,
|
||||
rev INTEGER NOT NULL,
|
||||
row INTEGER NOT NULL,
|
||||
value,
|
||||
PRIMARY KEY(colref, pid, rev, row)
|
||||
);";
|
||||
$ok = $ok && (bool) $this->sqlite->query($sql);
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,8 @@ class SchemaData extends Schema {
|
||||
* @return bool success of saving the data to the database
|
||||
*/
|
||||
public function saveData($data) {
|
||||
$table = 'data_' . $this->table;
|
||||
$stable = 'data_' . $this->table;
|
||||
$mtable = 'multi_' . $this->table;
|
||||
|
||||
$colrefs = array_flip($this->labels);
|
||||
$now = $this->ts;
|
||||
@ -48,6 +49,10 @@ class SchemaData extends Schema {
|
||||
$multiopts = array();
|
||||
$singlecols = 'pid, rev, latest';
|
||||
foreach ($data as $colname => $value) {
|
||||
if(!isset($colrefs[$colname])) {
|
||||
throw new StructException("Unknown column %s in schema.", hsc($colname));
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $index => $multivalue) {
|
||||
$multiopts[] = array($colrefs[$colname], $index+1, $multivalue,);
|
||||
@ -57,20 +62,23 @@ class SchemaData extends Schema {
|
||||
$opt[] = $value;
|
||||
}
|
||||
}
|
||||
$singlesql = "INSERT INTO $table ($singlecols) VALUES (" . trim(str_repeat('?,',count($opt)),',') . ")";
|
||||
$multisql = "INSERT INTO multivals (tbl, rev, pid, colref, row, value) VALUES (?,?,?,?,?,?)";
|
||||
$singlesql = "INSERT INTO $stable ($singlecols) VALUES (" . trim(str_repeat('?,',count($opt)),',') . ")";
|
||||
/** @noinspection SqlResolve */
|
||||
$multisql = "INSERT INTO $mtable (rev, pid, colref, row, value) VALUES (?,?,?,?,?)";
|
||||
|
||||
$this->sqlite->query('BEGIN TRANSACTION');
|
||||
|
||||
// remove latest status from previous data
|
||||
$ok = $this->sqlite->query( "UPDATE $table SET latest = 0 WHERE latest = 1 AND pid = ?",array($this->page));
|
||||
/** @noinspection SqlResolve */
|
||||
$ok = $this->sqlite->query( "UPDATE $stable SET latest = 0 WHERE latest = 1 AND pid = ?",array($this->page));
|
||||
|
||||
// insert single values
|
||||
$ok = $ok && $this->sqlite->query($singlesql, $opt);
|
||||
|
||||
|
||||
// insert multi values
|
||||
foreach ($multiopts as $multiopt) {
|
||||
$multiopt = array_merge(array($this->table, $now, $this->page,), $multiopt);
|
||||
$multiopt = array_merge(array($now, $this->page,), $multiopt);
|
||||
$ok = $ok && $this->sqlite->query($multisql, $multiopt);
|
||||
}
|
||||
|
||||
@ -161,7 +169,8 @@ class SchemaData extends Schema {
|
||||
* @return array Two fields: the SQL string and the parameters array
|
||||
*/
|
||||
protected function buildGetDataSQL($singles, $multis) {
|
||||
$table = 'data_' . $this->table;
|
||||
$stable = 'data_' . $this->table;
|
||||
$mtable = 'multi_' . $this->table;
|
||||
|
||||
$colsel = join(',', preg_filter('/^/', 'col', $singles));
|
||||
|
||||
@ -170,15 +179,15 @@ class SchemaData extends Schema {
|
||||
foreach($multis as $col) {
|
||||
$tn = 'M' . $col;
|
||||
$select .= ",$tn.value AS col$col";
|
||||
$join .= "LEFT OUTER JOIN multivals $tn";
|
||||
$join .= "LEFT OUTER JOIN $mtable $tn";
|
||||
$join .= " ON DATA.pid = $tn.pid AND DATA.rev = $tn.rev";
|
||||
$join .= " AND $tn.tbl = '{$this->table}' AND $tn.colref = $col\n";
|
||||
$join .= " AND $tn.colref = $col\n";
|
||||
}
|
||||
|
||||
$where = "WHERE DATA.pid = ? AND DATA.rev = ?";
|
||||
$opt = array($this->page, $this->ts);
|
||||
|
||||
$sql = "$select FROM $table DATA\n$join $where";
|
||||
$sql = "$select FROM $stable DATA\n$join $where";
|
||||
|
||||
return array($sql, $opt);
|
||||
}
|
||||
|
@ -144,9 +144,9 @@ class Search {
|
||||
if($col->isMulti()) {
|
||||
$tn = 'M' . $col->getColref();
|
||||
$select .= "GROUP_CONCAT($tn.value, '$sep') AS $CN, ";
|
||||
$from .= "\nLEFT OUTER JOIN multivals AS $tn";
|
||||
$from .= "\nLEFT OUTER JOIN multi_{$col->getTable()} AS $tn";
|
||||
$from .= " ON data_{$col->getTable()}.pid = $tn.pid AND data_{$col->getTable()}.rev = $tn.rev";
|
||||
$from .= " AND $tn.tbl = '{$col->getTable()}' AND $tn.colref = {$col->getColref()}\n";
|
||||
$from .= " AND $tn.colref = {$col->getColref()}\n";
|
||||
} else {
|
||||
$select .= 'data_' . $col->getTable() . ' . col' . $col->getColref() . " AS $CN, ";
|
||||
$grouping[] = $CN;
|
||||
@ -161,9 +161,9 @@ class Search {
|
||||
/** @var $col Column */
|
||||
if($col->isMulti()) {
|
||||
$tn = 'MN' . $col->getColref(); // FIXME this joins a second time if the column was selected before
|
||||
$from .= "\nLEFT OUTER JOIN multivals AS $tn";
|
||||
$from .= "\nLEFT OUTER JOIN multi_{$col->getTable()} AS $tn";
|
||||
$from .= " ON data_{$col->getTable()}.pid = $tn.pid AND data_{$col->getTable()}.rev = $tn.rev";
|
||||
$from .= " AND $tn.tbl = '{$col->getTable()}' AND $tn.colref = {$col->getColref()}\n";
|
||||
$from .= " AND $tn.colref = {$col->getColref()}\n";
|
||||
|
||||
$column = "$tn.value";
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user