feat: make standard image ingestion capture-safe
This commit is contained in:
parent
9f409645e3
commit
4b611ca723
3 changed files with 143 additions and 8 deletions
|
|
@ -2510,6 +2510,80 @@ 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 ";
|
||||
|
||||
static Lardon3DProjectDbResult ensure_capture_for_registered_image_locked(
|
||||
Lardon3DProjectDb *database, uint64_t scanset_id, uint64_t asset_id, uint64_t image_id) {
|
||||
sqlite3_stmt *statement = NULL;
|
||||
Lardon3DProjectDbResult result = prepare(
|
||||
database, "SELECT capture_id FROM capture_images WHERE image_id=?1", &statement);
|
||||
if (result != LARDON3D_PROJECT_DB_OK) {
|
||||
return result;
|
||||
}
|
||||
(void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)image_id);
|
||||
int code = sqlite3_step(statement);
|
||||
if (code == SQLITE_ROW) {
|
||||
(void)sqlite3_finalize(statement);
|
||||
return LARDON3D_PROJECT_DB_OK;
|
||||
}
|
||||
(void)sqlite3_finalize(statement);
|
||||
if (code != SQLITE_DONE) {
|
||||
return sqlite_result(database, code, "find capture for registered image");
|
||||
}
|
||||
|
||||
result = prepare(
|
||||
database, "INSERT INTO captures(scanset_id,created_at) "
|
||||
"SELECT scanset_id,imported_at FROM images WHERE image_id=?1 AND scanset_id=?2",
|
||||
&statement);
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
(void)sqlite3_bind_int64(statement, 1, (sqlite3_int64)image_id);
|
||||
(void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)scanset_id);
|
||||
result = step_done(database, statement, "create initial image capture");
|
||||
statement = NULL;
|
||||
}
|
||||
if (result != LARDON3D_PROJECT_DB_OK) {
|
||||
return result;
|
||||
}
|
||||
if (sqlite3_changes(database->connection) != 1) {
|
||||
return LARDON3D_PROJECT_DB_CORRUPT;
|
||||
}
|
||||
sqlite3_int64 capture_id = sqlite3_last_insert_rowid(database->connection);
|
||||
if (capture_id <= 0) {
|
||||
return LARDON3D_PROJECT_DB_CORRUPT;
|
||||
}
|
||||
|
||||
result = prepare(database,
|
||||
"INSERT INTO capture_assets(capture_id,asset_id,role) VALUES(?1,?2,1)",
|
||||
&statement);
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
(void)sqlite3_bind_int64(statement, 1, capture_id);
|
||||
(void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)asset_id);
|
||||
result = step_done(database, statement, "attach initial image source asset");
|
||||
statement = NULL;
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = prepare(database,
|
||||
"INSERT INTO capture_images(capture_id,image_id) VALUES(?1,?2)",
|
||||
&statement);
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
(void)sqlite3_bind_int64(statement, 1, capture_id);
|
||||
(void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)image_id);
|
||||
result = step_done(database, statement, "attach initial image capture");
|
||||
statement = NULL;
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = prepare(database,
|
||||
"INSERT INTO capture_selections(capture_id,image_id) VALUES(?1,?2)",
|
||||
&statement);
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
(void)sqlite3_bind_int64(statement, 1, capture_id);
|
||||
(void)sqlite3_bind_int64(statement, 2, (sqlite3_int64)image_id);
|
||||
result = step_done(database, statement, "select initial capture image");
|
||||
statement = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Lardon3DProjectDbResult lardon3d_project_db_register_image_asset(
|
||||
Lardon3DProjectDb *database,
|
||||
const unsigned char sha256[LARDON3D_PROJECT_DB_SHA256_SIZE], const char *asset_path,
|
||||
|
|
@ -2682,6 +2756,10 @@ Lardon3DProjectDbResult lardon3d_project_db_register_image(
|
|||
(void)sqlite3_finalize(statement);
|
||||
statement = NULL;
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = ensure_capture_for_registered_image_locked(database, scanset_id, (uint64_t)asset_id,
|
||||
(uint64_t)image_id);
|
||||
}
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
result = execute(database, "COMMIT", "commit image register");
|
||||
}
|
||||
|
|
@ -6346,12 +6424,14 @@ lardon3d_project_db_test_delete_catalog_identity(Lardon3DProjectDb *database, ui
|
|||
Lardon3DProjectDbResult result =
|
||||
execute(database, "BEGIN IMMEDIATE", "begin catalog identity deletion test");
|
||||
const char *sql[] = {
|
||||
("DELETE FROM captures WHERE capture_id IN "
|
||||
"(SELECT capture_id FROM capture_images WHERE image_id=?1)"),
|
||||
"DELETE FROM images WHERE image_id=?1",
|
||||
"DELETE FROM image_assets WHERE asset_id=?1",
|
||||
"DELETE FROM scansets WHERE scanset_id=?1",
|
||||
};
|
||||
const uint64_t ids[] = {image_id, asset_id, scanset_id};
|
||||
for (size_t index = 0; index < 3 && result == LARDON3D_PROJECT_DB_OK; ++index) {
|
||||
const uint64_t ids[] = {image_id, image_id, asset_id, scanset_id};
|
||||
for (size_t index = 0; index < 4 && result == LARDON3D_PROJECT_DB_OK; ++index) {
|
||||
sqlite3_stmt *statement = NULL;
|
||||
result = prepare(database, sql[index], &statement);
|
||||
if (result == LARDON3D_PROJECT_DB_OK) {
|
||||
|
|
|
|||
|
|
@ -114,6 +114,26 @@ import_thread(void *userdata)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
has_initial_capture(Lardon3DProjectDb *database, const Lardon3DProjectDbImage *image,
|
||||
const Lardon3DProjectDbImageAsset *asset, uint64_t scanset_id,
|
||||
Lardon3DProjectDbCapture *capture)
|
||||
{
|
||||
if (lardon3d_project_db_find_capture_for_image(database, image->image_id, capture)
|
||||
!= LARDON3D_PROJECT_DB_OK
|
||||
|| capture->scanset_id != scanset_id) return false;
|
||||
uint64_t selected_image = 0;
|
||||
if (lardon3d_project_db_get_selected_capture_image(database, capture->capture_id,
|
||||
&selected_image) != LARDON3D_PROJECT_DB_OK
|
||||
|| selected_image != image->image_id) return false;
|
||||
Lardon3DProjectDbCaptureAsset capture_asset;
|
||||
size_t count = 0;
|
||||
return lardon3d_project_db_list_capture_assets(database, capture->capture_id, 0,
|
||||
&capture_asset, 1, &count) == LARDON3D_PROJECT_DB_OK
|
||||
&& count == 1 && capture_asset.asset_id == asset->asset_id
|
||||
&& capture_asset.role == LARDON3D_DB_CAPTURE_ASSET_SOURCE;
|
||||
}
|
||||
|
||||
static bool
|
||||
run_test(void)
|
||||
{
|
||||
|
|
@ -139,6 +159,9 @@ run_test(void)
|
|||
CHECK(lardon3d_image_catalog_create_scanset(&state, "Campagne générale", &a));
|
||||
CHECK(lardon3d_image_catalog_create_scanset(&state, "Pièce démontée", &b));
|
||||
CHECK(a.scanset_id != b.scanset_id);
|
||||
Lardon3DProjectDbCapture preexisting_capture;
|
||||
CHECK(lardon3d_project_db_create_capture(database, a.scanset_id, 1,
|
||||
&preexisting_capture) == LARDON3D_PROJECT_DB_OK);
|
||||
Lardon3DProjectDbScanSet scanset_page[2]; size_t scanset_count = 0;
|
||||
CHECK(lardon3d_project_db_list_scansets(database, 0, scanset_page, 1,
|
||||
&scanset_count) == LARDON3D_PROJECT_DB_OK && scanset_count == 1
|
||||
|
|
@ -163,14 +186,20 @@ run_test(void)
|
|||
CHECK(lardon3d_image_catalog_import_file(&state, a.scanset_id, source_a, 0,
|
||||
&image_a, &asset_a) == LARDON3D_IMAGE_CATALOG_IMPORTED);
|
||||
CHECK(canonical_asset_path(asset_a.path));
|
||||
Lardon3DProjectDbCapture capture_a, capture_b, capture_different;
|
||||
CHECK(has_initial_capture(database, &image_a, &asset_a, a.scanset_id, &capture_a));
|
||||
CHECK(capture_a.capture_id != image_a.image_id);
|
||||
CHECK(lardon3d_image_catalog_import_file(&state, a.scanset_id, source_b, 0,
|
||||
&duplicate_a, &duplicate_asset) == LARDON3D_IMAGE_CATALOG_ALREADY_PRESENT);
|
||||
CHECK(image_a.image_id == duplicate_a.image_id
|
||||
&& asset_a.asset_id == duplicate_asset.asset_id);
|
||||
CHECK(has_initial_capture(database, &duplicate_a, &duplicate_asset, a.scanset_id, &capture_a));
|
||||
CHECK(lardon3d_image_catalog_import_file(&state, b.scanset_id, source_b, 0,
|
||||
&image_b, &asset_b) == LARDON3D_IMAGE_CATALOG_IMPORTED);
|
||||
CHECK(image_b.image_id != image_a.image_id && asset_b.asset_id == asset_a.asset_id);
|
||||
CHECK(strcmp(asset_b.path, asset_a.path) == 0);
|
||||
CHECK(has_initial_capture(database, &image_b, &asset_b, b.scanset_id, &capture_b));
|
||||
CHECK(capture_b.capture_id != capture_a.capture_id);
|
||||
|
||||
char different_dir[PATH_MAX], same_name[PATH_MAX];
|
||||
CHECK(join_path(different_dir, root, "different") && mkdir(different_dir, 0700) == 0);
|
||||
|
|
@ -184,6 +213,9 @@ run_test(void)
|
|||
&& different_asset.asset_id != asset_a.asset_id
|
||||
&& canonical_asset_path(different_asset.path)
|
||||
&& strcmp(different_asset.path, asset_a.path) != 0);
|
||||
CHECK(has_initial_capture(database, &different_image, &different_asset, a.scanset_id,
|
||||
&capture_different));
|
||||
CHECK(capture_different.capture_id != capture_a.capture_id);
|
||||
|
||||
char orphan_source[PATH_MAX], asset_root[PATH_MAX];
|
||||
CHECK(join_path(orphan_source, root, "orphan.jpg"));
|
||||
|
|
|
|||
|
|
@ -1066,22 +1066,24 @@ static bool run_test(void) {
|
|||
|
||||
/* 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_find_capture_for_image(database, pair_image.image_id, &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,
|
||||
CHECK(lardon3d_project_db_list_captures(database, replacement_scanset.scanset_id,
|
||||
capture.capture_id - 1,
|
||||
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);
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
selected_image == pair_image.image_id);
|
||||
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);
|
||||
|
|
@ -1093,7 +1095,7 @@ static bool run_test(void) {
|
|||
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);
|
||||
LARDON3D_DB_CAPTURE_ASSET_SOURCE) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
CHECK(lardon3d_project_db_attach_capture_asset(
|
||||
database, capture.capture_id, raw_asset.asset_id,
|
||||
(Lardon3DProjectDbCaptureAssetRole)99) == LARDON3D_PROJECT_DB_INVALID_ARGUMENT);
|
||||
|
|
@ -1108,6 +1110,17 @@ static bool run_test(void) {
|
|||
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);
|
||||
Lardon3DProjectDbCapture developed_capture;
|
||||
CHECK(lardon3d_project_db_find_capture_for_image(database, developed_image.image_id,
|
||||
&developed_capture) == LARDON3D_PROJECT_DB_OK);
|
||||
lardon3d_project_db_close(database);
|
||||
database = NULL;
|
||||
char remove_developed_capture[256];
|
||||
CHECK(snprintf(remove_developed_capture, sizeof(remove_developed_capture),
|
||||
"PRAGMA foreign_keys=ON;DELETE FROM captures WHERE capture_id=%llu",
|
||||
(unsigned long long)developed_capture.capture_id) > 0);
|
||||
CHECK(execute_test_sql(database_path, remove_developed_capture));
|
||||
CHECK(lardon3d_project_db_open(database_path, &database, error) == 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);
|
||||
|
|
@ -1118,7 +1131,7 @@ static bool run_test(void) {
|
|||
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);
|
||||
pair_image.image_id) == LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
CHECK(lardon3d_project_db_attach_capture_image(database, other_capture.capture_id,
|
||||
pair_image.image_id) ==
|
||||
LARDON3D_PROJECT_DB_CONSTRAINT);
|
||||
|
|
@ -1141,6 +1154,16 @@ static bool run_test(void) {
|
|||
&selected_image) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
selected_image == developed_image.image_id && pair_image.image_id != developed_image.image_id);
|
||||
CHECK(lardon3d_project_db_register_image(
|
||||
database, replacement_scanset.scanset_id, third_hash, third_asset_path, 1,
|
||||
"pair-a.jpg", "/source/pair-a.jpg", 0, 3, &identity_status,
|
||||
&pair_image) == LARDON3D_PROJECT_DB_OK &&
|
||||
identity_status == LARDON3D_PROJECT_DB_IMAGE_ALREADY_PRESENT);
|
||||
CHECK(lardon3d_project_db_get_selected_capture_image(database, capture.capture_id,
|
||||
&selected_image) ==
|
||||
LARDON3D_PROJECT_DB_OK &&
|
||||
selected_image == developed_image.image_id);
|
||||
CHECK(query_integer(database_path, "SELECT count(*) FROM asset_derivations", 0));
|
||||
Lardon3DProjectDbAssetDerivation derivation = {
|
||||
.parent_asset_id = raw_asset.asset_id,
|
||||
.child_asset_id = developed_image.asset_id,
|
||||
|
|
|
|||
Loading…
Reference in a new issue