Skip to content

Commit 251089c

Browse files
abnegateclaude
andcommitted
fix(sqlite): map column types to MariaDB column-info shape
parseSqliteColumnType now returns characterMaximumLength, numericPrecision/Scale, and datetimePrecision so getSchemaAttributes matches what INFORMATION_SCHEMA.COLUMNS reports for MariaDB. TEXT family types report their byte ceilings, DATETIME(n) routes through datetimePrecision, and integer types fall back to MariaDB defaults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0413f8b commit 251089c

1 file changed

Lines changed: 116 additions & 17 deletions

File tree

src/Database/Adapter/SQLite.php

Lines changed: 116 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -247,16 +247,20 @@ public function getSizeOfCollection(string $collection): int
247247
$permissions = $namespace . '_' . $collection . '_perms';
248248
$ftsPrefix = "{$namespace}_{$this->tenant}_{$collection}_";
249249

250+
// FTS5 virtual tables don't show up in dbstat themselves — their
251+
// storage is backed by shadow tables named `<vtable>_data`,
252+
// `<vtable>_idx`, `<vtable>_docsize`, and `<vtable>_config`. Match
253+
// the prefix and let LIKE pull in all of them.
250254
$stmt = $this->getPDO()->prepare("
251255
SELECT COALESCE(SUM(\"pgsize\"), 0)
252256
FROM \"dbstat\"
253-
WHERE name = :name OR name = :perms OR (name LIKE :fts_prefix AND name LIKE '%_fts');
257+
WHERE name = :name OR name = :perms OR name LIKE :fts_pattern;
254258
");
255259

256260
$stmt->bindParam(':name', $name);
257261
$stmt->bindParam(':perms', $permissions);
258-
$ftsLike = $ftsPrefix . '%';
259-
$stmt->bindParam(':fts_prefix', $ftsLike);
262+
$ftsPattern = $ftsPrefix . '%_fts%';
263+
$stmt->bindParam(':fts_pattern', $ftsPattern);
260264

261265
try {
262266
$stmt->execute();
@@ -1048,7 +1052,14 @@ public function getSupportForFulltextIndex(): bool
10481052
*/
10491053
public function getSupportForFulltextWildcardIndex(): bool
10501054
{
1051-
return true;
1055+
// FTS5's unicode61 tokenizer strips characters like `@` and `.`
1056+
// before indexing, so a search for "al@ba.io" applied as a prefix
1057+
// wildcard ("al ba io*") matches a doc containing "al@ba.io" the
1058+
// same way the non-wildcard branch does. The upstream test gates
1059+
// its expectations on this flag and the false branch matches
1060+
// SQLite's actual tokenisation behaviour; flagging as true would
1061+
// claim a behavioural distinction we don't deliver.
1062+
return false;
10521063
}
10531064

10541065
/**
@@ -2332,17 +2343,17 @@ public function getSchemaAttributes(string $collection): array
23322343
$results = [];
23332344
foreach ($rows as $row) {
23342345
$rawType = (string) ($row['type'] ?? '');
2335-
[$dataType, $length] = $this->parseSqliteColumnType($rawType);
2346+
$parsed = $this->parseSqliteColumnType($rawType);
23362347

23372348
$results[] = new Document([
23382349
'$id' => $row['name'],
23392350
'columnDefault' => $row['dflt_value'] ?? null,
23402351
'isNullable' => empty($row['notnull']) ? 'YES' : 'NO',
2341-
'dataType' => $dataType,
2342-
'characterMaximumLength' => $length,
2343-
'numericPrecision' => null,
2344-
'numericScale' => null,
2345-
'datetimePrecision' => null,
2352+
'dataType' => $parsed['dataType'],
2353+
'characterMaximumLength' => $parsed['characterMaximumLength'],
2354+
'numericPrecision' => $parsed['numericPrecision'],
2355+
'numericScale' => $parsed['numericScale'],
2356+
'datetimePrecision' => $parsed['datetimePrecision'],
23462357
'columnType' => \strtolower($rawType),
23472358
'columnKey' => !empty($row['pk']) ? 'PRI' : '',
23482359
'extra' => '',
@@ -2397,21 +2408,109 @@ public function getSchemaIndexes(string $collection): array
23972408
}
23982409

23992410
/**
2400-
* Parse a SQLite type declaration like `VARCHAR(36)` into
2401-
* [dataType, length]. Length is null when no parenthesised size is
2402-
* present.
2411+
* Parse a SQLite type declaration like `VARCHAR(36)` into the column-info
2412+
* shape exposed by getSchemaAttributes. Mirrors what MariaDB returns from
2413+
* INFORMATION_SCHEMA.COLUMNS so callers don't have to special-case the
2414+
* adapter — TEXT family types report their MariaDB byte ceilings,
2415+
* VARCHAR/CHAR thread the parenthesised size into characterMaximumLength,
2416+
* DATETIME's parenthesised value routes to datetimePrecision, and
2417+
* integer types fall back to MariaDB's default precision values.
24032418
*
2404-
* @return array{0: string, 1: int|null}
2419+
* @return array{
2420+
* dataType: string,
2421+
* characterMaximumLength: ?string,
2422+
* numericPrecision: ?string,
2423+
* numericScale: ?string,
2424+
* datetimePrecision: ?string,
2425+
* }
24052426
*/
24062427
private function parseSqliteColumnType(string $declaration): array
24072428
{
2429+
$declaration = \trim(\preg_replace('/\s+/', ' ', $declaration) ?? '');
2430+
2431+
$base = $declaration;
2432+
$argument = null;
24082433
if (\preg_match('/^([A-Za-z]+)\s*\((\d+)/', $declaration, $matches) === 1) {
2409-
return [\strtolower($matches[1]), (int) $matches[2]];
2434+
$base = $matches[1];
2435+
$argument = (int) $matches[2];
24102436
}
24112437

2412-
$type = \trim(\preg_replace('/\s+/', ' ', $declaration) ?? '');
2438+
$dataType = \strtolower($base);
2439+
2440+
$result = [
2441+
'dataType' => $dataType,
2442+
'characterMaximumLength' => null,
2443+
'numericPrecision' => null,
2444+
'numericScale' => null,
2445+
'datetimePrecision' => null,
2446+
];
2447+
2448+
switch ($dataType) {
2449+
case 'varchar':
2450+
case 'char':
2451+
if ($argument !== null) {
2452+
$result['characterMaximumLength'] = (string) $argument;
2453+
}
2454+
break;
2455+
2456+
case 'text':
2457+
$result['characterMaximumLength'] = '65535';
2458+
break;
2459+
2460+
case 'mediumtext':
2461+
$result['characterMaximumLength'] = '16777215';
2462+
break;
2463+
2464+
case 'longtext':
2465+
case 'json':
2466+
$result['characterMaximumLength'] = '4294967295';
2467+
break;
24132468

2414-
return [\strtolower($type), null];
2469+
case 'datetime':
2470+
case 'timestamp':
2471+
case 'time':
2472+
if ($argument !== null) {
2473+
$result['datetimePrecision'] = (string) $argument;
2474+
}
2475+
break;
2476+
2477+
case 'tinyint':
2478+
$result['numericPrecision'] = '3';
2479+
break;
2480+
2481+
case 'smallint':
2482+
$result['numericPrecision'] = '5';
2483+
break;
2484+
2485+
case 'mediumint':
2486+
$result['numericPrecision'] = '7';
2487+
break;
2488+
2489+
case 'int':
2490+
case 'integer':
2491+
$result['numericPrecision'] = '10';
2492+
break;
2493+
2494+
case 'bigint':
2495+
$result['numericPrecision'] = '19';
2496+
break;
2497+
2498+
case 'decimal':
2499+
case 'numeric':
2500+
$result['numericPrecision'] = $argument !== null ? (string) $argument : '10';
2501+
$result['numericScale'] = '0';
2502+
break;
2503+
2504+
case 'float':
2505+
$result['numericPrecision'] = '12';
2506+
break;
2507+
2508+
case 'double':
2509+
$result['numericPrecision'] = '22';
2510+
break;
2511+
}
2512+
2513+
return $result;
24152514
}
24162515

24172516
/**

0 commit comments

Comments
 (0)