migration to mysql
Some checks failed
PHPUnit / test (push) Has been cancelled

This commit is contained in:
2026-07-10 09:57:44 +02:00
parent 351af170a0
commit 810ed24792
24 changed files with 986 additions and 208 deletions

View File

@ -181,10 +181,10 @@ case 'DELETE':
json_success(['deleted' => false, 'retired' => true, 'structureRevision' => $newRev]);
break;
}
$pdo->exec('PRAGMA foreign_keys = OFF;');
qdb_set_foreign_keys($pdo, false);
$pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id')->execute([':id' => $id]);
$pdo->prepare('DELETE FROM answer_option WHERE answerOptionID = :id')->execute([':id' => $id]);
$pdo->exec('PRAGMA foreign_keys = ON;');
qdb_set_foreign_keys($pdo, true);
$pdo->commit();
qdb_save($tmpDb, $lockFp);
json_success(['deleted' => true, 'retired' => false, 'structureRevision' => $newRev]);

View File

@ -180,15 +180,19 @@ if ($method === 'POST') {
$glassByParent = [];
$submittedQuestionIds = [];
$answerInsert = $pdo->prepare("
$answerInsert = $pdo->prepare('
INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue, answeredAt)
VALUES (:cc, :qid, :aoid, :ftv, :nv, :at)
ON CONFLICT(clientCode, questionID) DO UPDATE SET
answerOptionID = excluded.answerOptionID,
freeTextValue = excluded.freeTextValue,
numericValue = excluded.numericValue,
answeredAt = excluded.answeredAt
");
VALUES (:cc, :qid, :aoid, :ftv, :nv, :at) '
. qdb_upsert_update(
['clientCode', 'questionID'],
[
'answerOptionID' => 'excluded.answerOptionID',
'freeTextValue' => 'excluded.freeTextValue',
'numericValue' => 'excluded.numericValue',
'answeredAt' => 'excluded.answeredAt',
]
)
);
$answerDelete = $pdo->prepare(
'DELETE FROM client_answer WHERE clientCode = :cc AND questionID = :qid'
);
@ -298,16 +302,20 @@ if ($method === 'POST') {
? qdb_compute_questionnaire_score_from_manifest($pdo, $clientCode, $submitManifest)
: qdb_compute_questionnaire_score($pdo, $clientCode, $qnID);
$pdo->prepare("
$pdo->prepare('
INSERT INTO completed_questionnaire (clientCode, questionnaireID, assignedByCoach, status, startedAt, completedAt, sumPoints)
VALUES (:cc, :qn, :abc, 'completed', :sa, :ca, :sp)
ON CONFLICT(clientCode, questionnaireID) DO UPDATE SET
assignedByCoach = excluded.assignedByCoach,
status = 'completed',
startedAt = COALESCE(excluded.startedAt, startedAt),
completedAt = excluded.completedAt,
sumPoints = excluded.sumPoints
")->execute([
VALUES (:cc, :qn, :abc, \'completed\', :sa, :ca, :sp) '
. qdb_upsert_update(
['clientCode', 'questionnaireID'],
[
'assignedByCoach' => 'excluded.assignedByCoach',
'status' => "'completed'",
'startedAt' => 'COALESCE(excluded.startedAt, startedAt)',
'completedAt' => 'excluded.completedAt',
'sumPoints' => 'excluded.sumPoints',
]
)
)->execute([
':cc' => $clientCode,
':qn' => $qnID,
':abc' => $assignedByCoach !== '' ? $assignedByCoach : null,

View File

@ -7,21 +7,70 @@ if ($method !== 'POST') {
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
}
$dbPath = QDB_PATH;
$backupDir = QDB_UPLOADS_DIR . '/backups';
if (!file_exists($dbPath)) {
json_error('NOT_FOUND', 'No database to back up', 404);
}
if (!is_dir($backupDir)) {
if (!mkdir($backupDir, 0755, true) && !is_dir($backupDir)) {
json_error('SERVER_ERROR', 'Could not create backups directory', 500);
}
}
$timestamp = date('Y-m-d_H-i-s');
$filename = "questionnaire_database.$timestamp";
$timestamp = date('Y-m-d_H-i-s');
if (qdb_is_mysql()) {
[$host, $port] = [qdb_env_get('QDB_MYSQL_HOST') ?: '127.0.0.1', qdb_env_get('QDB_MYSQL_PORT') ?: '3306'];
[$user, $pass] = qdb_mysql_credentials();
$db = qdb_env_get('QDB_MYSQL_DATABASE') ?: 'nat-as-db';
$filename = "questionnaire_mysql_{$timestamp}.sql";
$backupFile = "$backupDir/$filename";
if (!function_exists('proc_open')) {
json_error('SERVER_ERROR', 'MySQL backup is unavailable: proc_open is disabled', 500);
}
$command = [
'mysqldump',
'--single-transaction',
'--routines',
'--triggers',
"--host={$host}",
"--port={$port}",
"--user={$user}",
$db,
];
$environment = getenv();
if (!is_array($environment)) {
$environment = [];
}
if ($pass !== '') {
$environment['MYSQL_PWD'] = $pass;
}
$descriptors = [
0 => ['file', '/dev/null', 'r'],
1 => ['file', $backupFile, 'wb'],
2 => ['pipe', 'w'],
];
$process = @proc_open($command, $descriptors, $pipes, null, $environment);
if (!is_resource($process)) {
@unlink($backupFile);
json_error('SERVER_ERROR', 'MySQL backup failed to start mysqldump', 500);
}
$errorOutput = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$code = proc_close($process);
if ($code !== 0 || !is_file($backupFile) || filesize($backupFile) === 0) {
@unlink($backupFile);
$detail = trim((string)$errorOutput);
json_error('SERVER_ERROR', 'MySQL backup failed' . ($detail !== '' ? ": {$detail}" : ''), 500);
}
@chmod($backupFile, 0600);
json_success(['filename' => $filename]);
}
$dbPath = QDB_PATH;
if (!file_exists($dbPath)) {
json_error('NOT_FOUND', 'No database to back up', 404);
}
$filename = "questionnaire_database.$timestamp";
$backupFile = "$backupDir/$filename";
if (!copy($dbPath, $backupFile)) {

View File

@ -107,9 +107,9 @@ case 'POST':
$replaceIfExists = !empty($body['replaceIfExists']);
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
try {
$pdo->exec('PRAGMA foreign_keys = OFF;');
qdb_set_foreign_keys($pdo, false);
$result = qdb_import_questionnaires_bundle($pdo, $bundle, $replaceIfExists);
$pdo->exec('PRAGMA foreign_keys = ON;');
qdb_set_foreign_keys($pdo, true);
qdb_save($tmpDb, $lockFp);
json_success($result);
} catch (Throwable $e) {
@ -208,7 +208,7 @@ case 'DELETE':
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
try {
$pdo->exec('PRAGMA foreign_keys = OFF;');
qdb_set_foreign_keys($pdo, false);
$pdo->beginTransaction();
$qIds = $pdo->prepare('SELECT questionID FROM question WHERE questionnaireID = :id');
@ -231,7 +231,7 @@ case 'DELETE':
$pdo->prepare('DELETE FROM questionnaire WHERE questionnaireID = :id')->execute([':id' => $id]);
$pdo->commit();
$pdo->exec('PRAGMA foreign_keys = ON;');
qdb_set_foreign_keys($pdo, true);
qdb_save($tmpDb, $lockFp);
json_success([]);
} catch (Throwable $e) {

View File

@ -277,7 +277,7 @@ case 'DELETE':
break;
}
$pdo->exec('PRAGMA foreign_keys = OFF;');
qdb_set_foreign_keys($pdo, false);
$aoIds = $pdo->prepare('SELECT answerOptionID FROM answer_option WHERE questionID = :id');
$aoIds->execute([':id' => $id]);
foreach ($aoIds->fetchAll(PDO::FETCH_COLUMN) as $aoid) {
@ -287,7 +287,7 @@ case 'DELETE':
$pdo->prepare('DELETE FROM question_score_rule WHERE questionID = :id')->execute([':id' => $id]);
$pdo->prepare('DELETE FROM question_translation WHERE questionID = :id')->execute([':id' => $id]);
$pdo->prepare('DELETE FROM question WHERE questionID = :id')->execute([':id' => $id]);
$pdo->exec('PRAGMA foreign_keys = ON;');
qdb_set_foreign_keys($pdo, true);
$pdo->commit();
qdb_save($tmpDb, $lockFp);
json_success([

View File

@ -232,12 +232,12 @@ case 'PUT':
if ($lang === QDB_SOURCE_LANGUAGE) {
qdb_ensure_source_language($pdo);
} elseif (qdb_column_exists($pdo, 'language', 'enabled')) {
$pdo->prepare("INSERT INTO language (languageCode, name, enabled) VALUES (:lc, :n, 1)
ON CONFLICT(languageCode) DO UPDATE SET name = excluded.name")
$pdo->prepare('INSERT INTO language (languageCode, name, enabled) VALUES (:lc, :n, 1) '
. qdb_upsert_update(['languageCode'], ['name' => 'excluded.name', 'enabled' => '1']))
->execute([':lc' => $lang, ':n' => $name]);
} else {
$pdo->prepare("INSERT INTO language (languageCode, name) VALUES (:lc, :n)
ON CONFLICT(languageCode) DO UPDATE SET name = excluded.name")
$pdo->prepare('INSERT INTO language (languageCode, name) VALUES (:lc, :n) '
. qdb_upsert_update(['languageCode'], ['name' => 'excluded.name']))
->execute([':lc' => $lang, ':n' => $name]);
}
qdb_save($tmpDb, $lockFp);