Skip to content

Commit c9568a0

Browse files
[Fix] utf8mb4 for MariaDB missing (#1156)
* closes #1155 * charset fixes * fix
1 parent 51d5a26 commit c9568a0

4 files changed

Lines changed: 87 additions & 39 deletions

File tree

app/Http/Controllers/DatabaseController.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ public function collations(Server $server, ?string $charset = null): JsonRespons
6868

6969
$charsets = $server->database()->type_data['charsets'] ?? [];
7070

71-
return response()->json(data_get($charsets, $charset.'.list', data_get($charsets, $charset.'.default', [])));
71+
return response()->json([
72+
'default' => data_get($charsets, $charset.'.default'),
73+
'list' => data_get($charsets, $charset.'.list', []),
74+
]);
7275
}
7376

7477
#[Post('/', name: 'databases.store')]

app/Services/Database/AbstractDatabase.php

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -269,34 +269,27 @@ public function getCharsets(): array
269269
$charsets = $this->tableToArray($data);
270270

271271
$results = [];
272-
$charsetCollations = [];
273272

274-
foreach ($charsets as $key => $charset) {
275-
if (empty($charsetCollations[$charset[1]])) {
276-
$charsetCollations[$charset[1]] = [];
277-
}
278-
279-
$charsetCollations[$charset[1]][] = $charset[0];
280-
281-
if ($charset[3] === 'Yes') {
282-
$results[$charset[1]] = [
283-
'default' => $charset[0],
284-
'list' => [],
285-
];
273+
foreach ($charsets as $charset) {
274+
$collation = $charset[0];
275+
$charsetName = $charset[1];
286276

277+
if (empty($charsetName) || $charsetName === 'NULL') {
287278
continue;
288279
}
289280

290-
if ($key == count($charsets) - 1) {
291-
$results[$charset[1]] = [
281+
if (! isset($results[$charsetName])) {
282+
$results[$charsetName] = [
292283
'default' => null,
293284
'list' => [],
294285
];
295286
}
296-
}
297287

298-
foreach (array_keys($results) as $charset) {
299-
$results[$charset]['list'] = $charsetCollations[$charset];
288+
$results[$charsetName]['list'][] = $collation;
289+
290+
if (($charset[3] ?? null) === 'Yes') {
291+
$results[$charsetName]['default'] = $collation;
292+
}
300293
}
301294

302295
ksort($results);

resources/js/pages/databases/components/create-database.tsx

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FormEvent, ReactNode, useEffect, useState } from 'react';
1+
import { FormEvent, ReactNode, useRef, useState } from 'react';
22
import {
33
Dialog,
44
DialogClose,
@@ -47,12 +47,8 @@ export default function CreateDatabase({
4747
const [open, setOpen] = useState(false);
4848
const [charsets, setCharsets] = useState<string[]>([]);
4949
const [collations, setCollations] = useState<string[]>([]);
50-
51-
const fetchCharsets = async () => {
52-
axios.get(route('databases.charsets', server)).then((response) => {
53-
setCharsets(response.data);
54-
});
55-
};
50+
const fetchedServer = useRef<number | null>(null);
51+
const latestCollationRequest = useRef(0);
5652

5753
const form = useForm<CreateForm>({
5854
name: '',
@@ -62,14 +58,44 @@ export default function CreateDatabase({
6258
existing_user_id: '',
6359
});
6460

65-
// Auto-load collations when modal opens with a default charset
66-
useEffect(() => {
67-
if (open && form.data.charset && charsets.includes(form.data.charset) && collations.length === 0) {
68-
axios.get(route('databases.collations', { server: server, charset: form.data.charset })).then((response) => {
69-
setCollations(response.data);
70-
});
61+
const resolveCollation = (list: string[], defaultCollation: string | null, current: string): string => {
62+
if (current && list.includes(current)) {
63+
return current;
7164
}
72-
}, [open, charsets, form.data.charset, server, collations]);
65+
if (defaultCollation && list.includes(defaultCollation)) {
66+
return defaultCollation;
67+
}
68+
return list[0] ?? '';
69+
};
70+
71+
const fetchCollations = async (charset: string, current: string): Promise<void> => {
72+
const requestId = ++latestCollationRequest.current;
73+
try {
74+
const response = await axios.get(route('databases.collations', { server: server, charset }));
75+
if (requestId !== latestCollationRequest.current) {
76+
return;
77+
}
78+
setCollations(response.data.list);
79+
form.setData('collation', resolveCollation(response.data.list, response.data.default, current));
80+
} catch {
81+
if (requestId !== latestCollationRequest.current) {
82+
return;
83+
}
84+
setCollations([]);
85+
form.setData('collation', '');
86+
}
87+
};
88+
89+
const fetchCharsets = async () => {
90+
setCollations([]);
91+
const response = await axios.get(route('databases.charsets', server));
92+
fetchedServer.current = server;
93+
setCharsets(response.data);
94+
95+
if (form.data.charset && response.data.includes(form.data.charset)) {
96+
await fetchCollations(form.data.charset, form.data.collation);
97+
}
98+
};
7399

74100
const submit = (e: FormEvent) => {
75101
e.preventDefault();
@@ -89,17 +115,14 @@ export default function CreateDatabase({
89115

90116
const handleOpenChange = (open: boolean) => {
91117
setOpen(open);
92-
if (open && charsets.length === 0) {
118+
if (open && fetchedServer.current !== server) {
93119
fetchCharsets();
94120
}
95121
};
96122

97123
const handleCharsetChange = (value: string) => {
98-
form.setData('collation', '');
99124
form.setData('charset', value);
100-
axios.get(route('databases.collations', { server: server, charset: value })).then((response) => {
101-
setCollations(response.data);
102-
});
125+
void fetchCollations(value, '');
103126
};
104127

105128
return (

tests/Unit/SSH/Services/Database/GetCharsetsTest.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class GetCharsetsTest extends TestCase
4040
];
4141

4242
/**
43-
* @param array<string, string|array<string>> $expected
43+
* @param array<string, array{default: string|null, list: array<int, string>}> $expected
4444
*/
4545
#[DataProvider('data')]
4646
public function test_update_charsets(string $name, string $version, string $output, array $expected): void
@@ -107,6 +107,35 @@ public static function data(): array
107107
EOD,
108108
static::$mysqlCharsets,
109109
],
110+
[
111+
'mariadb',
112+
'11.4',
113+
<<<'EOD'
114+
Collation Charset Id Default Compiled Sortlen
115+
big5_chinese_ci big5 1 Yes Yes 1
116+
big5_bin big5 84 Yes 1
117+
utf8mb4_general_ci utf8mb4 45 Yes 1
118+
utf8mb4_bin utf8mb4 46 Yes 1
119+
uca1400_ai_ci NULL NULL NULL Yes 8
120+
uca1400_ai_cs NULL NULL NULL Yes 8
121+
EOD,
122+
[
123+
'big5' => [
124+
'default' => 'big5_chinese_ci',
125+
'list' => [
126+
'big5_chinese_ci',
127+
'big5_bin',
128+
],
129+
],
130+
'utf8mb4' => [
131+
'default' => null,
132+
'list' => [
133+
'utf8mb4_general_ci',
134+
'utf8mb4_bin',
135+
],
136+
],
137+
],
138+
],
110139
[
111140
'postgresql',
112141
'16',

0 commit comments

Comments
 (0)