cpp2sqlite is a C++ tool that generates SQLite databases containing Swiss Healthcare Public Domain Drug Information. It aggregates data from multiple Swiss health authority sources (Swissmedic, BAG, Refdata, etc.) into SQLite databases used by AmiKo and other applications.
cd scripts && source steps_public1.source && ./download.sh
source steps_public2.source && ./download.sh
cd ../build && cmake .. && make -j9Note: BOOST_BIND_GLOBAL_PLACEHOLDERS is defined in CMakeLists.txt to suppress Boost bind placeholder deprecation warnings (the project doesn't use boost::bind directly, but Boost headers trigger the warning internally).
cpp2sqlite- Main tool, generates amiko_db SQLite databasespharma- Generates pharma.csvsai- Processes SAI (Refdata structured article information)zurrose- Processes Zur Rose data
All use BAG FHIR ndjson by default since 01.06.2026 (flagFHIR=true); pass --no-fhir to fall back to the legacy BAG XML. The --fhir flag still works as an explicit opt-in.
- Source:
src/bagFHIR.cpp/src/bagFHIR.hpp - Input:
downloads/fhir-sl-{de,fr,it}.ndjsonfrom https://epl.bag.admin.ch/static/fhir/foph-sl-export-latest-{de,fr,it}.ndjson (per-language NDJSON) - Each line is a FHIR Bundle with entries: MedicinalProductDefinition, RegulatedAuthorization, PackagedProductDefinition, Ingredient, ClinicalUseDefinition
- Prices (EFP/PP) and reimbursement data are in RegulatedAuthorization extensions under
reimbursementSL>productPrice - Legal status codes map to categories: A (756005022001), B (756005022003), C (756005022005), D (756005022007, 756005022008), E (756005022009)
- When
--fhiris active, all price lookups (including from Refdata and Swissmedic) must useBAGFHIR::getPricesAndFlags()instead ofBAG::getPricesAndFlags(), sinceBAG::parseXML()is not called and its prepList is empty
- Mandatory on SL prescriptions and invoices from 2026-07-01 (BAG Rundschreiben 2026-02-19); insurers may reject non-compliant invoices from 2027-01-01
- Source:
src/bagFHIR.cppcollectBundleIndC()— bundle-scoped pass over each NDJSON line (one Bundle per line) - Preferred source (BAG SL FHIR export >= v2.0.5): the explicit
indicationCodesub-extension carried on each limitation (RegulatedAuthorization.indication[].extension[regulatedAuthorization-limitation].extension[indicationCode].valueString, e.g.20403.01). The BAG changelog states the limitation code (ClinicalUseDefinition.id) and the indication code are independent fields, soXXXXX.NNmust NOT be reconstructed from the CUD id suffix. The limitation text is resolved via the siblinglimitationIndicationreference (e.g.ClinicalUseDefinition/CYRAMZA.01) into that CUD's text. Verified on the live DE feed: 718 / 6'775 bundles carry the explicit field. - Fallback (older feeds without the extension):
XXXXXfromRegulatedAuthorization.extension[reimbursementSL].extension[FOPHDossierNumber].valueIdentifier.value;.NNis the trailing all-digit segment of eachClinicalUseDefinition.id(e.g.CYRAMZA.01); onlytype == "indication"CUDs are picked up. The feed shipstypeas a plain string andindicationas a single object — both shapes are handled. - CUD text comes from
indication.diseaseSymptomProcedure.concept.text, with fallbackindication.extension[url == "limitationText"].valueString - Stored on
BAG::Preparation.indicationCodes(and copied onto everyPack) asIndicationCode {code, cudId, text} BAGFHIR::getIndCByRegnr(regnr, &codes, &text)returns the joined columns the schema expects: comma-joined codes + newline-joinedcode: textlines, deduped by code, in bundle order. Mirrors rust2xml ≥ 3.1.12 (INDIKATIONSCODE/INDIKATIONSCODE_TEXTXML elements). The function walks the entireprepListand merges IndicationCodes from every prep that shares the queried Swissmedic regnr, deduped by code — required because the same 5-digit Swissmedic regnr typically spans multiple FHIR bundles (only one of which carries the type=indication ClinicalUseDefinitions). An earlier early-return on the first matching prep with empty indicationCodes silently dropped IndC for ~80% of regnrs.- Schema/binding:
Sql::useIndCis set fromflagFHIRinopenDB(). When true, theamikodbschema gains two trailing TEXT columns (indikationscode,indikationscode_text) andinsertRowbinds positions 19/20. Non-FHIR builds emit the legacy 19-column schema, so apps reading by column index keep working. - iOS consumer:
generikacc/Generika/IndCSection.swift(issue zdavatz/generikacc#102) — guarded byAmikoDBManager isIndcColumnAvailableso older DB snapshots don't break the app
- Feed:
artikel_vollstamm_zurrose.csvandartikel_stamm_zurrose.csvare downloaded viascripts/download_zr.sh(SFTP from ftp.zur-rose.ch). - Schema change (2026-05): a trailing column
Exfact(V, index 21) was appended, growing the row from 21 to 22 semicolon-separated fields. All four column-count guards must match the new width —src/zur/stamm.cpp:69,src/zur/stamm.cpp:129,src/zur/voll.cpp:152,src/zur/voll.cpp:355. Note thatvoll.cpp::parseCSV(line 152) silentlycontinues on mismatch instead of exiting, so a missed bump there empties the output DB without an error message. Exfactis the Zur Rose ex-factory price and is wired intorosedb.exfpriceas a fallback for the BAG EFP involl.cpp::parseCSV(~line 304). BAG values stay canonical for SL-listed drugs; Zur Rose Exfact fills the long tail. In--fhirbuilds without BAG XML this liftsrosedb.exfpricepopulation from 0/163858 to 163858/163858.
VOLL::closeDB() calls sqlite3_finalize(statement) then sqlite3_close(db), so it must be called exactly once. Calling it twice double-frees both handles and corrupts the glibc heap — the failure surfaces as free(): invalid next size (fast) on process exit and, depending on arena state, may not reproduce on smaller workloads (atcdb hid it while fulldb crashed every run). src/zur/main.cpp previously had a stray second closeDB block right after the fulldb/atcdb branch; the single call inside the branch is the correct lifecycle.
src/c2s/ean13 is a git submodule pointing at ywesee/BarcodeGenerator (separate org from zdavatz/cpp2sqlite). Fixes there require: commit + push inside the submodule on master, then bump the parent's submodule pointer in a parent commit. Consumers (and the build server) must run git submodule update after pulling the parent.
GCC 13+ (and current libstdc++) no longer pulls many standard headers transitively. New code that uses standard types/streams must #include them explicitly or the Funtoo build server breaks:
<cstdint>foruint8_t/int32_t/ etc. (trippedsrc/c2s/ean13/functii.cpp— fixed in ywesee/BarcodeGenerator c1f17da)<fstream>forstd::ofstream/std::ifstream(trippedsrc/sap/main.cpp,src/dru/main.cpp— fixed in 0203368, plusbagFHIR.cpp,c2s/peddose.cpp,c2s/sappinfo.cpp,c2s/refdata.cppcleaned up preemptively)
If a new source file uses these (or <sstream>, <iomanip>, etc.) without an explicit include, it will compile locally on older toolchains and break on the build server.
src/- C++ source filessrc/c2s/- cpp2sqlite specific sourcessrc/pha/- pharma specific sourcessrc/sai/- SAI specific sourcessrc/zur/- zurrose specific sourcesscripts/- Build and download scriptsinput/- Input data files (not in git)downloads/- Downloaded source files (not in git)output/- Generated databases and reports (not in git)