1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Work around MySQL syntax weirdness

Also improve test for token translation to actually test that the
translated tokens are accepted by the database system
This commit is contained in:
J. King 2021-01-07 10:12:38 -05:00
parent 47ae65b9d3
commit 461e256052
4 changed files with 27 additions and 13 deletions

View file

@ -762,6 +762,7 @@ class Database {
// validate inputs // validate inputs
$folder = $this->folderValidateId($user, $folder)['id']; $folder = $this->folderValidateId($user, $folder)['id'];
// create a complex query // create a complex query
$integer = $this->db->sqlToken("integer");
$q = new Query( $q = new Query(
"SELECT "SELECT
s.id as id, s.id as id,
@ -789,7 +790,7 @@ class Database {
select select
subscription, subscription,
sum(hidden) as hidden, sum(hidden) as hidden,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked sum(cast((\"read\" = 1 and hidden = 0) as $integer)) as marked
from arsse_marks group by subscription from arsse_marks group by subscription
) as mark_stats on mark_stats.subscription = s.id" ) as mark_stats on mark_stats.subscription = s.id"
); );
@ -1211,7 +1212,7 @@ class Database {
* - "block": The block rule; any article which matches this rule are hidden * - "block": The block rule; any article which matches this rule are hidden
*/ */
public function feedRulesGet(int $feedID): Db\Result { public function feedRulesGet(int $feedID): Db\Result {
return $this->db->prepare("SELECT owner, coalesce(keep_rule, '') as keep, coalesce(block_rule, '') as block from arsse_subscriptions where feed = ? and (keep || block) <> '' order by owner", "int")->run($feedID); return $this->db->prepare("SELECT owner, coalesce(keep_rule, '') as keep, coalesce(block_rule, '') as block from arsse_subscriptions where feed = ? and (coalesce(keep_rule, '') || coalesce(block_rule, '')) <> '' order by owner", "int")->run($feedID);
} }
/** Retrieves various identifiers for the latest $count articles in the given newsfeed. The identifiers are: /** Retrieves various identifiers for the latest $count articles in the given newsfeed. The identifiers are:
@ -1803,6 +1804,7 @@ class Database {
/** Deletes from the database articles which are beyond the configured clean-up threshold */ /** Deletes from the database articles which are beyond the configured clean-up threshold */
public function articleCleanup(): bool { public function articleCleanup(): bool {
$integer = $this->db->sqlToken("integer");
$query = $this->db->prepareArray( $query = $this->db->prepareArray(
"WITH RECURSIVE "WITH RECURSIVE
exempt_articles as ( exempt_articles as (
@ -1828,8 +1830,8 @@ class Database {
left join ( left join (
select select
article, article,
sum(cast((starred = 1 and hidden = 0) as integer)) as starred, sum(cast((starred = 1 and hidden = 0) as $integer)) as starred,
sum(cast((\"read\" = 1 or hidden = 1) as integer)) as \"read\", sum(cast((\"read\" = 1 or hidden = 1) as $integer)) as \"read\",
max(arsse_marks.modified) as marked_date max(arsse_marks.modified) as marked_date
from arsse_marks from arsse_marks
group by article group by article
@ -1960,6 +1962,7 @@ class Database {
* @param boolean $includeEmpty Whether to include (true) or supress (false) labels which have no articles assigned to them * @param boolean $includeEmpty Whether to include (true) or supress (false) labels which have no articles assigned to them
*/ */
public function labelList(string $user, bool $includeEmpty = true): Db\Result { public function labelList(string $user, bool $includeEmpty = true): Db\Result {
$integer = $this->db->sqlToken("integer");
return $this->db->prepareArray( return $this->db->prepareArray(
"SELECT * FROM ( "SELECT * FROM (
SELECT SELECT
@ -1975,7 +1978,7 @@ class Database {
SELECT SELECT
label, label,
sum(hidden) as hidden, sum(hidden) as hidden,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked sum(cast((\"read\" = 1 and hidden = 0) as $integer)) as marked
from arsse_marks from arsse_marks
join arsse_subscriptions on arsse_subscriptions.id = arsse_marks.subscription join arsse_subscriptions on arsse_subscriptions.id = arsse_marks.subscription
join arsse_label_members on arsse_label_members.article = arsse_marks.article join arsse_label_members on arsse_label_members.article = arsse_marks.article
@ -2025,6 +2028,7 @@ class Database {
$this->labelValidateId($user, $id, $byName, false); $this->labelValidateId($user, $id, $byName, false);
$field = $byName ? "name" : "id"; $field = $byName ? "name" : "id";
$type = $byName ? "str" : "int"; $type = $byName ? "str" : "int";
$integer = $this->db->sqlToken("integer");
$out = $this->db->prepareArray( $out = $this->db->prepareArray(
"SELECT "SELECT
id, id,
@ -2039,7 +2043,7 @@ class Database {
SELECT SELECT
label, label,
sum(hidden) as hidden, sum(hidden) as hidden,
sum(cast((\"read\" = 1 and hidden = 0) as integer)) as marked sum(cast((\"read\" = 1 and hidden = 0) as $integer)) as marked
from arsse_marks from arsse_marks
join arsse_subscriptions on arsse_subscriptions.id = arsse_marks.subscription join arsse_subscriptions on arsse_subscriptions.id = arsse_marks.subscription
join arsse_label_members on arsse_label_members.article = arsse_marks.article join arsse_label_members on arsse_label_members.article = arsse_marks.article

View file

@ -74,6 +74,7 @@ interface Driver {
* - "greatest": the GREATEST function implemented by PostgreSQL and MySQL * - "greatest": the GREATEST function implemented by PostgreSQL and MySQL
* - "nocase": the name of a general-purpose case-insensitive collation sequence * - "nocase": the name of a general-purpose case-insensitive collation sequence
* - "like": the case-insensitive LIKE operator * - "like": the case-insensitive LIKE operator
* - "integer": the integer type to use for explicit casts
*/ */
public function sqlToken(string $token): string; public function sqlToken(string $token): string;

View file

@ -81,6 +81,8 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
switch (strtolower($token)) { switch (strtolower($token)) {
case "nocase": case "nocase":
return '"utf8mb4_unicode_ci"'; return '"utf8mb4_unicode_ci"';
case "integer":
return "signed integer";
default: default:
return $token; return $token;
} }

View file

@ -90,13 +90,6 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
$this->assertTrue($this->drv->charsetAcceptable()); $this->assertTrue($this->drv->charsetAcceptable());
} }
public function testTranslateAToken(): void {
$this->assertRegExp("/^[a-z][a-z0-9]*$/i", $this->drv->sqlToken("greatest"));
$this->assertRegExp("/^\"?[a-z][a-z0-9_\-]*\"?$/i", $this->drv->sqlToken("nocase"));
$this->assertRegExp("/^[a-z][a-z0-9]*$/i", $this->drv->sqlToken("like"));
$this->assertSame("distinct", $this->drv->sqlToken("distinct"));
}
public function testExecAValidStatement(): void { public function testExecAValidStatement(): void {
$this->assertTrue($this->drv->exec($this->create)); $this->assertTrue($this->drv->exec($this->create));
} }
@ -386,4 +379,18 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
// this performs maintenance in the absence of tables; see BaseUpdate.php for another test with tables // this performs maintenance in the absence of tables; see BaseUpdate.php for another test with tables
$this->assertTrue($this->drv->maintenance()); $this->assertTrue($this->drv->maintenance());
} }
public function testTranslateTokens(): void {
$greatest = $this->drv->sqlToken("GrEatESt");
$nocase = $this->drv->sqlToken("noCASE");
$like = $this->drv->sqlToken("liKe");
$integer = $this->drv->sqlToken("InTEGer");
$this->assertSame("NOT_A_TOKEN", $this->drv->sqlToken("NOT_A_TOKEN"));
$this->assertSame("Z", $this->drv->query("SELECT $greatest('Z', 'A')")->getValue());
$this->assertSame("Z", $this->drv->query("SELECT 'Z' collate $nocase")->getValue());
$this->assertSame("Z", $this->drv->query("SELECT 'Z' where 'Z' $like 'z'")->getValue());
$this->assertEquals(1, $this->drv->query("SELECT CAST((1=1) as $integer)")->getValue());
}
} }