@@ -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