3 Commits

Author SHA1 Message Date
7bacfc39a8 WIP
Some checks failed
Build and test / build (push) Failing after 17m17s
2026-04-06 12:55:40 +09:00
c74a5f5cb6 flake.nix: build sqlite with ICU extension 2026-04-06 10:21:59 +09:00
4fbed59143 flake.nix: split off sqlite debugging tools into separate devshell 2026-04-06 10:21:11 +09:00
5 changed files with 71 additions and 44 deletions

View File

@@ -43,7 +43,12 @@
"armv7l-linux" "armv7l-linux"
]; ];
forAllSystems = f: lib.genAttrs systems (system: f system nixpkgs.legacyPackages.${system}); forAllSystems = f: lib.genAttrs systems (system: let
pkgs = import nixpkgs {
inherit system;
overlays = [ self.overlays.sqlite-icu-ext ];
};
in f system pkgs);
in { in {
apps = forAllSystems (system: pkgs: { apps = forAllSystems (system: pkgs: {
default = { default = {
@@ -77,16 +82,12 @@
devShells = forAllSystems (system: pkgs: { devShells = forAllSystems (system: pkgs: {
default = pkgs.mkShell { default = pkgs.mkShell {
buildInputs = with pkgs; [ packages = with pkgs; [
dart dart
gnumake gnumake
lcov lcov
sqldiff sqldiff
sqlite-analyzer sqlite-interactive-icu-ext
sqlite-interactive
sqlite-web
# sqlint
sqlfluff
]; ];
env = { env = {
LIBSQLITE_PATH = "${pkgs.sqlite.out}/lib/libsqlite3.so"; LIBSQLITE_PATH = "${pkgs.sqlite.out}/lib/libsqlite3.so";
@@ -94,8 +95,34 @@
LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.sqlite ]; LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.sqlite ];
}; };
}; };
sqlite-debugging = pkgs.mkShell {
packages = with pkgs; [
sqlite-interactive-icu-ext
sqlite-analyzer
sqlite-web
sqlint
sqlfluff
];
};
}); });
overlays.sqlite-icu-ext = final: prev: let
overrideArgs = prev': {
configureFlags = prev'.configureFlags ++ [
"--with-icu-config=${lib.getExe' prev.icu.dev "icu-config"}"
"--enable-icu-collations"
];
buildInputs = prev'.buildInputs ++ [
prev.icu
];
};
in {
sqlite-icu-ext = prev.sqlite.overrideAttrs overrideArgs;
sqlite-interactive-icu-ext = prev.sqlite-interactive.overrideAttrs overrideArgs;
};
packages = let packages = let
edrdgMetadata = { edrdgMetadata = {
license = [{ license = [{
@@ -129,6 +156,8 @@
ln -s ${src} $out ln -s ${src} $out
''; '';
inherit (pkgs) sqlite-icu-ext sqlite-interactive-icu-ext;
jmdict = pkgs.callPackage ./nix/jmdict.nix { jmdict = pkgs.callPackage ./nix/jmdict.nix {
inherit jmdict-src jmdict-with-examples-src edrdgMetadata; inherit jmdict-src jmdict-with-examples-src edrdgMetadata;
}; };
@@ -143,17 +172,20 @@
database-tool = pkgs.callPackage ./nix/database_tool.nix { database-tool = pkgs.callPackage ./nix/database_tool.nix {
inherit src; inherit src;
sqlite = pkgs.sqlite-icu-ext;
}; };
database = pkgs.callPackage ./nix/database.nix { database = pkgs.callPackage ./nix/database.nix {
inherit (self.packages.${system}) database-tool jmdict radkfile kanjidic2; inherit (self.packages.${system}) database-tool jmdict radkfile kanjidic2;
inherit src; inherit src;
sqlite = pkgs.sqlite-icu-ext;
}; };
database-wal = pkgs.callPackage ./nix/database.nix { database-wal = pkgs.callPackage ./nix/database.nix {
inherit (self.packages.${system}) database-tool jmdict radkfile kanjidic2; inherit (self.packages.${system}) database-tool jmdict radkfile kanjidic2;
inherit src; inherit src;
wal = true; wal = true;
sqlite = pkgs.sqlite-icu-ext;
}; };
docs = pkgs.callPackage ./nix/docs.nix { docs = pkgs.callPackage ./nix/docs.nix {

View File

@@ -10,8 +10,9 @@ import 'package:sqflite_common/sqlite_api.dart';
class ResolvedXref { class ResolvedXref {
Entry entry; Entry entry;
bool ambiguous; bool ambiguous;
int? senseOrderNum;
ResolvedXref(this.entry, this.ambiguous); ResolvedXref(this.entry, this.ambiguous, senseOrderNum);
} }
/// Resolves an xref (pair of kanji, optionally reading, and optionally sense number) to an a specific /// Resolves an xref (pair of kanji, optionally reading, and optionally sense number) to an a specific
@@ -74,9 +75,10 @@ ResolvedXref resolveXref(
'kanjiRef: ${xref.kanjiRef}, readingRef: ${xref.readingRef}, ' 'kanjiRef: ${xref.kanjiRef}, readingRef: ${xref.readingRef}, '
'senseOrderNum: ${xref.senseOrderNum}', 'senseOrderNum: ${xref.senseOrderNum}',
); );
return ResolvedXref(candidateEntries.first, true);
return ResolvedXref(candidateEntries.first, true, xref.senseOrderNum);
} else { } else {
return ResolvedXref(candidateEntries.first, false); return ResolvedXref(candidateEntries.first, false, xref.senseOrderNum);
} }
} }
@@ -161,14 +163,14 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
b.insert(JMdictTableNames.senseRestrictedToKanji, { b.insert(JMdictTableNames.senseRestrictedToKanji, {
'entryId': e.entryId, 'entryId': e.entryId,
'senseId': s.senseId, 'senseId': s.senseId,
'kanji': rk, 'kanjiOrderNum': e.kanji.indexWhere((k) => k.reading == rk) + 1,
}); });
} }
for (final rr in s.restrictedToReading) { for (final rr in s.restrictedToReading) {
b.insert(JMdictTableNames.senseRestrictedToReading, { b.insert(JMdictTableNames.senseRestrictedToReading, {
'entryId': e.entryId, 'entryId': e.entryId,
'senseId': s.senseId, 'senseId': s.senseId,
'reading': rr, 'readingOrderNum': e.readings.indexWhere((r) => r.reading == rr) + 1,
}); });
} }
for (final ls in s.languageSource) { for (final ls in s.languageSource) {
@@ -229,9 +231,7 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
b.insert(JMdictTableNames.senseSeeAlso, { b.insert(JMdictTableNames.senseSeeAlso, {
'senseId': s.senseId, 'senseId': s.senseId,
'xrefEntryId': resolvedEntry.entry.entryId, 'xrefEntryId': resolvedEntry.entry.entryId,
'seeAlsoKanji': xref.kanjiRef, 'xrefSenseOrderNum': resolvedEntry.senseOrderNum,
'seeAlsoReading': xref.readingRef,
'seeAlsoSense': xref.senseOrderNum,
'ambiguous': resolvedEntry.ambiguous, 'ambiguous': resolvedEntry.ambiguous,
}); });
} }
@@ -256,9 +256,6 @@ Future<void> seedJMDictData(List<Entry> entries, Database db) async {
b.insert(JMdictTableNames.senseAntonyms, { b.insert(JMdictTableNames.senseAntonyms, {
'senseId': s.senseId, 'senseId': s.senseId,
'xrefEntryId': resolvedEntry.entry.entryId, 'xrefEntryId': resolvedEntry.entry.entryId,
'antonymKanji': ant.kanjiRef,
'antonymReading': ant.readingRef,
'antonymSense': ant.senseOrderNum,
'ambiguous': resolvedEntry.ambiguous, 'ambiguous': resolvedEntry.ambiguous,
}); });
} }

View File

@@ -27,6 +27,7 @@ Future<Database> openLocalDb({
await db.execute('PRAGMA journal_mode = WAL'); await db.execute('PRAGMA journal_mode = WAL');
} }
await db.execute('PRAGMA foreign_keys = ON'); await db.execute('PRAGMA foreign_keys = ON');
await db.execute("SELECT icu_load_collation('ja_JP', 'japanese')");
}, },
readOnly: !readWrite, readOnly: !readWrite,
), ),

View File

@@ -1,3 +1,5 @@
SELECT icu_load_collation('ja_JP', 'japanese');
CREATE TABLE "JMdict_Version" ( CREATE TABLE "JMdict_Version" (
"version" VARCHAR(10) PRIMARY KEY NOT NULL, "version" VARCHAR(10) PRIMARY KEY NOT NULL,
"date" DATE NOT NULL, "date" DATE NOT NULL,
@@ -55,13 +57,13 @@ CREATE TABLE "JMdict_KanjiElement" (
"elementId" INTEGER PRIMARY KEY, "elementId" INTEGER PRIMARY KEY,
"entryId" INTEGER NOT NULL REFERENCES "JMdict_Entry"("entryId"), "entryId" INTEGER NOT NULL REFERENCES "JMdict_Entry"("entryId"),
"orderNum" INTEGER NOT NULL, "orderNum" INTEGER NOT NULL,
"reading" TEXT NOT NULL, "reading" TEXT NOT NULL COLLATE japanese,
"news" INTEGER CHECK ("news" BETWEEN 1 AND 2), "news" INTEGER CHECK ("news" BETWEEN 1 AND 2),
"ichi" INTEGER CHECK ("ichi" BETWEEN 1 AND 2), "ichi" INTEGER CHECK ("ichi" BETWEEN 1 AND 2),
"spec" INTEGER CHECK ("spec" BETWEEN 1 AND 2), "spec" INTEGER CHECK ("spec" BETWEEN 1 AND 2),
"gai" INTEGER CHECK ("gai" BETWEEN 1 AND 2), "gai" INTEGER CHECK ("gai" BETWEEN 1 AND 2),
"nf" INTEGER CHECK ("nf" BETWEEN 1 AND 48), "nf" INTEGER CHECK ("nf" BETWEEN 1 AND 48),
UNIQUE("entryId", "reading"), -- UNIQUE("entryId", "reading"),
UNIQUE("entryId", "orderNum") UNIQUE("entryId", "orderNum")
) WITHOUT ROWID; ) WITHOUT ROWID;
@@ -80,14 +82,14 @@ CREATE TABLE "JMdict_ReadingElement" (
"elementId" INTEGER PRIMARY KEY, "elementId" INTEGER PRIMARY KEY,
"entryId" INTEGER NOT NULL REFERENCES "JMdict_Entry"("entryId"), "entryId" INTEGER NOT NULL REFERENCES "JMdict_Entry"("entryId"),
"orderNum" INTEGER NOT NULL, "orderNum" INTEGER NOT NULL,
"reading" TEXT NOT NULL, "reading" TEXT NOT NULL COLLATE japanese,
"readingDoesNotMatchKanji" BOOLEAN NOT NULL DEFAULT FALSE, "readingDoesNotMatchKanji" BOOLEAN NOT NULL DEFAULT FALSE,
"news" INTEGER CHECK ("news" BETWEEN 1 AND 2), "news" INTEGER CHECK ("news" BETWEEN 1 AND 2),
"ichi" INTEGER CHECK ("ichi" BETWEEN 1 AND 2), "ichi" INTEGER CHECK ("ichi" BETWEEN 1 AND 2),
"spec" INTEGER CHECK ("spec" BETWEEN 1 AND 2), "spec" INTEGER CHECK ("spec" BETWEEN 1 AND 2),
"gai" INTEGER CHECK ("gai" BETWEEN 1 AND 2), "gai" INTEGER CHECK ("gai" BETWEEN 1 AND 2),
"nf" INTEGER CHECK ("nf" BETWEEN 1 AND 48), "nf" INTEGER CHECK ("nf" BETWEEN 1 AND 48),
UNIQUE("entryId", "reading"), -- UNIQUE("entryId", "reading"),
UNIQUE("entryId", "orderNum") UNIQUE("entryId", "orderNum")
) WITHOUT ROWID; ) WITHOUT ROWID;
@@ -120,17 +122,17 @@ CREATE INDEX "JMdict_Sense_byEntryId_byOrderNum" ON "JMdict_Sense"("entryId", "o
CREATE TABLE "JMdict_SenseRestrictedToKanji" ( CREATE TABLE "JMdict_SenseRestrictedToKanji" (
"entryId" INTEGER NOT NULL, "entryId" INTEGER NOT NULL,
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"kanji" TEXT NOT NULL, "kanjiOrderNum" INTEGER NOT NULL CHECK ("kanjiOrderNum" > 0),
FOREIGN KEY ("entryId", "kanji") REFERENCES "JMdict_KanjiElement"("entryId", "reading"), FOREIGN KEY ("entryId", "kanjiOrderNum") REFERENCES "JMdict_KanjiElement"("entryId", "orderNum"),
PRIMARY KEY ("entryId", "senseId", "kanji") PRIMARY KEY ("entryId", "senseId", "kanjiOrderNum")
) WITHOUT ROWID; ) WITHOUT ROWID;
CREATE TABLE "JMdict_SenseRestrictedToReading" ( CREATE TABLE "JMdict_SenseRestrictedToReading" (
"entryId" INTEGER NOT NULL, "entryId" INTEGER NOT NULL,
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"reading" TEXT NOT NULL, "readingOrderNum" INTEGER NOT NULL CHECK ("readingOrderNum" > 0),
FOREIGN KEY ("entryId", "reading") REFERENCES "JMdict_ReadingElement"("entryId", "reading"), FOREIGN KEY ("entryId", "readingOrderNum") REFERENCES "JMdict_ReadingElement"("entryId", "orderNum"),
PRIMARY KEY ("entryId", "senseId", "reading") PRIMARY KEY ("entryId", "senseId", "readingOrderNum")
) WITHOUT ROWID; ) WITHOUT ROWID;
-- In order to add xrefs, you will need to have added the entry to xref to. -- In order to add xrefs, you will need to have added the entry to xref to.
@@ -145,32 +147,23 @@ CREATE TABLE "JMdict_SenseRestrictedToReading" (
CREATE TABLE "JMdict_SenseSeeAlso" ( CREATE TABLE "JMdict_SenseSeeAlso" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"xrefEntryId" INTEGER NOT NULL, "xrefEntryId" INTEGER NOT NULL REFERENCES "JMdict_Entry"("entryId"),
"seeAlsoReading" TEXT, -- Sometimes the cross reference is to a specific sense
"seeAlsoKanji" TEXT, "xrefSenseOrderNum" INTEGER,
"seeAlsoSense" INTEGER,
-- For some entries, the cross reference is ambiguous. This means that while the ingestion -- For some entries, the cross reference is ambiguous. This means that while the ingestion
-- has determined some xrefEntryId, it is not guaranteed to be the correct one. -- has determined some xrefEntryId, it is not guaranteed to be the correct one.
"ambiguous" BOOLEAN NOT NULL DEFAULT FALSE, "ambiguous" BOOLEAN NOT NULL DEFAULT FALSE,
FOREIGN KEY ("xrefEntryId", "seeAlsoKanji") REFERENCES "JMdict_KanjiElement"("entryId", "reading"), FOREIGN KEY ("xrefEntryId", "xrefSenseOrderNum") REFERENCES "JMdict_Sense"("entryId", "orderNum"),
FOREIGN KEY ("xrefEntryId", "seeAlsoReading") REFERENCES "JMdict_ReadingElement"("entryId", "reading"), UNIQUE("senseId", "xrefEntryId", "xrefSenseOrderNum")
FOREIGN KEY ("xrefEntryId", "seeAlsoSense") REFERENCES "JMdict_Sense"("entryId", "orderNum"),
UNIQUE("senseId", "xrefEntryId", "seeAlsoReading", "seeAlsoKanji", "seeAlsoSense")
); );
CREATE TABLE "JMdict_SenseAntonym" ( CREATE TABLE "JMdict_SenseAntonym" (
"senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"), "senseId" INTEGER NOT NULL REFERENCES "JMdict_Sense"("senseId"),
"xrefEntryId" INTEGER NOT NULL, "xrefEntryId" INTEGER NOT NULL REFERENCES "JMdict_Entry"("entryId"),
"antonymReading" TEXT,
"antonymKanji" TEXT,
"antonymSense" INTEGER,
-- For some entries, the cross reference is ambiguous. This means that while the ingestion -- For some entries, the cross reference is ambiguous. This means that while the ingestion
-- has determined some xrefEntryId, it is not guaranteed to be the correct one. -- has determined some xrefEntryId, it is not guaranteed to be the correct one.
"ambiguous" BOOLEAN NOT NULL DEFAULT FALSE, "ambiguous" BOOLEAN NOT NULL DEFAULT FALSE,
FOREIGN KEY ("xrefEntryId", "antonymKanji") REFERENCES "JMdict_KanjiElement"("entryId", "reading"), UNIQUE("senseId", "xrefEntryId")
FOREIGN KEY ("xrefEntryId", "antonymReading") REFERENCES "JMdict_ReadingElement"("entryId", "reading"),
FOREIGN KEY ("xrefEntryId", "antonymSense") REFERENCES "JMdict_Sense"("entryId", "orderNum"),
UNIQUE("senseId", "xrefEntryId", "antonymReading", "antonymKanji", "antonymSense")
); );
-- These cross references are going to be mostly accessed from a sense -- These cross references are going to be mostly accessed from a sense

View File

@@ -1,6 +1,7 @@
{ {
src, src,
buildDartApplication, buildDartApplication,
sqlite,
}: }:
buildDartApplication { buildDartApplication {
pname = "jadb-database-tool"; pname = "jadb-database-tool";
@@ -9,6 +10,9 @@ buildDartApplication {
dartEntryPoints."bin/jadb" = "bin/jadb.dart"; dartEntryPoints."bin/jadb" = "bin/jadb.dart";
# NOTE: here we are overriding the implicitly added runtimeDependency from the package fixup in pub2nix.
runtimeDependencies = [ sqlite ];
# NOTE: the default dart hooks are using `dart compile`, which is not able to call the # NOTE: the default dart hooks are using `dart compile`, which is not able to call the
# new dart build hooks required to use package:sqlite3 >= 3.0.0. So we override # new dart build hooks required to use package:sqlite3 >= 3.0.0. So we override
# these phases to use `dart build` instead. # these phases to use `dart build` instead.