Skip to content

Commit 1fbf852

Browse files
author
Colin Yang
committed
Back - Implemented DbMySQL & Optmised DbSaeKV
1 parent 8c85448 commit 1fbf852

File tree

4 files changed

+209
-114
lines changed

4 files changed

+209
-114
lines changed

backend/class/api/Manage.class.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,11 @@ function handle() {
131131
: [ 'status' => STATUS_API_FAILED, 'result' => "Failed deleting key: $key" ];
132132
}
133133
case 'erase_all': {
134-
foreach (DbProvider::getDb()->getAll() as $index => $key_value_pair) {
135-
DbProvider::getDb()->delete($key_value_pair['key']);
136-
}
134+
DbProvider::getDb()->eraseAll();
137135
return [ 'result' => 'All data erased' ];
138136
}
139137
case 'erase_data': {
140-
foreach (DbProvider::getDb()->getAll() as $index => $key_value_pair) {
141-
if (!DbBase::keyReserved($key_value_pair['key'])) {
142-
DbProvider::getDb()->delete($key_value_pair['key']);
143-
}
144-
}
138+
DbProvider::getDb()->eraseData();
145139
return [ 'result' => 'All data erased (except for reserved keys)' ];
146140
}
147141
case 'erase_pwd': {

backend/class/db/Base.class.php

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,17 @@ abstract function delete(string $key);
5555
* @param array<string> $keys
5656
* @return array<string=>bool> array of succeed or not, with keys paired
5757
*/
58-
abstract function mDelete(array $keys);
58+
function mDelete(array $keys) {
59+
$retarr = [];
60+
foreach ($keys as $index => $key) {
61+
if (!is_string($key)) {
62+
$retarr[$key] = false;
63+
continue;
64+
}
65+
$retarr[$key] = $this->delete($key);
66+
}
67+
return $retarr;
68+
}
5969

6070
/**
6171
* add / update value of the key
@@ -118,4 +128,63 @@ abstract function getAll(string $prefix = '');
118128
*/
119129
abstract function search(string $text, int $page, bool $ignoreCase, array $range);
120130

131+
/**
132+
* erase all keys that is not reserved
133+
*/
134+
abstract function eraseData();
135+
136+
/**
137+
* erase all keys (include reserved keys)
138+
*/
139+
abstract function eraseAll();
140+
141+
}
142+
143+
abstract class DbBaseIterator implements Iterator {
144+
145+
public static $PERPAGE = 100;
146+
147+
protected $prefix = '';
148+
149+
// one-based
150+
protected $page = 0;
151+
protected $currentValue = [];
152+
// zero-based
153+
protected $position = -1;
154+
155+
function __construct(string $prefix) {
156+
$this->prefix = $prefix;
157+
$this->rewind();
158+
}
159+
160+
/**
161+
* Called in next(), should load next page into $currentValue
162+
*/
163+
abstract function getNextPage();
164+
165+
public function current() {
166+
return $this->currentValue[$this->position];
167+
}
168+
public function key() {
169+
return ($this->page - 1) * 100 + $this->position;
170+
}
171+
public function next() {
172+
if ($this->position >= 0 && $this->position + 1 < count($this->currentValue)) {
173+
$this->position ++;
174+
} else {
175+
$this->page ++;
176+
$this->position = 0;
177+
$this->getNextPage();
178+
}
179+
}
180+
public function rewind() {
181+
$this->page = 0;
182+
$this->position = -1;
183+
$this->next();
184+
}
185+
public function valid() {
186+
return count($this->currentValue) > 0
187+
&& $this->position < count($this->currentValue);
188+
}
189+
121190
}

backend/class/db/MySQL.class.php

Lines changed: 114 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -34,95 +34,152 @@ function setTimeout(int $timeout) {
3434
$this->timeout = $timeout;
3535
}
3636

37-
private function mysqli() {
38-
$link = mysqli_init();
39-
$link->options(11 /*MYSQL_OPT_READ_TIMEOUT*/, $this->timeout);
40-
$ret = $link->real_connect($this->host, $this->user, $this->password, $this->database, $this->port, $this->socket);
37+
private function runMysql(string $statment, $paramTypes = null, &...$params) {
38+
$mysqli = mysqli_init();
39+
$mysqli->options(11 /*MYSQL_OPT_READ_TIMEOUT*/, $this->timeout);
40+
$ret = $mysqli->real_connect($this->host, $this->user, $this->password, $this->database, $this->port, $this->socket);
4141
if ($ret === false) {
42-
throw new Exception('Cannot connect to mysql server: ' . mysqli_error($link));
42+
throw new Exception('Cannot connect to mysql server: ' . mysqli_error($mysqli));
4343
}
44-
return $link;
45-
}
46-
47-
function delete(string $key) {
48-
$mysqli = $this->mysqli();
49-
$stmt = $mysqli->prepare("DELETE FROM `$this->table` WHERE `key` = ?");
44+
$stmt = $mysqli->prepare($statment);
5045
if ($stmt === false) {
5146
throw new Exception('Cannot prepare stmt: ' . mysqli_error($mysqli));
5247
}
53-
if (!$stmt->bind_param('s', $key)) {
54-
throw new Exception("Cannot bind param: $stmt->error");
48+
if ($paramTypes !== null && !$stmt->bind_param($paramTypes, ...$params)) {
49+
throw new Exception("Cannot bind param($stmt->errno): $stmt->error");
5550
}
56-
if (!($result = $stmt->execute())) {
51+
if (!$stmt->execute()) {
5752
throw new Exception("Cannot execute query: $stmt->error");
5853
}
54+
$result = $stmt->get_result();
5955
$stmt->close();
6056
$mysqli->close();
6157
return $result;
6258
}
6359

64-
function set(string $key, string $value) {
65-
$mysqli = $this->mysqli();
66-
$stmt = $mysqli->prepare("INSERT INTO `$this->table` (`key`,`value`) VALUES (?, ?)");
67-
if ($stmt === false) {
68-
throw new Exception('Cannot prepare stmt: ' . mysqli_error($mysqli));
69-
}
70-
if (!$stmt->bind_param('ss', $key, $value)) {
71-
throw new Exception("Cannot bind param: $stmt->error");
60+
function has(string $key) {
61+
$result = $this->runMysql("SELECT EXISTS(SELECT `value` FROM `$this->table` WHERE `key` = ? LIMIT 1)", 's', $key);
62+
return $result->fetch_array(MYSQLI_NUM)[0] > 0;
63+
}
64+
65+
function count(string $prefix = '') {
66+
$prefix .= '%';
67+
$result = $this->runMysql("SELECT COUNT(`value`) FROM `$this->table` WHERE `key` LIKE ?", 's', $prefix);
68+
return $result->fetch_array(MYSQLI_NUM)[0];
69+
}
70+
71+
function delete(string $key) {
72+
if (!$this->has($key)) {
73+
return true;
7274
}
73-
if (!($result = $stmt->execute())) {
74-
throw new Exception("Cannot execute query: $stmt->error");
75+
try {
76+
$this->runMysql("DELETE FROM `$this->table` WHERE `key` = ?", 's', $key);
77+
} catch (Exception $e) {
78+
return false;
7579
}
76-
$stmt->close();
77-
$mysqli->close();
78-
return $result;
80+
return true;
7981
}
8082

81-
function get(string $key) {
82-
$mysqli = $this->mysqli();
83-
$stmt = $mysqli->prepare("SELECT `value` FROM `$this->table` WHERE `key` = ? LIMIT 1");
84-
if ($stmt === false) {
85-
throw new Exception('Cannot prepare stmt: ' . mysqli_error($mysqli));
83+
function set(string $key, string $value) {
84+
return $this->has($key) ? $this->update($key, $value) : $this->add($key, $value);
85+
}
86+
87+
function add(string $key, string $value) {
88+
if ($this->has($key)) {
89+
return false;
8690
}
87-
if (!$stmt->bind_param('s', $key)) {
88-
throw new Exception("Cannot bind param: $stmt->error");
91+
try {
92+
$this->runMysql("INSERT INTO `$this->table` (`key`,`value`) VALUES (?, ?)", 'ss', $key, $value);
93+
} catch (Exception $e) {
94+
return false;
8995
}
90-
if (!$stmt->execute()) {
91-
throw new Exception("Cannot execute query: $stmt->error");
96+
return true;
97+
}
98+
99+
function update(string $key, string $value) {
100+
try {
101+
$this->runMysql("UPDATE `$this->table` SET `value` = ? WHERE `key` = ?", 'ss', $value, $key);
102+
} catch (Exception $e) {
103+
return false;
92104
}
93-
$result = $stmt->get_result();
94-
$rtn = false;
105+
return true;
106+
}
107+
108+
function get(string $key) {
109+
$result = $this->runMysql("SELECT `value` FROM `$this->table` WHERE `key` = ? LIMIT 1", 's', $key);
95110
$row = $result->fetch_array(MYSQLI_ASSOC);
96-
if (isset($row['value'])) {
97-
$rtn = $row['value'];
98-
}
99-
$stmt->close();
100-
$mysqli->close();
101-
return $rtn;
111+
return isset($row['value']) ? $row['value'] : false;
102112
}
103113

104114
function getPage(int $page = 1, int $perPage = 100, string $prefix = '') {
105-
$mysqli = $this->mysqli();
106-
$stmt = $mysqli->prepare("SELECT * FROM `$this->table` WHERE `key` LIKE ? LIMIT ?, ?");
107-
if ($stmt === false) {
108-
throw new Exception('Cannot prepare stmt: ' . mysqli_error($mysqli));
109-
}
110115
$prefix .= '%';
111116
$start = ($page - 1) * $perPage;
112-
if (!$stmt->bind_param('sii', $prefix, $start, $perPage)) {
113-
throw new Exception("Cannot bind param: $stmt->error");
117+
$result = $this->runMysql("SELECT * FROM `$this->table` WHERE `key` LIKE ? LIMIT ?, ?", 'sii', $prefix, $start, $perPage);
118+
$rtn = [];
119+
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
120+
$rtn[] = $row;
114121
}
115-
if (!$stmt->execute()) {
116-
throw new Exception("Cannot execute query: $stmt->error");
122+
return $rtn;
123+
}
124+
125+
function getAll(string $prefix = '') {
126+
return new DbMySQLIterator($prefix, $this);
127+
}
128+
129+
function search(string $text, int $page, bool $ignoreCase, array $range) {
130+
$text = '%' . $text . '%';
131+
if ($text === '%%') {
132+
$text = '%';
117133
}
118-
$result = $stmt->get_result();
134+
$caseProcessing = $ignoreCase ? 'UPPER' : '';
135+
$start = ($page - 1) * DbBase::$SEARCH_RESULT_PER_PAGE;
136+
$sql = "SELECT * FROM `$this->table` WHERE ";
137+
$sqlParamTypes = '';
138+
$sqlParams = [];
139+
foreach ($range as $index => $field) {
140+
$range[$index] = "$caseProcessing(`$field`) LIKE $caseProcessing(?)";
141+
$sqlParamTypes .= 's';
142+
$sqlParams[] = $text;
143+
}
144+
$sql .= implode(' OR ', $range);
145+
146+
$countResult = $this->runMysql(str_replace('SELECT *', 'SELECT COUNT(`value`)', $sql), $sqlParamTypes, ...$sqlParams);
147+
$count = $countResult->fetch_array(MYSQLI_NUM)[0];
148+
149+
$sql .= " LIMIT ?, ?";
150+
$sqlParamTypes .= 'ii';
151+
$sqlParams[] = $start;
152+
$sqlParams[] = DbBase::$SEARCH_RESULT_PER_PAGE;
153+
$result = $this->runMysql($sql, $sqlParamTypes, ...$sqlParams);
119154
$rtn = [];
120155
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
121156
$rtn[] = $row;
122157
}
123-
$stmt->close();
124-
$mysqli->close();
125-
return $rtn;
158+
return [ 'count' => $count, 'result' => $rtn ];
159+
}
160+
161+
function eraseData() {
162+
$reservedPrefix = DbBase::$KEY_RESERVED_PREFIX . '%';
163+
$this->runMysql("DELETE FROM `$this->table` WHERE `key` NOT LIKE ?", 's', $reservedPrefix);
164+
}
165+
166+
function eraseAll() {
167+
$this->runMysql("DELETE FROM `$this->table`");
168+
}
169+
170+
}
171+
172+
class DbMySQLIterator extends DbBaseIterator {
173+
174+
private $dbMySQL;
175+
176+
function __construct(string $prefix, DbMySQL $dbMySQL) {
177+
$this->dbMySQL = $dbMySQL;
178+
parent::__construct($prefix);
179+
}
180+
181+
public function getNextPage() {
182+
$this->currentValue = $this->dbMySQL->getPage($this->page, DbBaseIterator::$PERPAGE, $this->prefix);
126183
}
127184

128185
}

0 commit comments

Comments
 (0)