mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Start on removal of conditional CTEs
This breaks the code for now, but will make clearer queries once done
This commit is contained in:
parent
4a87926dd5
commit
396ca86482
1 changed files with 44 additions and 70 deletions
114
lib/Database.php
114
lib/Database.php
|
@ -1534,7 +1534,20 @@ class Database {
|
||||||
assert(strlen($outColumns) > 0, new \Exception("No input columns matched whitelist"));
|
assert(strlen($outColumns) > 0, new \Exception("No input columns matched whitelist"));
|
||||||
// define the basic query, to which we add lots of stuff where necessary
|
// define the basic query, to which we add lots of stuff where necessary
|
||||||
$q = new Query(
|
$q = new Query(
|
||||||
"SELECT
|
"WITH RECURSIVE
|
||||||
|
topmost(f_id,top) as (
|
||||||
|
select id,id from arsse_folders where owner = ? and parent is null union all select id,top from arsse_folders join topmost on parent=f_id
|
||||||
|
),
|
||||||
|
folder_data(id,name,top,top_name) as (
|
||||||
|
select f1.id, f1.name, top, f2.name from arsse_folders as f1 join topmost on f1.id = f_id join arsse_folders as f2 on f2.id = top
|
||||||
|
),
|
||||||
|
labelled(article,label_id,label_name) as (
|
||||||
|
select m.article, l.id, l.name from arsse_label_members as m join arsse_labels as l on l.id = m.label where l.owner = ? and m.assigned = 1
|
||||||
|
),
|
||||||
|
tagged(subscription,tag_id,tag_name) as (
|
||||||
|
select m.subscription, t.id, t.name from arsse_tag_members as m join arsse_tags as t on t.id = m.tag where t.owner = ? and m.assigned = 1
|
||||||
|
)
|
||||||
|
select
|
||||||
$outColumns
|
$outColumns
|
||||||
from arsse_articles
|
from arsse_articles
|
||||||
join arsse_subscriptions on arsse_subscriptions.feed = arsse_articles.feed and arsse_subscriptions.owner = ?
|
join arsse_subscriptions on arsse_subscriptions.feed = arsse_articles.feed and arsse_subscriptions.owner = ?
|
||||||
|
@ -1543,16 +1556,14 @@ class Database {
|
||||||
left join arsse_marks on arsse_marks.subscription = arsse_subscriptions.id and arsse_marks.article = arsse_articles.id
|
left join arsse_marks on arsse_marks.subscription = arsse_subscriptions.id and arsse_marks.article = arsse_articles.id
|
||||||
left join arsse_enclosures on arsse_enclosures.article = arsse_articles.id
|
left join arsse_enclosures on arsse_enclosures.article = arsse_articles.id
|
||||||
join (
|
join (
|
||||||
SELECT article, max(id) as edition from arsse_editions group by article
|
select article, max(id) as edition from arsse_editions group by article
|
||||||
) as latest_editions on arsse_articles.id = latest_editions.article
|
) as latest_editions on arsse_articles.id = latest_editions.article
|
||||||
left join (
|
left join (
|
||||||
SELECT arsse_label_members.article, max(arsse_label_members.modified) as modified, sum(arsse_label_members.assigned) as assigned from arsse_label_members join arsse_labels on arsse_labels.id = arsse_label_members.label where arsse_labels.owner = ? group by arsse_label_members.article
|
select arsse_label_members.article, max(arsse_label_members.modified) as modified, sum(arsse_label_members.assigned) as assigned from arsse_label_members join arsse_labels on arsse_labels.id = arsse_label_members.label where arsse_labels.owner = ? group by arsse_label_members.article
|
||||||
) as label_stats on label_stats.article = arsse_articles.id",
|
) as label_stats on label_stats.article = arsse_articles.id",
|
||||||
["str", "str"],
|
["str", "str", "str", "str", "str"],
|
||||||
[$user, $user]
|
[$user, $user, $user, $user, $user]
|
||||||
);
|
);
|
||||||
$q->setCTE("topmost(f_id,top)", "SELECT id,id from arsse_folders where owner = ? and parent is null union all select id,top from arsse_folders join topmost on parent=f_id", ["str"], [$user]);
|
|
||||||
$q->setCTE("folder_data(id,name,top,top_name)", "SELECT f1.id, f1.name, top, f2.name from arsse_folders as f1 join topmost on f1.id = f_id join arsse_folders as f2 on f2.id = top");
|
|
||||||
$q->setLimit($context->limit, $context->offset);
|
$q->setLimit($context->limit, $context->offset);
|
||||||
// handle the simple context options
|
// handle the simple context options
|
||||||
$options = [
|
$options = [
|
||||||
|
@ -1630,75 +1641,38 @@ class Database {
|
||||||
}
|
}
|
||||||
// handle labels and tags
|
// handle labels and tags
|
||||||
$options = [
|
$options = [
|
||||||
'label' => [
|
'label' => ["labelled", "article", "label_id", "=", "int"],
|
||||||
'match_col' => "arsse_articles.id",
|
'labels' => ["labelled", "article", "label_id", "in", "int"],
|
||||||
'cte_name' => "labelled",
|
'labelName' => ["labelled", "article", "label_name", "=", "str"],
|
||||||
'cte_cols' => ["article", "label_id", "label_name"],
|
'labelNames' => ["labelled", "article", "label_name", "in", "str"],
|
||||||
'cte_body' => "SELECT m.article, l.id, l.name from arsse_label_members as m join arsse_labels as l on l.id = m.label where l.owner = ? and m.assigned = 1",
|
'tag' => ["tagged", "subscription", "tag_id", "=", "int"],
|
||||||
'cte_types' => ["str"],
|
'tags' => ["tagged", "subscription", "tag_id", "in", "int"],
|
||||||
'cte_values' => [$user],
|
'tagName' => ["tagged", "subscription", "tag_name", "=", "str"],
|
||||||
'options' => [
|
'tagNames' => ["tagged", "subscription", "tag_name", "in", "str"],
|
||||||
'label' => ['use_name' => false, 'multi' => false],
|
|
||||||
'labels' => ['use_name' => false, 'multi' => true],
|
|
||||||
'labelName' => ['use_name' => true, 'multi' => false],
|
|
||||||
'labelNames' => ['use_name' => true, 'multi' => true],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'tag' => [
|
|
||||||
'match_col' => "arsse_subscriptions.id",
|
|
||||||
'cte_name' => "tagged",
|
|
||||||
'cte_cols' => ["subscription", "tag_id", "tag_name"],
|
|
||||||
'cte_body' => "SELECT m.subscription, t.id, t.name from arsse_tag_members as m join arsse_tags as t on t.id = m.tag where t.owner = ? and m.assigned = 1",
|
|
||||||
'cte_types' => ["str"],
|
|
||||||
'cte_values' => [$user],
|
|
||||||
'options' => [
|
|
||||||
'tag' => ['use_name' => false, 'multi' => false],
|
|
||||||
'tags' => ['use_name' => false, 'multi' => true],
|
|
||||||
'tagName' => ['use_name' => true, 'multi' => false],
|
|
||||||
'tagNames' => ['use_name' => true, 'multi' => true],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
foreach ($options as $opt) {
|
foreach ($options as $m => [$cte, $col, $selection, $op, $type]) {
|
||||||
$seen = false;
|
if ($context->$m()) {
|
||||||
$match = $opt['match_col'];
|
if ($op === "in") {
|
||||||
$table = $opt['cte_name'];
|
|
||||||
foreach ($opt['options'] as $m => $props) {
|
|
||||||
$named = $props['use_name'];
|
|
||||||
$multi = $props['multi'];
|
|
||||||
$selection = $opt['cte_cols'][0];
|
|
||||||
$col = $opt['cte_cols'][$named ? 2 : 1];
|
|
||||||
if ($context->$m()) {
|
|
||||||
$seen = true;
|
|
||||||
if (!$context->$m) {
|
if (!$context->$m) {
|
||||||
throw new Db\ExceptionInput("tooShort", ['field' => $m, 'action' => $this->caller(), 'min' => 1]); // must have at least one array element
|
throw new Db\ExceptionInput("tooShort", ['field' => $m, 'action' => $this->caller(), 'min' => 1]); // must have at least one array element
|
||||||
}
|
}
|
||||||
if ($multi) {
|
[$inClause, $inTypes, $inValues] = $this->generateIn($context->$m, $type);
|
||||||
[$test, $types, $values] = $this->generateIn($context->$m, $named ? "str" : "int");
|
$q->setWhere("{$colDefs[$col]} in (select $selection from $cte where $col in($inClause))", $inTypes, $inValues);
|
||||||
$test = "in ($test)";
|
} else {
|
||||||
} else {
|
$q->setWhere("{$colDefs[$col]} in (select $selection from $cte where $col = ?)", $type, $$context->$m);
|
||||||
$test = "= ?";
|
|
||||||
$types = $named ? "str" : "int";
|
|
||||||
$values = $context->$m;
|
|
||||||
}
|
|
||||||
$q->setWhere("$match in (select $selection from $table where $col $test)", $types, $values);
|
|
||||||
}
|
|
||||||
if ($context->not->$m()) {
|
|
||||||
$seen = true;
|
|
||||||
if ($multi) {
|
|
||||||
[$test, $types, $values] = $this->generateIn($context->not->$m, $named ? "str" : "int");
|
|
||||||
$test = "in ($test)";
|
|
||||||
} else {
|
|
||||||
$test = "= ?";
|
|
||||||
$types = $named ? "str" : "int";
|
|
||||||
$values = $context->not->$m;
|
|
||||||
}
|
|
||||||
$q->setWhereNot("$match in (select $selection from $table where $col $test)", $types, $values);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($seen) {
|
// handle the exclusionary version
|
||||||
$spec = $opt['cte_name']."(".implode(",", $opt['cte_cols']).")";
|
if ($context->not->$m()) {
|
||||||
$q->setCTE($spec, $opt['cte_body'], $opt['cte_types'], $opt['cte_values']);
|
if ($op === "in") {
|
||||||
|
if (!$context->not->$m) {
|
||||||
|
throw new Db\ExceptionInput("tooShort", ['field' => $m, 'action' => $this->caller(), 'min' => 1]); // must have at least one array element
|
||||||
|
}
|
||||||
|
[$inClause, $inTypes, $inValues] = $this->generateIn($context->not->$m, $type);
|
||||||
|
$q->setWhereNot("{$colDefs[$col]} in (select $selection from $cte where $col in($inClause))", $inTypes, $inValues);
|
||||||
|
} else {
|
||||||
|
$q->setWhereNot("{$colDefs[$col]} in (select $selection from $cte where $col = ?)", $type, $$context->not->$m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle complex context options
|
// handle complex context options
|
||||||
|
|
Loading…
Reference in a new issue