From 9f409645e3795891d55bfe4f48ab8293346885de Mon Sep 17 00:00:00 2001 From: fy59 Date: Thu, 27 Aug 2026 07:55:33 +0200 Subject: [PATCH] feat: add capture and asset provenance v1 --- README.md | 5 +- docs/architecture/project_database.md | 33 +- docs/concepts/scan_sets.md | 15 +- docs/roadmap/roadmap.md | 4 + include/lardon3d/project_db.h | 65 +++- src/project_db.c | 506 +++++++++++++++++++++++++- tests/test_geometric_verification.c | 13 +- tests/test_geometric_verifier_task.c | 7 +- tests/test_match_result.c | 15 +- tests/test_matcher_task.c | 7 +- tests/test_project_db.c | 205 +++++++++-- tests/test_sparse_sfm_model.c | 2 +- tests/test_sparse_sfm_payload.cpp | 2 +- 13 files changed, 831 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 4dcede2..277b696 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,14 @@ persistante, enrichissable et versionnable. - **Project** : cycle de vie persistant, identité stable et Project Database ouverte - **Import** : premier task kind de production, exécuté par la file générique en lots bornés et reprenables - **ScanSet / Image Catalog v1** : acquisitions, images logiques, provenance et assets SHA-256 persistants et paginés +- **Capture / Asset Provenance v1** : Captures par ScanSet, associations source/dérivé + et sélection explicite d'une image logique — IMPLEMENTED / VALIDATION PENDING - **Feature Store v1/v2** : ORB U8×32, SIFT/RootSIFT F32×128 et lecture typée bornée - **Image View** : vues triées et filtrées pour la TUI - **Task** : moteur de tâches avec pause/reprise, annulation et séquences - **Task Checkpoint v1** : snapshot durable, fichier atomique et reprise sûre -- **Project Database v18** : résultats v16 et payload durable typé Sparse SfM Gate F +- **Project Database v19** : fondations v18 préservées, avec Capture / Asset + Provenance additive - **Sparse SfM Gates C/D/E** : géométrie calibrée, noyau incrémental et Bundle Adjustment final par composante, tous PASS / FROZEN - **Sparse SfM Gate F** : orchestration durable, runtime gouverné et publication diff --git a/docs/architecture/project_database.md b/docs/architecture/project_database.md index 6ed00ef..0c1deca 100644 --- a/docs/architecture/project_database.md +++ b/docs/architecture/project_database.md @@ -1,5 +1,24 @@ # Base de données projet Lardon3D +## Capture / Asset Provenance v1 — Project DB v19 + +**IMPLEMENTED / VALIDATION PENDING.** La migration transactionnelle v18→v19 +ajoute seulement une fondation de catalogue : `captures`, `capture_images`, +`capture_assets`, `capture_selections` et `asset_derivations`. Un `Capture` +appartient à un seul ScanSet ; il n'est ni un `image_id` ni une nouvelle identité +scientifique. Les assets source et dérivés peuvent lui être associés sans créer +d'image logique. Une sélection courante optionnelle référence exactement une +image déjà associée au Capture et ne modifie jamais cette image, son asset, ni +les résultats scientifiques existants. + +`asset_derivations` est volontairement limité à un parent asset et un enfant +asset, avec kind/version et fingerprint canonique de 32 octets. Il n'est pas un +DAG générique et ne réalise aucun développement RAW ni extraction vidéo. La +migration crée, par `image_id` croissant, un Capture legacy indépendant pour +chaque image v18, relie son asset comme source et rend cette même image +sélectionnée. Aucun nom de fichier, EXIF ou rapprochement de siblings n'est +interprété ; les IDs et la sémantique v18 restent inchangés. + ## Phase H — décision Project DB v18 **PASS / FROZEN.** La migration transactionnelle v17→v18 @@ -65,7 +84,9 @@ persistée exactement ; seule une composante de graphe non reconstruite est omise. Les comptes et métriques globaux décrivent exclusivement la géométrie effectivement persistée. Aucun placeholder ni table diagnostique n'est ajouté. -> Version courante : **v18**. La migration transactionnelle v17→v18 ajoute le +> Version courante : **v19**. La migration transactionnelle v18→v19 ajoute la +> fondation Capture/Asset Provenance décrite ci-dessus sans modifier les tables +> scientifiques historiques. La migration transactionnelle v17→v18 ajoute le > discriminateur générique nullable et les deux tables H minimales décrites > ci-dessus. La migration v16→v17 ajoute le > payload durable typé `sparse_sfm_tasks`. La migration transactionnelle v15→v16 ajoute @@ -808,8 +829,8 @@ ouvert. ## Statut -**IMPLEMENTED** — SQLite système, schéma v18 et migrations séquentielles -v1→v2→v3→v4→v5→v6→v7→v8→v9→v10→v11→v12→v13→v14→v15→v16→v17→v18, identité projet, transactions +**IMPLEMENTED / VALIDATION PENDING** — SQLite système, schéma v19 et migrations séquentielles +v1→v2→v3→v4→v5→v6→v7→v8→v9→v10→v11→v12→v13→v14→v15→v16→v17→v18→v19, identité projet, transactions tâche+checkpoint, pagination de reprise et artefacts génériques. **IMPLEMENTED** — ouverture/fermeture avec le projet, identité INI/DB cohérente, @@ -829,6 +850,12 @@ bornée à 256. Les identités sont des `INTEGER PRIMARY KEY AUTOINCREMENT` SQLi allouées sous transaction ; aucun `SELECT MAX()+1` n'est utilisé en fonctionnement normal et une identité validée n'est jamais réutilisée. +**IMPLEMENTED / VALIDATION PENDING** — Capture / Asset Provenance v1 : +Capture borné par ScanSet, associations explicites asset/image, sélection +courante optionnelle et dérivation asset à parent unique. RAW, vidéo, +appariement automatique et toute exécution de dérivation restent hors de cette +fondation. + La migration v3→v4 ne lit pas `manifest.tsv`. Elle crée le ScanSet legacy et positionne `metadata.legacy_image_catalog_pending=1` dès qu'une ancienne tâche d'import existe. Cet indicateur signifie « données legacy potentiellement non diff --git a/docs/concepts/scan_sets.md b/docs/concepts/scan_sets.md index e916bc4..0e50c45 100644 --- a/docs/concepts/scan_sets.md +++ b/docs/concepts/scan_sets.md @@ -13,8 +13,10 @@ l'import. ```text Project └── ScanSet (acquisition logique) - └── Image (observation logique) - └── Image Asset (contenu physique géré) + └── Capture (observation physique potentielle) + ├── Image Assets source/dérivés (contenus physiques gérés) + ├── Images (représentations logiques) + └── zéro ou une Image sélectionnée ``` - `scanset_id` identifie durablement une acquisition, indépendamment de son @@ -25,6 +27,15 @@ Project chemin relatif décrivent le fichier géré par Lardon3D. - La provenance d'une image conserve son nom original, son chemin source, son instant d'import et, s'il existe, le `task_id` importeur. +- Un `Capture` est une association de catalogue dans un unique ScanSet ; il ne + remplace pas `image_id`. Il peut conserver par exemple les assets RAW et JPEG + d'un même déclenchement, sans inférer ce lien à partir de leurs noms. +- Seule une image logique explicitement sélectionnée est la représentation + courante proposée au pipeline scientifique ultérieur. La sélection ne modifie + ni l'image, ni son asset, ni les résultats existants. +- Une dérivation asset bornée parent→enfant, versionnée par fingerprint, rend + possible une future image de travail RAW ou frame vidéo sans prétendre que + RAW ou vidéo sont déjà importés. Un contenu identique n'implique pas une identité logique unique. Dans un même ScanSet, le couple `(scanset_id, asset_id)` est unique : réimporter le même diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index 2296c64..b73af8a 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -58,6 +58,10 @@ Lardon3D suit une feuille de route ordonnée qui privilégie la stabilité et la ### Phase 5 : Reconstruction - ✅ Orchestration de reconstruction incrémentale H v1 — **PASS / FROZEN** +- 🔄 Capture / Asset Provenance v1 — **IMPLEMENTED / VALIDATION PENDING** : + Project DB v19 ajoute les Captures par ScanSet, les associations source/dérivé + et la sélection explicite d'une image logique, sans changer le pipeline + scientifique. L'ingestion RAW et vidéo reste PLANNED. - ✅ MVS-M1 — **PASS / FROZEN** : identité dense `L3DMDID2` v2 (220 octets) liant reconstruction de base, jeu d'images source, `calibration_scope_identity` historique, binding numérique exact `L3DMCAL1` v1, diff --git a/include/lardon3d/project_db.h b/include/lardon3d/project_db.h index 0625416..4ad871f 100644 --- a/include/lardon3d/project_db.h +++ b/include/lardon3d/project_db.h @@ -10,7 +10,7 @@ #include enum { - LARDON3D_PROJECT_DB_SCHEMA_VERSION = 18, + LARDON3D_PROJECT_DB_SCHEMA_VERSION = 19, LARDON3D_PROJECT_DB_ID_CAPACITY = 65, LARDON3D_PROJECT_DB_KIND_CAPACITY = 65, LARDON3D_PROJECT_DB_PATH_CAPACITY = 4096, @@ -129,6 +129,38 @@ typedef struct { int64_t imported_at; } Lardon3DProjectDbImage; +typedef struct { + uint64_t capture_id; + uint64_t scanset_id; + int64_t created_at; +} Lardon3DProjectDbCapture; + +typedef enum { + LARDON3D_DB_CAPTURE_ASSET_SOURCE = 1, + LARDON3D_DB_CAPTURE_ASSET_DERIVED = 2, +} Lardon3DProjectDbCaptureAssetRole; + +typedef struct { + uint64_t capture_id; + uint64_t asset_id; + Lardon3DProjectDbCaptureAssetRole role; +} Lardon3DProjectDbCaptureAsset; + +typedef enum { + LARDON3D_DB_ASSET_DERIVATION_GENERIC_VERSIONED = 1, +} Lardon3DProjectDbAssetDerivationKind; + +typedef struct { + uint64_t parent_asset_id; + uint64_t child_asset_id; + Lardon3DProjectDbAssetDerivationKind kind; + uint32_t version; + unsigned char parameter_fingerprint[LARDON3D_PROJECT_DB_SHA256_SIZE]; + bool has_producer_task; + uint64_t producer_task_id; + int64_t created_at; +} Lardon3DProjectDbAssetDerivation; + typedef struct { uint64_t candidate_pair_id; uint64_t image_id_a; @@ -469,6 +501,10 @@ Lardon3DProjectDbResult lardon3d_project_db_register_image( uint64_t size_bytes, const char *original_name, const char *source_path, uint64_t producer_task_id, int64_t imported_at, Lardon3DProjectDbImageRegisterStatus *status, Lardon3DProjectDbImage *image); +Lardon3DProjectDbResult lardon3d_project_db_register_image_asset( + Lardon3DProjectDb *database, + const unsigned char sha256[LARDON3D_PROJECT_DB_SHA256_SIZE], const char *asset_path, + uint64_t size_bytes, int64_t created_at, Lardon3DProjectDbImageAsset *asset); Lardon3DProjectDbResult lardon3d_project_db_load_image(Lardon3DProjectDb *database, uint64_t image_id, Lardon3DProjectDbImage *image, @@ -481,6 +517,33 @@ Lardon3DProjectDbResult lardon3d_project_db_list_images(Lardon3DProjectDb *datab size_t capacity, size_t *count); Lardon3DProjectDbResult lardon3d_project_db_count_images(Lardon3DProjectDb *database, uint64_t scanset_id, uint64_t *count); +Lardon3DProjectDbResult lardon3d_project_db_create_capture( + Lardon3DProjectDb *database, uint64_t scanset_id, int64_t created_at, + Lardon3DProjectDbCapture *capture); +Lardon3DProjectDbResult lardon3d_project_db_load_capture( + Lardon3DProjectDb *database, uint64_t capture_id, Lardon3DProjectDbCapture *capture); +Lardon3DProjectDbResult lardon3d_project_db_list_captures( + Lardon3DProjectDb *database, uint64_t scanset_id, uint64_t after_capture_id, + Lardon3DProjectDbCapture *captures, size_t capacity, size_t *count); +Lardon3DProjectDbResult lardon3d_project_db_attach_capture_asset( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t asset_id, + Lardon3DProjectDbCaptureAssetRole role); +Lardon3DProjectDbResult lardon3d_project_db_list_capture_assets( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t after_asset_id, + Lardon3DProjectDbCaptureAsset *assets, size_t capacity, size_t *count); +Lardon3DProjectDbResult lardon3d_project_db_attach_capture_image( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t image_id); +Lardon3DProjectDbResult lardon3d_project_db_find_capture_for_image( + Lardon3DProjectDb *database, uint64_t image_id, Lardon3DProjectDbCapture *capture); +Lardon3DProjectDbResult lardon3d_project_db_record_asset_derivation( + Lardon3DProjectDb *database, const Lardon3DProjectDbAssetDerivation *derivation); +Lardon3DProjectDbResult lardon3d_project_db_load_asset_derivation( + Lardon3DProjectDb *database, uint64_t child_asset_id, + Lardon3DProjectDbAssetDerivation *derivation); +Lardon3DProjectDbResult lardon3d_project_db_set_selected_capture_image( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t image_id); +Lardon3DProjectDbResult lardon3d_project_db_get_selected_capture_image( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t *image_id); Lardon3DProjectDbResult lardon3d_project_db_create_candidate_pair( Lardon3DProjectDb *database, uint64_t image_id_a, uint64_t image_id_b, int64_t created_at, Lardon3DProjectDbCandidatePair *pair); diff --git a/src/project_db.c b/src/project_db.c index b9c762d..cddd32e 100644 --- a/src/project_db.c +++ b/src/project_db.c @@ -577,6 +577,36 @@ static const char schema_incremental_reconstruction_v18[] = "incremental_version INTEGER NOT NULL CHECK(incremental_version=1)," "parameter_fingerprint BLOB NOT NULL CHECK(length(parameter_fingerprint)=32));"; +static const char schema_capture_provenance_v19[] = + "CREATE TABLE captures(" + "capture_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(capture_id>0)," + "scanset_id INTEGER NOT NULL REFERENCES scansets(scanset_id)," + "created_at INTEGER NOT NULL CHECK(created_at>=0));" + "CREATE INDEX captures_scanset_idx ON captures(scanset_id,capture_id);" + "CREATE TABLE capture_images(" + "capture_id INTEGER NOT NULL REFERENCES captures(capture_id) ON DELETE CASCADE," + "image_id INTEGER NOT NULL UNIQUE REFERENCES images(image_id)," + "PRIMARY KEY(capture_id,image_id));" + "CREATE TABLE capture_assets(" + "capture_id INTEGER NOT NULL REFERENCES captures(capture_id) ON DELETE CASCADE," + "asset_id INTEGER NOT NULL REFERENCES image_assets(asset_id)," + "role INTEGER NOT NULL CHECK(role IN(1,2))," + "PRIMARY KEY(capture_id,asset_id));" + "CREATE INDEX capture_assets_page_idx ON capture_assets(capture_id,asset_id);" + "CREATE TABLE capture_selections(" + "capture_id INTEGER PRIMARY KEY REFERENCES captures(capture_id) ON DELETE CASCADE," + "image_id INTEGER NOT NULL UNIQUE REFERENCES images(image_id)," + "FOREIGN KEY(capture_id,image_id) REFERENCES capture_images(capture_id,image_id));" + "CREATE TABLE asset_derivations(" + "child_asset_id INTEGER PRIMARY KEY REFERENCES image_assets(asset_id)," + "parent_asset_id INTEGER NOT NULL REFERENCES image_assets(asset_id)," + "derivation_kind INTEGER NOT NULL CHECK(derivation_kind=1)," + "derivation_version INTEGER NOT NULL CHECK(derivation_version>0)," + "parameter_fingerprint BLOB NOT NULL CHECK(length(parameter_fingerprint)=32)," + "producer_task_id INTEGER REFERENCES tasks(task_id)," + "created_at INTEGER NOT NULL CHECK(created_at>=0)," + "CHECK(parent_asset_id!=child_asset_id));"; + static void copy_error(char destination[LARDON3D_PROJECT_DB_ERROR_CAPACITY], const char *text) { if (destination) { (void)snprintf(destination, LARDON3D_PROJECT_DB_ERROR_CAPACITY, "%s", text ? text : ""); @@ -668,7 +698,7 @@ static Lardon3DProjectDbResult migrate(Lardon3DProjectDb *database, unsigned int from_version != 4 && from_version != 5 && from_version != 6 && from_version != 7 && from_version != 8 && from_version != 9 && from_version != 10 && from_version != 11 && from_version != 12 && from_version != 13 && from_version != 14 && - from_version != 15 && from_version != 16 && from_version != 17) { + from_version != 15 && from_version != 16 && from_version != 17 && from_version != 18) { return LARDON3D_PROJECT_DB_CORRUPT; } Lardon3DProjectDbResult result = execute(database, "BEGIN IMMEDIATE", "begin migration"); @@ -1033,6 +1063,36 @@ static Lardon3DProjectDbResult migrate(Lardon3DProjectDb *database, unsigned int "finish schema v18 migration"); } } + if (result == LARDON3D_PROJECT_DB_OK && from_version < 19) { + result = execute(database, schema_capture_provenance_v19, + "migrate image catalog capture provenance v18 to v19"); + if (result == LARDON3D_PROJECT_DB_OK) { + result = execute( + database, + "INSERT INTO captures(capture_id,scanset_id,created_at) " + "SELECT image_id,scanset_id,imported_at FROM images ORDER BY image_id;" + "INSERT INTO capture_images(capture_id,image_id) " + "SELECT image_id,image_id FROM images ORDER BY image_id;" + "INSERT INTO capture_assets(capture_id,asset_id,role) " + "SELECT image_id,asset_id,1 FROM images ORDER BY image_id;" + "INSERT INTO capture_selections(capture_id,image_id) " + "SELECT image_id,image_id FROM images ORDER BY image_id;", + "migrate legacy images to independent captures"); + } +#ifdef LARDON3D_PROJECT_DB_TESTING + if (result == LARDON3D_PROJECT_DB_OK && + getenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V19")) { + result = execute(database, "INSERT INTO missing_v19_test_table VALUES(1)", + "forced migration v19 failure"); + } +#endif + if (result == LARDON3D_PROJECT_DB_OK) { + result = execute( + database, + "UPDATE metadata SET value=19 WHERE key='schema_version' AND value=18", + "finish schema v19 migration"); + } + } if (result == LARDON3D_PROJECT_DB_OK) { result = execute(database, "COMMIT", "commit migration"); } @@ -1163,7 +1223,12 @@ Lardon3DProjectDbResult lardon3d_project_db_open(const char *path, Lardon3DProje "sparse_landmarks", "sparse_landmark_observations", "incremental_reconstructions", - "incremental_reconstruction_tasks"}; + "incremental_reconstruction_tasks", + "captures", + "capture_images", + "capture_assets", + "capture_selections", + "asset_derivations"}; for (size_t index = 0; index < sizeof(required) / sizeof(required[0]) && result == LARDON3D_PROJECT_DB_OK; ++index) { @@ -2445,6 +2510,77 @@ static const char image_select[] = "a.state,a.created_at,i.producer_task_id,i.imported_at FROM images i JOIN image_assets a ON " "a.asset_id=i.asset_id "; +Lardon3DProjectDbResult lardon3d_project_db_register_image_asset( + Lardon3DProjectDb *database, + const unsigned char sha256[LARDON3D_PROJECT_DB_SHA256_SIZE], const char *asset_path, + uint64_t size_bytes, int64_t created_at, Lardon3DProjectDbImageAsset *asset) { + if (!database || !sha256 || !valid_relative_asset_path(asset_path) || + !canonical_asset_path(sha256, asset_path) || size_bytes > INT64_MAX || created_at < 0 || + !asset) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + memset(asset, 0, sizeof(*asset)); + (void)pthread_mutex_lock(&database->mutex); + Lardon3DProjectDbResult result = execute(database, "BEGIN IMMEDIATE", "begin image asset register"); + sqlite3_stmt *statement = NULL; + if (result == LARDON3D_PROJECT_DB_OK) { + result = prepare(database, "INSERT INTO image_assets(sha256,path,size_bytes,state,created_at) " + "VALUES(?1,?2,?3,1,?4) ON CONFLICT(sha256) DO NOTHING", + &statement); + } + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_blob(statement, 1, sha256, LARDON3D_PROJECT_DB_SHA256_SIZE, + SQLITE_TRANSIENT); + (void)sqlite3_bind_text(statement, 2, asset_path, -1, SQLITE_TRANSIENT); + (void)sqlite3_bind_int64(statement, 3, (sqlite3_int64)size_bytes); + (void)sqlite3_bind_int64(statement, 4, created_at); + result = step_done(database, statement, "insert image asset"); + statement = NULL; + } + if (result == LARDON3D_PROJECT_DB_OK) { + result = prepare(database, "SELECT asset_id,sha256,path,size_bytes,state,created_at FROM " + "image_assets WHERE sha256=?1", &statement); + } + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_blob(statement, 1, sha256, LARDON3D_PROJECT_DB_SHA256_SIZE, + SQLITE_TRANSIENT); + int code = sqlite3_step(statement); + if (code == SQLITE_DONE) { + result = LARDON3D_PROJECT_DB_CORRUPT; + } else if (code != SQLITE_ROW) { + result = sqlite_result(database, code, "load registered image asset"); + } else { + sqlite3_int64 id = sqlite3_column_int64(statement, 0); + sqlite3_int64 stored_size = sqlite3_column_int64(statement, 3); + sqlite3_int64 stored_created = sqlite3_column_int64(statement, 5); + const void *stored_hash = sqlite3_column_blob(statement, 1); + if (id <= 0 || stored_size < 0 || stored_created < 0 || + sqlite3_column_bytes(statement, 1) != LARDON3D_PROJECT_DB_SHA256_SIZE || !stored_hash || + !copy_column(statement, 2, asset->path, sizeof(asset->path)) || + strcmp(asset->path, asset_path) != 0 || stored_size != (sqlite3_int64)size_bytes || + sqlite3_column_int(statement, 4) != LARDON3D_DB_IMAGE_ASSET_READY) { + result = LARDON3D_PROJECT_DB_CORRUPT; + } else { + asset->asset_id = (uint64_t)id; + memcpy(asset->sha256, stored_hash, LARDON3D_PROJECT_DB_SHA256_SIZE); + asset->size_bytes = (uint64_t)stored_size; + asset->state = LARDON3D_DB_IMAGE_ASSET_READY; + asset->created_at = stored_created; + } + } + (void)sqlite3_finalize(statement); + statement = NULL; + } + if (result == LARDON3D_PROJECT_DB_OK) { + result = execute(database, "COMMIT", "commit image asset register"); + } + if (result != LARDON3D_PROJECT_DB_OK) { + (void)execute(database, "ROLLBACK", "rollback image asset register"); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + Lardon3DProjectDbResult lardon3d_project_db_register_image( Lardon3DProjectDb *database, uint64_t scanset_id, const unsigned char sha256[LARDON3D_PROJECT_DB_SHA256_SIZE], const char *asset_path, @@ -2670,6 +2806,372 @@ Lardon3DProjectDbResult lardon3d_project_db_count_images(Lardon3DProjectDb *data return result; } +static bool valid_capture_asset_role(Lardon3DProjectDbCaptureAssetRole role) { + return role == LARDON3D_DB_CAPTURE_ASSET_SOURCE || role == LARDON3D_DB_CAPTURE_ASSET_DERIVED; +} + +static bool valid_asset_derivation_kind(Lardon3DProjectDbAssetDerivationKind kind) { + return kind == LARDON3D_DB_ASSET_DERIVATION_GENERIC_VERSIONED; +} + +static bool read_capture(sqlite3_stmt *statement, Lardon3DProjectDbCapture *capture) { + sqlite3_int64 capture_id = sqlite3_column_int64(statement, 0); + sqlite3_int64 scanset_id = sqlite3_column_int64(statement, 1); + sqlite3_int64 created_at = sqlite3_column_int64(statement, 2); + if (capture_id <= 0 || scanset_id <= 0 || created_at < 0) { + return false; + } + capture->capture_id = (uint64_t)capture_id; + capture->scanset_id = (uint64_t)scanset_id; + capture->created_at = created_at; + return true; +} + +Lardon3DProjectDbResult lardon3d_project_db_create_capture( + Lardon3DProjectDb *database, uint64_t scanset_id, int64_t created_at, + Lardon3DProjectDbCapture *capture) { + if (!database || !valid_catalog_id(scanset_id) || created_at < 0 || !capture) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + memset(capture, 0, sizeof(*capture)); + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "INSERT INTO captures(scanset_id,created_at) VALUES(?1,?2)", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)scanset_id); + (void)sqlite3_bind_int64(statement, 2, created_at); + result = step_done(database, statement, "create capture"); + } + sqlite3_int64 capture_id = sqlite3_last_insert_rowid(database->connection); + if (result == LARDON3D_PROJECT_DB_OK && capture_id <= 0) { + result = LARDON3D_PROJECT_DB_CONSTRAINT; + } + if (result == LARDON3D_PROJECT_DB_OK) { + capture->capture_id = (uint64_t)capture_id; + capture->scanset_id = scanset_id; + capture->created_at = created_at; + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_load_capture( + Lardon3DProjectDb *database, uint64_t capture_id, Lardon3DProjectDbCapture *capture) { + if (!database || !valid_catalog_id(capture_id) || !capture) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + memset(capture, 0, sizeof(*capture)); + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "SELECT capture_id,scanset_id,created_at FROM captures WHERE capture_id=?1", + &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)capture_id); + int code = sqlite3_step(statement); + if (code == SQLITE_DONE) { + result = LARDON3D_PROJECT_DB_NOT_FOUND; + } else if (code != SQLITE_ROW || !read_capture(statement, capture)) { + result = LARDON3D_PROJECT_DB_CORRUPT; + } + (void)sqlite3_finalize(statement); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_list_captures( + Lardon3DProjectDb *database, uint64_t scanset_id, uint64_t after_capture_id, + Lardon3DProjectDbCapture *captures, size_t capacity, size_t *count) { + if (count) { + *count = 0; + } + if (!database || !valid_catalog_id(scanset_id) || after_capture_id > INT64_MAX || !captures || + !count || capacity == 0 || capacity > LARDON3D_PROJECT_DB_CATALOG_PAGE_MAX) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "SELECT capture_id,scanset_id,created_at FROM captures WHERE scanset_id=?1 " + "AND capture_id>?2 ORDER BY capture_id LIMIT ?3", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)scanset_id); + (void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)after_capture_id); + (void)sqlite3_bind_int64(statement, 3, (sqlite3_int64)capacity); + int code = SQLITE_DONE; + while (*count < capacity && (code = sqlite3_step(statement)) == SQLITE_ROW) { + if (!read_capture(statement, &captures[*count])) { + result = LARDON3D_PROJECT_DB_CORRUPT; + break; + } + ++*count; + } + if (result == LARDON3D_PROJECT_DB_OK && *count < capacity && code != SQLITE_DONE) { + result = sqlite_result(database, code, "list captures"); + } + (void)sqlite3_finalize(statement); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_attach_capture_asset( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t asset_id, + Lardon3DProjectDbCaptureAssetRole role) { + if (!database || !valid_catalog_id(capture_id) || !valid_catalog_id(asset_id) || + !valid_capture_asset_role(role)) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "INSERT INTO capture_assets(capture_id,asset_id,role) VALUES(?1,?2,?3)", + &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)capture_id); + (void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)asset_id); + (void)sqlite3_bind_int(statement, 3, (int)role); + result = step_done(database, statement, "attach capture asset"); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_list_capture_assets( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t after_asset_id, + Lardon3DProjectDbCaptureAsset *assets, size_t capacity, size_t *count) { + if (count) { + *count = 0; + } + if (!database || !valid_catalog_id(capture_id) || after_asset_id > INT64_MAX || !assets || + !count || capacity == 0 || capacity > LARDON3D_PROJECT_DB_CATALOG_PAGE_MAX) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "SELECT capture_id,asset_id,role FROM capture_assets WHERE capture_id=?1 " + "AND asset_id>?2 ORDER BY asset_id LIMIT ?3", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)capture_id); + (void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)after_asset_id); + (void)sqlite3_bind_int64(statement, 3, (sqlite3_int64)capacity); + int code = SQLITE_DONE; + while (*count < capacity && (code = sqlite3_step(statement)) == SQLITE_ROW) { + sqlite3_int64 stored_capture = sqlite3_column_int64(statement, 0); + sqlite3_int64 stored_asset = sqlite3_column_int64(statement, 1); + int stored_role = sqlite3_column_int(statement, 2); + if (stored_capture <= 0 || stored_asset <= 0 || + !valid_capture_asset_role((Lardon3DProjectDbCaptureAssetRole)stored_role)) { + result = LARDON3D_PROJECT_DB_CORRUPT; + break; + } + assets[*count] = (Lardon3DProjectDbCaptureAsset){ + .capture_id = (uint64_t)stored_capture, + .asset_id = (uint64_t)stored_asset, + .role = (Lardon3DProjectDbCaptureAssetRole)stored_role}; + ++*count; + } + if (result == LARDON3D_PROJECT_DB_OK && *count < capacity && code != SQLITE_DONE) { + result = sqlite_result(database, code, "list capture assets"); + } + (void)sqlite3_finalize(statement); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_attach_capture_image( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t image_id) { + if (!database || !valid_catalog_id(capture_id) || !valid_catalog_id(image_id)) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "INSERT INTO capture_images(capture_id,image_id) " + "SELECT ?1,?2 WHERE EXISTS(SELECT 1 FROM captures c JOIN images i " + "ON i.scanset_id=c.scanset_id WHERE c.capture_id=?1 AND i.image_id=?2)", + &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)capture_id); + (void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)image_id); + result = step_done(database, statement, "attach capture image"); + if (result == LARDON3D_PROJECT_DB_OK && sqlite3_changes(database->connection) != 1) { + result = LARDON3D_PROJECT_DB_NOT_FOUND; + } + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_find_capture_for_image( + Lardon3DProjectDb *database, uint64_t image_id, Lardon3DProjectDbCapture *capture) { + if (!database || !valid_catalog_id(image_id) || !capture) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + memset(capture, 0, sizeof(*capture)); + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "SELECT c.capture_id,c.scanset_id,c.created_at FROM captures c JOIN " + "capture_images ci ON ci.capture_id=c.capture_id WHERE ci.image_id=?1", + &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)image_id); + int code = sqlite3_step(statement); + if (code == SQLITE_DONE) { + result = LARDON3D_PROJECT_DB_NOT_FOUND; + } else if (code != SQLITE_ROW || !read_capture(statement, capture)) { + result = LARDON3D_PROJECT_DB_CORRUPT; + } + (void)sqlite3_finalize(statement); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_record_asset_derivation( + Lardon3DProjectDb *database, const Lardon3DProjectDbAssetDerivation *derivation) { + if (!database || !derivation || !valid_catalog_id(derivation->parent_asset_id) || + !valid_catalog_id(derivation->child_asset_id) || + derivation->parent_asset_id == derivation->child_asset_id || + !valid_asset_derivation_kind(derivation->kind) || derivation->version == 0 || + derivation->created_at < 0 || + (derivation->has_producer_task && !valid_task_id(derivation->producer_task_id))) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "INSERT INTO asset_derivations(child_asset_id,parent_asset_id,derivation_kind," + "derivation_version,parameter_fingerprint,producer_task_id,created_at) " + "VALUES(?1,?2,?3,?4,?5,?6,?7)", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)derivation->child_asset_id); + (void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)derivation->parent_asset_id); + (void)sqlite3_bind_int(statement, 3, (int)derivation->kind); + (void)sqlite3_bind_int64(statement, 4, (sqlite3_int64)derivation->version); + (void)sqlite3_bind_blob(statement, 5, derivation->parameter_fingerprint, + LARDON3D_PROJECT_DB_SHA256_SIZE, SQLITE_TRANSIENT); + if (derivation->has_producer_task) { + (void)sqlite3_bind_int64(statement, 6, (sqlite3_int64)derivation->producer_task_id); + } else { + (void)sqlite3_bind_null(statement, 6); + } + (void)sqlite3_bind_int64(statement, 7, derivation->created_at); + result = step_done(database, statement, "record asset derivation"); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_load_asset_derivation( + Lardon3DProjectDb *database, uint64_t child_asset_id, + Lardon3DProjectDbAssetDerivation *derivation) { + if (!database || !valid_catalog_id(child_asset_id) || !derivation) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + memset(derivation, 0, sizeof(*derivation)); + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "SELECT parent_asset_id,child_asset_id,derivation_kind,derivation_version," + "parameter_fingerprint,producer_task_id,created_at FROM asset_derivations " + "WHERE child_asset_id=?1", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)child_asset_id); + int code = sqlite3_step(statement); + if (code == SQLITE_DONE) { + result = LARDON3D_PROJECT_DB_NOT_FOUND; + } else if (code != SQLITE_ROW) { + result = sqlite_result(database, code, "load asset derivation"); + } else { + sqlite3_int64 parent = sqlite3_column_int64(statement, 0); + sqlite3_int64 child = sqlite3_column_int64(statement, 1); + sqlite3_int64 version = sqlite3_column_int64(statement, 3); + sqlite3_int64 producer = sqlite3_column_int64(statement, 5); + sqlite3_int64 created_at = sqlite3_column_int64(statement, 6); + const void *fingerprint = sqlite3_column_blob(statement, 4); + if (parent <= 0 || child <= 0 || parent == child || version <= 0 || version > UINT32_MAX || + created_at < 0 || + !valid_asset_derivation_kind((Lardon3DProjectDbAssetDerivationKind) + sqlite3_column_int(statement, 2)) || + sqlite3_column_bytes(statement, 4) != LARDON3D_PROJECT_DB_SHA256_SIZE || !fingerprint || + (sqlite3_column_type(statement, 5) != SQLITE_NULL && producer <= 0)) { + result = LARDON3D_PROJECT_DB_CORRUPT; + } else { + derivation->parent_asset_id = (uint64_t)parent; + derivation->child_asset_id = (uint64_t)child; + derivation->kind = (Lardon3DProjectDbAssetDerivationKind)sqlite3_column_int(statement, 2); + derivation->version = (uint32_t)version; + memcpy(derivation->parameter_fingerprint, fingerprint, + LARDON3D_PROJECT_DB_SHA256_SIZE); + derivation->has_producer_task = sqlite3_column_type(statement, 5) != SQLITE_NULL; + derivation->producer_task_id = derivation->has_producer_task ? (uint64_t)producer : 0; + derivation->created_at = created_at; + } + } + (void)sqlite3_finalize(statement); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_set_selected_capture_image( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t image_id) { + if (!database || !valid_catalog_id(capture_id) || !valid_catalog_id(image_id)) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "INSERT INTO capture_selections(capture_id,image_id) VALUES(?1,?2) " + "ON CONFLICT(capture_id) DO UPDATE SET image_id=excluded.image_id", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)capture_id); + (void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)image_id); + result = step_done(database, statement, "set selected capture image"); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + +Lardon3DProjectDbResult lardon3d_project_db_get_selected_capture_image( + Lardon3DProjectDb *database, uint64_t capture_id, uint64_t *image_id) { + if (image_id) { + *image_id = 0; + } + if (!database || !valid_catalog_id(capture_id) || !image_id) { + return LARDON3D_PROJECT_DB_INVALID_ARGUMENT; + } + (void)pthread_mutex_lock(&database->mutex); + sqlite3_stmt *statement = NULL; + Lardon3DProjectDbResult result = prepare( + database, "SELECT image_id FROM capture_selections WHERE capture_id=?1", &statement); + if (result == LARDON3D_PROJECT_DB_OK) { + (void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)capture_id); + int code = sqlite3_step(statement); + if (code == SQLITE_DONE) { + result = LARDON3D_PROJECT_DB_NOT_FOUND; + } else if (code != SQLITE_ROW) { + result = sqlite_result(database, code, "get selected capture image"); + } else { + sqlite3_int64 selected = sqlite3_column_int64(statement, 0); + if (selected <= 0) { + result = LARDON3D_PROJECT_DB_CORRUPT; + } else { + *image_id = (uint64_t)selected; + } + } + (void)sqlite3_finalize(statement); + } + (void)pthread_mutex_unlock(&database->mutex); + return result; +} + Lardon3DProjectDbResult lardon3d_project_db_create_artifact(Lardon3DProjectDb *database, const Lardon3DProjectDbArtifact *artifact) { diff --git a/tests/test_geometric_verification.c b/tests/test_geometric_verification.c index d312fea..1877dc3 100644 --- a/tests/test_geometric_verification.c +++ b/tests/test_geometric_verification.c @@ -172,6 +172,11 @@ static bool create_v11_database(const char *path) { lardon3d_project_db_close(database); return execute_sql(path, "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE IF EXISTS asset_derivations;" + "DROP TABLE IF EXISTS capture_selections;" + "DROP TABLE IF EXISTS capture_assets;" + "DROP TABLE IF EXISTS capture_images;" + "DROP TABLE IF EXISTS captures;" "DROP TABLE IF EXISTS incremental_reconstruction_tasks;" "DROP TABLE IF EXISTS incremental_reconstructions;" "DROP TABLE IF EXISTS sparse_sfm_tasks;" @@ -197,7 +202,7 @@ static bool test_model_api(const char *path) { Lardon3DProjectDb *database = NULL; CHECK(lardon3d_project_db_open(path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); Parents parents; CHECK(create_parents(database, &parents)); @@ -481,7 +486,7 @@ static bool test_migration(const char *v11_path, const char *failed_path) { 0)); CHECK(lardon3d_project_db_open(v11_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); Parents parents; CHECK(create_parents(database, &parents)); unsigned char fingerprint[32] = {0x91}; @@ -496,7 +501,7 @@ static bool test_migration(const char *v11_path, const char *failed_path) { migrated_result.geometric_verification_result_id; lardon3d_project_db_close(database); CHECK(query_integer( - v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v11_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK( query_integer(v11_path, "SELECT count(*) FROM sqlite_master WHERE type='index' AND " @@ -525,7 +530,7 @@ static bool test_migration(const char *v11_path, const char *failed_path) { 0)); CHECK(lardon3d_project_db_open(failed_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); return true; } diff --git a/tests/test_geometric_verifier_task.c b/tests/test_geometric_verifier_task.c index 4ed3bc2..b4e41ca 100644 --- a/tests/test_geometric_verifier_task.c +++ b/tests/test_geometric_verifier_task.c @@ -332,6 +332,11 @@ static bool run_task_test(void) { CHECK(exec_sql( database_path, "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE IF EXISTS asset_derivations;" + "DROP TABLE IF EXISTS capture_selections;" + "DROP TABLE IF EXISTS capture_assets;" + "DROP TABLE IF EXISTS capture_images;" + "DROP TABLE IF EXISTS captures;" "DROP TABLE IF EXISTS incremental_reconstruction_tasks;" "DROP TABLE IF EXISTS incremental_reconstructions;" "DROP TABLE IF EXISTS sparse_sfm_tasks;" @@ -364,7 +369,7 @@ static bool run_task_test(void) { 0)); CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); CHECK(remove_tree(root)); return true; diff --git a/tests/test_match_result.c b/tests/test_match_result.c index c80cc7b..695fa41 100644 --- a/tests/test_match_result.c +++ b/tests/test_match_result.c @@ -53,6 +53,11 @@ static bool create_v9_database(const char *path) { return false; static const char sql[] = "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE IF EXISTS asset_derivations;" + "DROP TABLE IF EXISTS capture_selections;" + "DROP TABLE IF EXISTS capture_assets;" + "DROP TABLE IF EXISTS capture_images;" + "DROP TABLE IF EXISTS captures;" "DROP TABLE IF EXISTS incremental_reconstruction_tasks;" "DROP TABLE IF EXISTS incremental_reconstructions;" "DROP TABLE IF EXISTS sparse_sfm_tasks;" @@ -119,7 +124,7 @@ static bool run_test(void) { Lardon3DProjectDb *database = NULL; CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(database && lardon3d_project_db_schema_version(database) == 18); + CHECK(database && lardon3d_project_db_schema_version(database) == 19); Lardon3DProjectDbScanSet scanset; CHECK(lardon3d_project_db_create_scanset(database, "Match-test", &scanset) == @@ -496,7 +501,7 @@ static bool run_test(void) { CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); /* Verify persistence: load previously created results */ CHECK(lardon3d_project_db_load_match_result(database, first_id, &loaded) == @@ -539,11 +544,11 @@ static bool run_test(void) { v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 9)); CHECK(lardon3d_project_db_open(v9_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK(query_integer( - v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v9_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK( query_integer(v9_path, "SELECT count(*) FROM sqlite_master WHERE type='table' AND " @@ -571,7 +576,7 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(failed_v10_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; diff --git a/tests/test_matcher_task.c b/tests/test_matcher_task.c index deb70ca..40f1857 100644 --- a/tests/test_matcher_task.c +++ b/tests/test_matcher_task.c @@ -89,6 +89,11 @@ static bool downgrade_project_to_historical_v10(const char *database_path) { } static const char sql[] = "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE IF EXISTS asset_derivations;" + "DROP TABLE IF EXISTS capture_selections;" + "DROP TABLE IF EXISTS capture_assets;" + "DROP TABLE IF EXISTS capture_images;" + "DROP TABLE IF EXISTS captures;" "DROP TABLE IF EXISTS incremental_reconstruction_tasks;" "DROP TABLE IF EXISTS incremental_reconstructions;" "DROP TABLE IF EXISTS sparse_sfm_tasks;" @@ -368,7 +373,7 @@ static bool run_test(void) { "name='matcher_tasks'", 0)); CHECK(reopen_runtime(&fixture)); - CHECK(lardon3d_project_db_schema_version(fixture.state.project_db) == 18); + CHECK(lardon3d_project_db_schema_version(fixture.state.project_db) == 19); CHECK( query_integer(database_path, "SELECT count(*) FROM sqlite_master WHERE type='table' AND " diff --git a/tests/test_project_db.c b/tests/test_project_db.c index e17156e..cdb853e 100644 --- a/tests/test_project_db.c +++ b/tests/test_project_db.c @@ -116,7 +116,7 @@ static bool create_future_database(const char *path) { sqlite3_exec( connection, "CREATE TABLE metadata(key TEXT PRIMARY KEY,value INTEGER NOT NULL);" - "INSERT INTO metadata VALUES('schema_version',19);", + "INSERT INTO metadata VALUES('schema_version',20);", NULL, NULL, NULL) == SQLITE_OK; return sqlite3_close(connection) == SQLITE_OK && ok; } @@ -133,6 +133,8 @@ static bool create_v7_database(const char *path) { return false; static const char sql[] = "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" "DROP TABLE incremental_reconstruction_tasks;" "DROP TABLE incremental_reconstructions;" "DROP TABLE sparse_sfm_tasks;" @@ -172,6 +174,8 @@ static bool create_v6_database(const char *path) { return false; static const char sql[] = "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" "DROP TABLE incremental_reconstruction_tasks;" "DROP TABLE incremental_reconstructions;" "DROP TABLE sparse_sfm_tasks;" @@ -219,6 +223,8 @@ static bool create_v10_database(const char *path) { } static const char sql[] = "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" "DROP TABLE incremental_reconstruction_tasks;" "DROP TABLE incremental_reconstructions;" "DROP TABLE sparse_sfm_tasks;" @@ -255,6 +261,8 @@ static bool create_v13_database(const char *path) { return false; static const char sql[] = "PRAGMA foreign_keys=OFF;BEGIN IMMEDIATE;" + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" "DROP TABLE incremental_reconstruction_tasks;" "DROP TABLE incremental_reconstructions;" "DROP TABLE sparse_sfm_tasks;" @@ -879,7 +887,7 @@ static bool run_test(void) { char v10_path[512], failed_v11_path[512]; char v13_path[512], true_v14_path[512], failed_v14_path[512], failed_v15_path[512], true_v15_path[512], v17_path[512], - failed_v18_path[512]; + failed_v18_path[512], failed_v19_path[512]; CHECK(snprintf(database_path, sizeof(database_path), "%s/project.db", directory) > 0); CHECK(snprintf(artifact_path, sizeof(artifact_path), "%s/artifact.bin", @@ -925,12 +933,14 @@ static bool run_test(void) { CHECK(snprintf(v17_path, sizeof(v17_path), "%s/v17.db", directory) > 0); CHECK(snprintf(failed_v18_path, sizeof(failed_v18_path), "%s/failed-v18.db", directory) > 0); + CHECK(snprintf(failed_v19_path, sizeof(failed_v19_path), + "%s/failed-v19.db", directory) > 0); char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY]; Lardon3DProjectDb *database = NULL; CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(database && lardon3d_project_db_schema_version(database) == 18); + CHECK(database && lardon3d_project_db_schema_version(database) == 19); bool legacy_pending = true; CHECK(lardon3d_project_db_legacy_catalog_pending(database, &legacy_pending) == LARDON3D_PROJECT_DB_OK && @@ -1054,6 +1064,106 @@ static bool run_test(void) { LARDON3D_PROJECT_DB_CANDIDATE_PAIR_PAGE_MAX + 1, &pair_count) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT); + /* S1: Capture is a catalog policy layer; image_id and asset_id stay immutable. */ + Lardon3DProjectDbCapture capture, other_capture, mapped_capture; + CHECK(lardon3d_project_db_create_capture(database, replacement_scanset.scanset_id, 20, + &capture) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_create_capture(database, replacement_scanset.scanset_id, 21, + &other_capture) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_create_capture(database, UINT64_C(999999), 22, &mapped_capture) == + LARDON3D_PROJECT_DB_CONSTRAINT); + Lardon3DProjectDbCapture capture_page[2]; + size_t capture_count = 0; + CHECK(lardon3d_project_db_list_captures(database, replacement_scanset.scanset_id, 0, + capture_page, 1, &capture_count) == + LARDON3D_PROJECT_DB_OK && + capture_count == 1 && capture_page[0].capture_id == capture.capture_id); + uint64_t selected_image = 0; + CHECK(lardon3d_project_db_get_selected_capture_image(database, capture.capture_id, + &selected_image) == + LARDON3D_PROJECT_DB_NOT_FOUND); + unsigned char raw_hash[LARDON3D_PROJECT_DB_SHA256_SIZE] = {5}; + char raw_asset_path[LARDON3D_PROJECT_DB_PATH_CAPACITY]; + asset_path_for_hash(raw_hash, raw_asset_path); + Lardon3DProjectDbImageAsset raw_asset; + CHECK(lardon3d_project_db_register_image_asset(database, raw_hash, raw_asset_path, 2, 22, + &raw_asset) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_attach_capture_asset( + database, capture.capture_id, raw_asset.asset_id, + LARDON3D_DB_CAPTURE_ASSET_SOURCE) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_attach_capture_asset( + database, capture.capture_id, pair_image.asset_id, + LARDON3D_DB_CAPTURE_ASSET_SOURCE) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_attach_capture_asset( + database, capture.capture_id, raw_asset.asset_id, + (Lardon3DProjectDbCaptureAssetRole)99) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT); + CHECK(lardon3d_project_db_attach_capture_asset( + database, capture.capture_id, raw_asset.asset_id, + LARDON3D_DB_CAPTURE_ASSET_SOURCE) == LARDON3D_PROJECT_DB_CONSTRAINT); + unsigned char developed_hash[LARDON3D_PROJECT_DB_SHA256_SIZE] = {6}; + char developed_asset_path[LARDON3D_PROJECT_DB_PATH_CAPACITY]; + asset_path_for_hash(developed_hash, developed_asset_path); + Lardon3DProjectDbImage developed_image; + CHECK(lardon3d_project_db_register_image( + database, replacement_scanset.scanset_id, developed_hash, developed_asset_path, 3, + "developed.png", "/source/developed.png", 0, 23, &identity_status, + &developed_image) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_attach_capture_asset( + database, capture.capture_id, developed_image.asset_id, + LARDON3D_DB_CAPTURE_ASSET_DERIVED) == LARDON3D_PROJECT_DB_OK); + Lardon3DProjectDbCaptureAsset capture_assets[3]; + size_t capture_asset_count = 0; + CHECK(lardon3d_project_db_list_capture_assets(database, capture.capture_id, 0, capture_assets, + 3, &capture_asset_count) == + LARDON3D_PROJECT_DB_OK && + capture_asset_count == 3); + CHECK(lardon3d_project_db_attach_capture_image(database, capture.capture_id, + pair_image.image_id) == LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_attach_capture_image(database, other_capture.capture_id, + pair_image.image_id) == + LARDON3D_PROJECT_DB_CONSTRAINT); + CHECK(lardon3d_project_db_attach_capture_image(database, capture.capture_id, + developed_image.image_id) == + LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_find_capture_for_image(database, pair_image.image_id, + &mapped_capture) == LARDON3D_PROJECT_DB_OK && + mapped_capture.capture_id == capture.capture_id); + CHECK(lardon3d_project_db_set_selected_capture_image(database, capture.capture_id, + pair_image.image_id) == + LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_set_selected_capture_image(database, other_capture.capture_id, + developed_image.image_id) == + LARDON3D_PROJECT_DB_CONSTRAINT); + CHECK(lardon3d_project_db_set_selected_capture_image(database, capture.capture_id, + developed_image.image_id) == + LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_get_selected_capture_image(database, capture.capture_id, + &selected_image) == + LARDON3D_PROJECT_DB_OK && + selected_image == developed_image.image_id && pair_image.image_id != developed_image.image_id); + Lardon3DProjectDbAssetDerivation derivation = { + .parent_asset_id = raw_asset.asset_id, + .child_asset_id = developed_image.asset_id, + .kind = LARDON3D_DB_ASSET_DERIVATION_GENERIC_VERSIONED, + .version = 1, + .created_at = 24}; + memset(derivation.parameter_fingerprint, 0xA5, sizeof(derivation.parameter_fingerprint)); + CHECK(lardon3d_project_db_record_asset_derivation(database, &derivation) == + LARDON3D_PROJECT_DB_OK); + Lardon3DProjectDbAssetDerivation loaded_derivation; + CHECK(lardon3d_project_db_load_asset_derivation(database, developed_image.asset_id, + &loaded_derivation) == LARDON3D_PROJECT_DB_OK && + loaded_derivation.parent_asset_id == raw_asset.asset_id && + loaded_derivation.version == 1 && + memcmp(loaded_derivation.parameter_fingerprint, derivation.parameter_fingerprint, + sizeof(derivation.parameter_fingerprint)) == 0); + derivation.parent_asset_id = pair_image.asset_id; + CHECK(lardon3d_project_db_record_asset_derivation(database, &derivation) == + LARDON3D_PROJECT_DB_CONSTRAINT); + derivation.parent_asset_id = developed_image.asset_id; + CHECK(lardon3d_project_db_record_asset_derivation(database, &derivation) == + LARDON3D_PROJECT_DB_INVALID_ARGUMENT); + Lardon3DProjectDbProject project = {.created_at = 100, .updated_at = 100}; (void)snprintf(project.stable_id, sizeof(project.stable_id), "project-0001"); (void)snprintf(project.name, sizeof(project.name), "Projet test"); @@ -1248,7 +1358,7 @@ static bool run_test(void) { database = NULL; CHECK(query_integer(database_path, "SELECT value FROM metadata WHERE key='schema_version'", - 18)); + 19)); CHECK(query_integer(database_path, "SELECT count(*) FROM tasks WHERE task_id=1", 1)); @@ -1270,10 +1380,12 @@ static bool run_test(void) { "created_at) VALUES(1,1,1,1,1,zeroblob(32),1,2,1,0,0,1);" "DROP TABLE incremental_reconstruction_tasks;" "DROP TABLE incremental_reconstructions;" + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" "UPDATE metadata SET value=17 WHERE key='schema_version';")); CHECK(lardon3d_project_db_open(v17_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK(query_integer(v17_path, @@ -1290,10 +1402,36 @@ static bool run_test(void) { LARDON3D_PROJECT_DB_OK); lardon3d_project_db_close(database); database = NULL; + + CHECK(lardon3d_project_db_open(failed_v19_path, &database, error) == + LARDON3D_PROJECT_DB_OK); + lardon3d_project_db_close(database); + database = NULL; + CHECK(execute_test_sql( + failed_v19_path, + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" + "UPDATE metadata SET value=18 WHERE key='schema_version';")); + CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V19", "1", 1) == 0); + CHECK(lardon3d_project_db_open(failed_v19_path, &database, error) == + LARDON3D_PROJECT_DB_IO_ERROR); + CHECK(unsetenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V19") == 0); + CHECK(query_integer(failed_v19_path, + "SELECT value FROM metadata WHERE key='schema_version'", 18)); + CHECK(query_integer(failed_v19_path, + "SELECT count(*) FROM sqlite_master WHERE type='table' AND " + "name='captures'", 0)); + CHECK(lardon3d_project_db_open(failed_v19_path, &database, error) == + LARDON3D_PROJECT_DB_OK); + CHECK(lardon3d_project_db_schema_version(database) == 19); + lardon3d_project_db_close(database); + database = NULL; CHECK(execute_test_sql( failed_v18_path, "DROP TABLE incremental_reconstruction_tasks;" "DROP TABLE incremental_reconstructions;" + "DROP TABLE asset_derivations;DROP TABLE capture_selections;DROP TABLE capture_assets;" + "DROP TABLE capture_images;DROP TABLE captures;" "UPDATE metadata SET value=17 WHERE key='schema_version';")); CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V18", "1", 1) == 0); CHECK(lardon3d_project_db_open(failed_v18_path, &database, error) == @@ -1336,7 +1474,7 @@ static bool run_test(void) { CHECK(create_v1_database(legacy_path)); CHECK(lardon3d_project_db_open(legacy_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); CHECK(lardon3d_project_db_get_project(database, &loaded_project) == LARDON3D_PROJECT_DB_OK && strcmp(loaded_project.stable_id, "legacy-project") == 0); @@ -1351,7 +1489,7 @@ static bool run_test(void) { database = NULL; CHECK(query_integer(legacy_path, "SELECT value FROM metadata WHERE key='schema_version'", - 18)); + 19)); CHECK(create_v1_database(failed_migration_path)); CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V2", "1", 1) == 0); @@ -1382,7 +1520,7 @@ static bool run_test(void) { lardon3d_project_db_close(database); database = NULL; CHECK(query_integer( - v2_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v2_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK(create_v2_database(failed_v3_migration_path)); CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V3", "1", 1) == 0); @@ -1416,7 +1554,7 @@ static bool run_test(void) { lardon3d_project_db_close(database); database = NULL; CHECK(query_integer( - v3_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v3_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK(create_v3_database(failed_v4_path)); CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V4", "1", 1) == 0); @@ -1438,7 +1576,7 @@ static bool run_test(void) { fprintf(stderr, "Migration v4 (%d): %s\n", (int)v4_result, error); } CHECK(v4_result == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); CHECK(lardon3d_project_db_load_task(database, 9, &task) == LARDON3D_PROJECT_DB_OK); CHECK(lardon3d_project_db_load_artifact(database, "legacy-artifact", @@ -1493,7 +1631,7 @@ static bool run_test(void) { error); } CHECK(retry_v7 == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -1503,7 +1641,7 @@ static bool run_test(void) { 5)); CHECK(lardon3d_project_db_open(direct_v5_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -1512,11 +1650,11 @@ static bool run_test(void) { v8_path, "SELECT value FROM metadata WHERE key='schema_version'", 7)); CHECK(lardon3d_project_db_open(v8_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK(query_integer( - v8_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v8_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK(create_v7_database(failed_v8_path)); CHECK(setenv("LARDON3D_TEST_PROJECT_DB_FAIL_MIGRATION_V8", "1", 1) == 0); @@ -1533,7 +1671,7 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(failed_v8_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -1552,11 +1690,11 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(v10_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK(query_integer( - v10_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v10_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK( query_integer(v10_path, "SELECT count(*) FROM sqlite_master WHERE type='table' AND " @@ -1583,7 +1721,7 @@ static bool run_test(void) { 1)); CHECK(lardon3d_project_db_open(failed_v11_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -1597,11 +1735,11 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(v13_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK(query_integer( - v13_path, "SELECT value FROM metadata WHERE key='schema_version'", 18)); + v13_path, "SELECT value FROM metadata WHERE key='schema_version'", 19)); CHECK( query_integer(v13_path, "SELECT count(*) FROM sqlite_master WHERE type='table' AND " @@ -1624,7 +1762,7 @@ static bool run_test(void) { true_v14_path); } CHECK(true_v14_result == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK( @@ -1739,7 +1877,7 @@ static bool run_test(void) { if (true_v15_result != LARDON3D_PROJECT_DB_OK) fprintf(stderr, "true v15 upgrade: %d %s\n", true_v15_result, error); CHECK(true_v15_result == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); Lardon3DProjectDbProject migrated_project; CHECK(lardon3d_project_db_get_project(database, &migrated_project) == LARDON3D_PROJECT_DB_OK && @@ -1757,6 +1895,20 @@ static bool run_test(void) { ".lardon3d/checkpoints/legacy.scope") == 0); CHECK(query_integer(true_v15_path, "SELECT count(*) FROM scansets", 1)); CHECK(query_integer(true_v15_path, "SELECT count(*) FROM images", 2)); + CHECK(query_integer(true_v15_path, "SELECT count(*) FROM captures", 2)); + CHECK(query_integer(true_v15_path, "SELECT count(*) FROM capture_images", 2)); + CHECK(query_integer(true_v15_path, "SELECT count(*) FROM capture_assets WHERE role=1", 2)); + CHECK(query_integer(true_v15_path, "SELECT count(*) FROM capture_selections", 2)); + CHECK(query_integer( + true_v15_path, + "SELECT count(*) FROM captures c JOIN capture_images ci ON ci.capture_id=c.capture_id " + "JOIN images i ON i.image_id=ci.image_id WHERE c.scanset_id=i.scanset_id " + "AND c.capture_id=i.image_id", + 2)); + Lardon3DProjectDbCapture post_migration_capture; + CHECK(lardon3d_project_db_create_capture(database, 1, 2, &post_migration_capture) == + LARDON3D_PROJECT_DB_OK); + CHECK(post_migration_capture.capture_id > 2); CHECK(query_integer(true_v15_path, "SELECT count(*) FROM feature_sets", 2)); CHECK(query_integer(true_v15_path, "SELECT count(*) FROM track_sets", 1)); CHECK(query_integer(true_v15_path, "SELECT count(*) FROM tracks", 1)); @@ -1771,7 +1923,7 @@ static bool run_test(void) { database = NULL; CHECK(lardon3d_project_db_open(database_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; CHECK(schema_compare(database_path, true_v15_path, true)); @@ -1806,7 +1958,7 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(true_v15_path, &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -1825,7 +1977,7 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(failed_v14_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -1844,7 +1996,7 @@ static bool run_test(void) { 0)); CHECK(lardon3d_project_db_open(failed_v15_path, &database, error) == LARDON3D_PROJECT_DB_OK && - lardon3d_project_db_schema_version(database) == 18); + lardon3d_project_db_schema_version(database) == 19); lardon3d_project_db_close(database); database = NULL; @@ -2315,6 +2467,7 @@ static bool run_test(void) { CHECK(unlink(failed_v15_path) == 0); CHECK(unlink(v17_path) == 0); CHECK(unlink(failed_v18_path) == 0); + CHECK(unlink(failed_v19_path) == 0); CHECK(rmdir(directory) == 0); return true; } diff --git a/tests/test_sparse_sfm_model.c b/tests/test_sparse_sfm_model.c index f6bf832..cd59011 100644 --- a/tests/test_sparse_sfm_model.c +++ b/tests/test_sparse_sfm_model.c @@ -123,7 +123,7 @@ static bool run_test(void) { Lardon3DProjectDb *db = NULL; char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY]; CHECK(lardon3d_project_db_open(path, &db, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(db) == 18); + CHECK(lardon3d_project_db_schema_version(db) == 19); Lardon3DProjectDbScanSet scanset; CHECK(lardon3d_project_db_create_scanset(db, "Sparse model", &scanset) == diff --git a/tests/test_sparse_sfm_payload.cpp b/tests/test_sparse_sfm_payload.cpp index 5db635c..723cdb3 100644 --- a/tests/test_sparse_sfm_payload.cpp +++ b/tests/test_sparse_sfm_payload.cpp @@ -66,7 +66,7 @@ int main() { Lardon3DProjectDb *database = nullptr; char error[LARDON3D_PROJECT_DB_ERROR_CAPACITY]{}; CHECK(lardon3d_project_db_open(path.c_str(), &database, error) == LARDON3D_PROJECT_DB_OK); - CHECK(lardon3d_project_db_schema_version(database) == 18); + CHECK(lardon3d_project_db_schema_version(database) == 19); sqlite3 *raw = nullptr; CHECK(sqlite3_open(path.c_str(), &raw) == SQLITE_OK);