Modified: subversion/trunk/subversion/libsvn_wc/update_editor.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/update_editor.c?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/update_editor.c (original) +++ subversion/trunk/subversion/libsvn_wc/update_editor.c Tue Dec 13 09:49:29 2022 @@ -48,6 +48,7 @@ #include "conflicts.h" #include "translate.h" #include "workqueue.h" +#include "textbase.h" #include "private/svn_subr_private.h" #include "private/svn_wc_private.h" @@ -439,7 +440,8 @@ struct handler_baton /* Get an empty file in the temporary area for WRI_ABSPATH. The file will not be set for automatic deletion, and the name will be returned in - TMP_FILENAME. + TMP_FILENAME_P. Set *CLEANUP_WORK_ITEM_P to a new work item that will + remove the temporary file. This implementation creates a new empty file with a unique name. @@ -451,19 +453,35 @@ struct handler_baton ### file name to create later. A better way may not be readily available. */ static svn_error_t * -get_empty_tmp_file(const char **tmp_filename, +get_empty_tmp_file(const char **tmp_filename_p, + svn_skel_t **cleanup_work_item_p, svn_wc__db_t *db, const char *wri_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *temp_dir_abspath; + const char *tmp_filename; + svn_skel_t *work_item; + svn_error_t *err; SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath, + SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_filename, temp_dir_abspath, svn_io_file_del_none, scratch_pool, scratch_pool)); + err = svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, + tmp_filename, + result_pool, scratch_pool); + if (err) + { + return svn_error_compose_create( + err, + svn_io_remove_file2(tmp_filename, TRUE, scratch_pool)); + } + + *tmp_filename_p = tmp_filename; + *cleanup_work_item_p = work_item; return SVN_NO_ERROR; } @@ -3726,10 +3744,10 @@ lazy_open_source(svn_stream_t **stream, { struct file_baton *fb = baton; - SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db, - fb->local_abspath, - fb->original_checksum, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__textbase_get_contents(stream, fb->edit_baton->db, + fb->local_abspath, + fb->original_checksum, FALSE, + result_pool, scratch_pool)); return SVN_NO_ERROR; @@ -3748,19 +3766,35 @@ lazy_open_target(svn_stream_t **stream_p svn_stream_t *pristine_install_stream; svn_wc__working_file_writer_t *file_writer; svn_stream_t *stream; + svn_boolean_t hydrated; + + if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed) + { + hydrated = TRUE; + } + else if (fb->adding_file && !fb->add_existed) + { + /* Clean new file, hint that we don't need the text-base. */ + hydrated = FALSE; + } + else + { + hydrated = TRUE; + } /* By convention return value is undefined on error, but we rely on HB->INSTALL_DATA value in window_handler() and abort INSTALL_STREAM if is not NULL on error. So we store INSTALL_DATA to local variable first, to leave HB->INSTALL_DATA unchanged on error. */ - SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_install_stream, - &pristine_install_data, - &hb->new_text_base_sha1_checksum, - NULL, - fb->edit_baton->db, - fb->dir_baton->local_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__textbase_prepare_install(&pristine_install_stream, + &pristine_install_data, + &hb->new_text_base_sha1_checksum, + NULL, + fb->edit_baton->db, + fb->local_abspath, + hydrated, + result_pool, scratch_pool)); if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed) { @@ -4063,17 +4097,19 @@ svn_wc__perform_file_merge(svn_skel_t ** the textual changes into the working file. */ const char *oldrev_str, *newrev_str, *mine_str; const char *merge_left; - svn_boolean_t delete_left = FALSE; const char *path_ext = ""; const char *new_pristine_abspath; enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; svn_skel_t *work_item; + svn_skel_t *cleanup_queue = NULL; *work_items = NULL; - SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, - db, wri_abspath, new_checksum, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__textbase_setaside_wq(&new_pristine_abspath, &work_item, + db, local_abspath, new_checksum, + cancel_func, cancel_baton, + result_pool, scratch_pool)); + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool); /* If we have any file extensions we're supposed to preserve in generated conflict file names, then find @@ -4107,14 +4143,19 @@ svn_wc__perform_file_merge(svn_skel_t ** if (! original_checksum) { - SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath, + SVN_ERR(get_empty_tmp_file(&merge_left, &work_item, db, wri_abspath, result_pool, scratch_pool)); - delete_left = TRUE; + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool); } else - SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath, - original_checksum, - result_pool, scratch_pool)); + { + SVN_ERR(svn_wc__textbase_setaside_wq(&merge_left, &work_item, + db, local_abspath, + original_checksum, + cancel_func, cancel_baton, + result_pool, scratch_pool)); + cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool); + } /* Merge the changes from the old textbase to the new textbase into the file we're updating. @@ -4135,16 +4176,9 @@ svn_wc__perform_file_merge(svn_skel_t ** result_pool, scratch_pool)); *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); - *found_conflict = (merge_outcome == svn_wc_merge_conflict); + *work_items = svn_wc__wq_merge(*work_items, cleanup_queue, result_pool); - /* If we created a temporary left merge file, get rid of it. */ - if (delete_left) - { - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, - merge_left, - result_pool, scratch_pool)); - *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); - } + *found_conflict = (merge_outcome == svn_wc_merge_conflict); return SVN_NO_ERROR; } @@ -4729,9 +4763,9 @@ close_file(void *file_baton, } else { - SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, eb->db, - eb->wcroot_abspath, new_checksum, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__textbase_get_contents(&src_stream, eb->db, + fb->local_abspath, new_checksum, + FALSE, scratch_pool, scratch_pool)); } SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, scratch_pool)); @@ -4871,6 +4905,7 @@ update_keywords_after_switch_cb(void *ba svn_boolean_t record_fileinfo; svn_skel_t *work_items; const char *install_from; + svn_skel_t *cleanup_work_item; propval = svn_hash_gets(props, SVN_PROP_KEYWORDS); if (!propval) @@ -4896,11 +4931,18 @@ update_keywords_after_switch_cb(void *ba SVN_ERR(svn_stream_copy3(working_stream, install_from_stream, eb->cancel_func, eb->cancel_baton, scratch_pool)); + SVN_ERR(svn_wc__wq_build_file_remove(&cleanup_work_item, eb->db, + local_abspath, install_from, + scratch_pool, scratch_pool)); record_fileinfo = FALSE; } else { - install_from = NULL; + SVN_ERR(svn_wc__textbase_setaside_wq(&install_from, + &cleanup_work_item, + eb->db, local_abspath, NULL, + eb->cancel_func, eb->cancel_baton, + scratch_pool, scratch_pool)); record_fileinfo = TRUE; } @@ -4909,15 +4951,7 @@ update_keywords_after_switch_cb(void *ba eb->use_commit_times, record_fileinfo, scratch_pool, scratch_pool)); - if (install_from) - { - svn_skel_t *work_item; - - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, - local_abspath, install_from, - scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - } + work_items = svn_wc__wq_merge(work_items, cleanup_work_item, scratch_pool); SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items, scratch_pool)); @@ -5606,33 +5640,39 @@ svn_wc_add_repos_file4(svn_wc_context_t entry_props, pool, pool)); } + { + const char *tmp_dir_abspath; + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath, + db, dir_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath, + tmp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + } + /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */ if (copyfrom_url) { - SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents, - &install_data, - &new_text_base_sha1_checksum, - &new_text_base_md5_checksum, - wc_ctx->db, local_abspath, - scratch_pool, scratch_pool)); + svn_stream_t *install_stream; + + SVN_ERR(svn_wc__textbase_prepare_install(&install_stream, + &install_data, + &new_text_base_sha1_checksum, + &new_text_base_md5_checksum, + wc_ctx->db, local_abspath, + copyfrom_url != NULL, + scratch_pool, scratch_pool)); + + tmp_base_contents = svn_stream_tee(install_stream, + tmp_base_contents, + scratch_pool); } else { - const char *tmp_dir_abspath; - - /* We are not installing a PRISTINE file, but we use the same code to - create whatever we want to install */ - - SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath, - db, dir_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath, - tmp_dir_abspath, svn_io_file_del_none, - scratch_pool, scratch_pool)); - new_text_base_sha1_checksum = NULL; new_text_base_md5_checksum = NULL; } @@ -5655,6 +5695,10 @@ svn_wc_add_repos_file4(svn_wc_context_t SVN_ERR(svn_stream_copy3(new_contents, tmp_contents, cancel_func, cancel_baton, pool)); } + else + { + source_abspath = tmp_text_base_abspath; + } /* Install new text base for copied files. Added files do NOT have a text base. */ @@ -5690,42 +5734,20 @@ svn_wc_add_repos_file4(svn_wc_context_t new_text_base_md5_checksum = NULL; } - /* For added files without NEW_CONTENTS, then generate the working file - from the provided "pristine" contents. */ - if (new_contents == NULL && copyfrom_url == NULL) - source_abspath = tmp_text_base_abspath; - - { - svn_boolean_t record_fileinfo; + /* Install the working copy file (with appropriate translation) from + the provided temporary file at SOURCE_ABSPATH. */ + SVN_ERR(svn_wc__wq_build_file_install(&work_item, + db, local_abspath, + source_abspath, + FALSE /* use_commit_times */, + TRUE /* record_fileinfo */, + pool, pool)); + all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); - /* If new contents were provided, then we do NOT want to record the - file information. We assume the new contents do not match the - "proper" values for RECORDED_SIZE and RECORDED_TIME. */ - record_fileinfo = (new_contents == NULL); - - /* Install the working copy file (with appropriate translation) from - the appropriate source. SOURCE_ABSPATH will be NULL, indicating an - installation from the pristine (available for copied/moved files), - or it will specify a temporary file where we placed a "pristine" - (for an added file) or a detranslated local-mods file. */ - SVN_ERR(svn_wc__wq_build_file_install(&work_item, - db, local_abspath, - source_abspath, - FALSE /* use_commit_times */, - record_fileinfo, - pool, pool)); - all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); - - /* If we installed from somewhere besides the official pristine, then - it is a temporary file, which needs to be removed. */ - if (source_abspath != NULL) - { - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath, - source_abspath, - pool, pool)); - all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); - } - } + SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath, + source_abspath, + pool, pool)); + all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath, new_base_props,
Modified: subversion/trunk/subversion/libsvn_wc/upgrade.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/upgrade.c?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/upgrade.c (original) +++ subversion/trunk/subversion/libsvn_wc/upgrade.c Tue Dec 13 09:49:29 2022 @@ -1074,7 +1074,7 @@ migrate_text_bases(apr_hash_t **text_bas /* Insert a row into the pristine table. */ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_INSERT_OR_IGNORE_PRISTINE)); + STMT_INSERT_OR_IGNORE_PRISTINE_F31)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool)); SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); @@ -2026,6 +2026,7 @@ svn_error_t * svn_wc__upgrade(svn_wc_context_t *wc_ctx, const char *local_abspath, int target_format, + svn_boolean_t store_pristine, svn_wc_upgrade_get_repos_info_t repos_info_func, void *repos_info_baton, svn_cancel_func_t cancel_func, @@ -2128,7 +2129,7 @@ svn_wc__upgrade(svn_wc_context_t *wc_ctx &data.repos_id, &data.wc_id, db, target_format, data.root_abspath, this_dir->repos, this_dir->uuid, - scratch_pool)); + store_pristine, scratch_pool)); /* Migrate the entries over to the new database. ### We need to think about atomicity here. Modified: subversion/trunk/subversion/libsvn_wc/util.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/util.c?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/util.c (original) +++ subversion/trunk/subversion/libsvn_wc/util.c Tue Dec 13 09:49:29 2022 @@ -37,6 +37,7 @@ #include "wc.h" /* just for prototypes of things in this .c file */ #include "entries.h" +#include "textbase.h" #include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -505,8 +506,9 @@ svn_wc__fetch_base_func(const char **fil return SVN_NO_ERROR; } - SVN_ERR(svn_wc__db_pristine_get_path(filename, sfb->db, local_abspath, - checksum, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__textbase_setaside(filename, sfb->db, local_abspath, + checksum, NULL, NULL, + result_pool, scratch_pool)); return SVN_NO_ERROR; } Modified: subversion/trunk/subversion/libsvn_wc/wc-metadata.sql URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-metadata.sql?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc-metadata.sql (original) +++ subversion/trunk/subversion/libsvn_wc/wc-metadata.sql Tue Dec 13 09:49:29 2022 @@ -708,12 +708,88 @@ WHERE l.op_depth = 0 /* ------------------------------------------------------------------------- */ -/* Format 32 is identical to format 31. - Its purpose is as a place-holder in developing and testing the - multi-wc-format support feature. */ +/* Format 32 adds support for optional pristine contents with the + following schema changes: + - Add the 'hydrated' column to the PRISTINE table. + - Add the I_PRISTINE_UNREFERENCED index. + - Add the TEXTBASE_REFS table. + - Add the SETTINGS table. */ -- STMT_UPGRADE_TO_32 +/* True iff the pristine contents are currently available on disk. */ +ALTER TABLE PRISTINE ADD COLUMN hydrated INTEGER NOT NULL DEFAULT 1; + + /* Note: we use checksums to detect if the file contents have been modified + in textbase.c and in the svn_wc__internal_file_modified_p() function. + + The new working copy format SHOULD incorporate a switch to a different + checksum type without known collisions. + + For the updated pristine table schema, we MAY want to add a new column + containing a checksum of the first 8KB of the file to allow saying that + the file is modified without reading all its content. That could speed + up the check for large modified files whose size did not change, for + example if they are allocated in certain extents. + */ + +CREATE INDEX I_PRISTINE_UNREFERENCED ON PRISTINE (refcount, refcount=0); + +/* This table contains references to the on disk text-base contents. + Every row corresponds to a row in NODES table with the same key. + While a row is present is this table, the contents identified by the + corresponding NODES.checksum cannot be dehydrated from the pristine store. + */ +CREATE TABLE TEXTBASE_REFS ( + /* Same key columns as in the NODES table */ + wc_id INTEGER NOT NULL, + local_relpath TEXT NOT NULL, + op_depth INTEGER NOT NULL, + + PRIMARY KEY (wc_id, local_relpath, op_depth) + ); + +DROP TRIGGER nodes_delete_trigger; + +CREATE TRIGGER nodes_delete_trigger +AFTER DELETE ON nodes +WHEN OLD.checksum IS NOT NULL +BEGIN + UPDATE pristine SET refcount = refcount - 1 + WHERE checksum = OLD.checksum; + DELETE FROM textbase_refs + WHERE wc_id = OLD.wc_id + AND local_relpath = OLD.local_relpath + AND op_depth = OLD.op_depth; +END; + +DROP TRIGGER nodes_update_checksum_trigger; + +CREATE TRIGGER nodes_update_checksum_trigger +AFTER UPDATE OF checksum ON nodes +WHEN NEW.checksum IS NOT OLD.checksum + /* AND (NEW.checksum IS NOT NULL OR OLD.checksum IS NOT NULL) */ +BEGIN + UPDATE pristine SET refcount = refcount + 1 + WHERE checksum = NEW.checksum; + UPDATE pristine SET refcount = refcount - 1 + WHERE checksum = OLD.checksum; + DELETE FROM textbase_refs + WHERE wc_id = OLD.wc_id + AND local_relpath = OLD.local_relpath + AND op_depth = OLD.op_depth; +END; + +/* This table contains settings of a working copy, identified by WC_ID. */ +CREATE TABLE SETTINGS ( + wc_id INTEGER NOT NULL REFERENCES WCROOT (id) PRIMARY KEY, + store_pristine INTEGER +); + PRAGMA user_version = 32; +/* ------------------------------------------------------------------------- */ +/* Format 33 .... */ +/* -- STMT_UPGRADE_TO_33 +PRAGMA user_version = 33; */ /* ------------------------------------------------------------------------- */ /* When bumping the format, also update: Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original) +++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Tue Dec 13 09:49:29 2022 @@ -883,23 +883,33 @@ SELECT id, work FROM work_queue ORDER BY -- STMT_DELETE_WORK_ITEM DELETE FROM work_queue WHERE id = ?1 --- STMT_INSERT_OR_IGNORE_PRISTINE +-- STMT_INSERT_OR_IGNORE_PRISTINE_F31 INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount) VALUES (?1, ?2, ?3, 0) --- STMT_INSERT_PRISTINE +-- STMT_INSERT_OR_IGNORE_PRISTINE_F32 +INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount, hydrated) +VALUES (?1, ?2, ?3, 0, ?4) + +-- STMT_UPSERT_PRISTINE_F31 INSERT INTO pristine (checksum, md5_checksum, size, refcount) VALUES (?1, ?2, ?3, 0) +ON CONFLICT(checksum) DO UPDATE SET size=?3 + +-- STMT_UPSERT_PRISTINE_F32 +INSERT INTO pristine (checksum, md5_checksum, size, refcount, hydrated) +VALUES (?1, ?2, ?3, 0, ?4) +ON CONFLICT(checksum) DO UPDATE SET size=?3, hydrated=?4 --- STMT_SELECT_PRISTINE -SELECT md5_checksum +-- STMT_SELECT_PRISTINE_F31 +SELECT md5_checksum, size, 1 FROM pristine WHERE checksum = ?1 --- STMT_SELECT_PRISTINE_SIZE -SELECT size +-- STMT_SELECT_PRISTINE_F32 +SELECT md5_checksum, size, hydrated FROM pristine -WHERE checksum = ?1 LIMIT 1 +WHERE checksum = ?1 -- STMT_SELECT_PRISTINE_BY_MD5 SELECT checksum @@ -915,9 +925,28 @@ WHERE refcount = 0 DELETE FROM pristine WHERE checksum = ?1 AND refcount = 0 --- STMT_SELECT_COPY_PRISTINES +-- STMT_SELECT_COPY_PRISTINES_F31 +/* For the root itself */ +SELECT n.checksum, md5_checksum, size, 1 +FROM nodes_current n +LEFT JOIN pristine p ON n.checksum = p.checksum +WHERE wc_id = ?1 + AND n.local_relpath = ?2 + AND n.checksum IS NOT NULL +UNION ALL +/* And all descendants */ +SELECT n.checksum, md5_checksum, size, 1 +FROM nodes n +LEFT JOIN pristine p ON n.checksum = p.checksum +WHERE wc_id = ?1 + AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2) + AND op_depth >= + (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2) + AND n.checksum IS NOT NULL + +-- STMT_SELECT_COPY_PRISTINES_F32 /* For the root itself */ -SELECT n.checksum, md5_checksum, size +SELECT n.checksum, md5_checksum, size, p.hydrated FROM nodes_current n LEFT JOIN pristine p ON n.checksum = p.checksum WHERE wc_id = ?1 @@ -925,7 +954,7 @@ WHERE wc_id = ?1 AND n.checksum IS NOT NULL UNION ALL /* And all descendants */ -SELECT n.checksum, md5_checksum, size +SELECT n.checksum, md5_checksum, size, p.hydrated FROM nodes n LEFT JOIN pristine p ON n.checksum = p.checksum WHERE wc_id = ?1 @@ -934,6 +963,10 @@ WHERE wc_id = ?1 (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2) AND n.checksum IS NOT NULL +-- STMT_UPDATE_PRISTINE_HYDRATED +UPDATE pristine SET hydrated = ?2 +WHERE checksum = ?1 + -- STMT_VACUUM VACUUM @@ -1812,6 +1845,61 @@ SELECT lock_token, lock_owner, lock_comm FROM lock WHERE repos_id = ?1 AND (repos_relpath = ?2) +-- STMT_TEXTBASE_ADD_REF +INSERT OR IGNORE INTO textbase_refs (wc_id, local_relpath, op_depth) +VALUES (?1, ?2, ?3) + +-- STMT_TEXTBASE_REMOVE_REF +DELETE FROM textbase_refs +WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 + +-- STMT_TEXTBASE_WALK +SELECT refs.wc_id IS NOT NULL, + nodes.local_relpath, nodes.op_depth, + nodes.checksum, nodes.properties, + nodes.translated_size, nodes.last_mod_time, + (SELECT properties FROM ACTUAL_NODE a + WHERE a.wc_id = ?1 + AND a.local_relpath = nodes.local_relpath + LIMIT 1), + (SELECT MAX(op_depth) + FROM NODES w + WHERE w.wc_id = ?1 + AND w.local_relpath = nodes.local_relpath) +FROM nodes +LEFT OUTER JOIN textbase_refs refs ON nodes.wc_id = refs.wc_id + AND nodes.local_relpath = refs.local_relpath + AND nodes.op_depth = refs.op_depth +WHERE nodes.wc_id = ?1 + AND (nodes.local_relpath = ?2 + OR IS_STRICT_DESCENDANT_OF(nodes.local_relpath, ?2)) + AND nodes.checksum IS NOT NULL + +-- STMT_TEXTBASE_SYNC +SELECT pristine.checksum, + MIN(pristine.hydrated != 0), + MAX(refs.wc_id IS NOT NULL), + nodes.repos_path, nodes.repos_id, nodes.revision +FROM nodes +JOIN pristine ON nodes.wc_id = ?1 + AND nodes.checksum = pristine.checksum + AND (nodes.local_relpath = ?2 + OR IS_STRICT_DESCENDANT_OF(nodes.local_relpath, ?2)) +LEFT OUTER JOIN textbase_refs refs ON nodes.wc_id = refs.wc_id + AND nodes.local_relpath = refs.local_relpath + AND nodes.op_depth = refs.op_depth +GROUP BY pristine.checksum +UNION ALL +SELECT pristine.checksum, pristine.hydrated, 0, NULL, NULL, NULL +FROM pristine WHERE refcount = 0 + +-- STMT_SELECT_SETTINGS +SELECT store_pristine FROM settings WHERE wc_id = ?1 + +-- STMT_UPSERT_SETTINGS +INSERT INTO settings (wc_id, store_pristine) +VALUES (?1, ?2) +ON CONFLICT(wc_id) DO UPDATE SET store_pristine=?2 /* ------------------------------------------------------------------------- */ Modified: subversion/trunk/subversion/libsvn_wc/wc.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc.h?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc.h (original) +++ subversion/trunk/subversion/libsvn_wc/wc.h Tue Dec 13 09:49:29 2022 @@ -162,11 +162,10 @@ extern "C" { * == 1.10.x shipped with format 31 * == 1.11.x shipped with format 31 * - * The bump to 32 is a no-op, used for testing the multi-wc-format branch. - * Downgrading from format 32 to format 31 is just a matter of running - * "PRAGMA user_version = 31;". + * The bump to 32 adds support for optional pristine contents; see the docstring + * of STMT_UPGRADE_TO_32 for details. * - * == 1.15.x shipped with format 32 + * == 1.15.x shipped with format 32 and multi-wc-format support * * Please document any further format changes here. */ @@ -220,6 +219,13 @@ extern "C" { sqlite_stat1 table on opening */ #define SVN_WC__ENSURE_STAT1_TABLE 31 +/* Starting from this version, pristine content is optional and can be + * fetched on demand. */ +#define SVN_WC__HAS_OPTIONAL_PRISTINE 32 + +/* Starting from this version, the DB stores per-WC settings. */ +#define SVN_WC__HAS_SETTINGS 32 + /* Return a string indicating the released version (or versions) of * Subversion that used WC format number WC_FORMAT, or some other * suitable string if no released version used WC_FORMAT. @@ -418,7 +424,7 @@ svn_wc__internal_file_modified_p(svn_boo Property changes sent by the update are provided in PROP_DIFF. - For a complete description, see svn_wc_merge5() for which this is + For a complete description, see svn_wc_merge6() for which this is the (loggy) implementation. *WORK_ITEMS will be allocated in RESULT_POOL. All temporary allocations @@ -504,7 +510,7 @@ svn_wc__conflicted_for_update_p(svn_bool apr_pool_t *scratch_pool); -/* Internal version of svn_wc_transmit_text_deltas3(). */ +/* Internal version of svn_wc_transmit_text_deltas4(). */ svn_error_t * svn_wc__internal_transmit_text_deltas(svn_stream_t *tempstream, const svn_checksum_t **new_text_base_md5_checksum, @@ -535,6 +541,7 @@ svn_wc__internal_ensure_adm(svn_wc__db_t const char *repos_uuid, svn_revnum_t revision, svn_depth_t depth, + svn_boolean_t store_pristine, apr_pool_t *scratch_pool); Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.c Tue Dec 13 09:49:29 2022 @@ -1366,6 +1366,7 @@ init_db(/* output values */ const char *root_node_repos_relpath, svn_revnum_t root_node_revision, svn_depth_t root_node_depth, + svn_boolean_t store_pristine, const char *wcroot_abspath, apr_pool_t *scratch_pool) { @@ -1389,6 +1390,13 @@ init_db(/* output values */ SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT)); SVN_ERR(svn_sqlite__insert(wc_id, stmt)); + if (target_format >= SVN_WC__HAS_SETTINGS) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPSERT_SETTINGS)); + SVN_ERR(svn_sqlite__bindf(stmt, "id", *wc_id, store_pristine)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + } + if (root_node_repos_relpath) { svn_wc__db_status_t status = svn_wc__db_status_normal; @@ -1436,6 +1444,7 @@ create_db(svn_sqlite__db_t **sdb, const char *root_node_repos_relpath, svn_revnum_t root_node_revision, svn_depth_t root_node_depth, + svn_boolean_t store_pristine, svn_boolean_t exclusive, apr_int32_t timeout, apr_pool_t *result_pool, @@ -1450,7 +1459,8 @@ create_db(svn_sqlite__db_t **sdb, SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id, *sdb, target_format, repos_root_url, repos_uuid, root_node_repos_relpath, root_node_revision, - root_node_depth, dir_abspath, scratch_pool), + root_node_depth, store_pristine, dir_abspath, + scratch_pool), *sdb); return SVN_NO_ERROR; @@ -1466,6 +1476,7 @@ svn_wc__db_init(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t initial_rev, svn_depth_t depth, + svn_boolean_t store_pristine, apr_pool_t *scratch_pool) { svn_sqlite__db_t *sdb; @@ -1495,7 +1506,7 @@ svn_wc__db_init(svn_wc__db_t *db, /* Create the SDB and insert the basic rows. */ SVN_ERR(create_db(&sdb, &repos_id, &wc_id, target_format, local_abspath, repos_root_url, repos_uuid, SDB_FILE, - repos_relpath, initial_rev, depth, + repos_relpath, initial_rev, depth, store_pristine, sqlite_exclusive, sqlite_timeout, db->state_pool, scratch_pool)); @@ -1504,6 +1515,7 @@ svn_wc__db_init(svn_wc__db_t *db, apr_pstrdup(db->state_pool, local_abspath), sdb, wc_id, FORMAT_FROM_SDB, FALSE /* auto-upgrade */, + store_pristine, db->state_pool, scratch_pool)); /* Any previously cached children may now have a new WCROOT, most likely that @@ -1537,10 +1549,11 @@ svn_wc__db_init(svn_wc__db_t *db, svn_error_t * -svn_wc__db_get_format(int *format, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool) +svn_wc__db_get_settings(int *format_p, + svn_boolean_t *store_pristine_p, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; @@ -1552,7 +1565,11 @@ svn_wc__db_get_format(int *format, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - *format = wcroot->format; + if (format_p) + *format_p = wcroot->format; + if (store_pristine_p) + *store_pristine_p = wcroot->store_pristine; + return SVN_NO_ERROR; } @@ -13442,6 +13459,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_ const char *dir_abspath, const char *repos_root_url, const char *repos_uuid, + svn_boolean_t store_pristine, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; @@ -13451,6 +13469,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_ dir_abspath, repos_root_url, repos_uuid, SDB_FILE, NULL, SVN_INVALID_REVNUM, svn_depth_unknown, + store_pristine, TRUE /* exclusive */, 0 /* timeout */, wc_db->state_pool, scratch_pool)); @@ -13460,6 +13479,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_ dir_abspath), *sdb, *wc_id, FORMAT_FROM_SDB, FALSE /* auto-upgrade */, + store_pristine, wc_db->state_pool, scratch_pool)); /* The WCROOT is complete. Stash it into DB. */ Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db.h (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db.h Tue Dec 13 09:49:29 2022 @@ -310,17 +310,20 @@ svn_wc__db_init(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t initial_rev, svn_depth_t depth, + svn_boolean_t store_pristine, apr_pool_t *scratch_pool); -/* Return the working copy format for LOCAL_ABSPATH in DB in *FORMAT. +/* Return the working copy settings *FORMAT_P and *STORE_PRISTINE_P for + LOCAL_ABSPATH in DB. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * -svn_wc__db_get_format(int *format, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool); +svn_wc__db_get_settings(int *format_p, + svn_boolean_t *store_pristine_p, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool); /* Compute the LOCAL_RELPATH for the given LOCAL_ABSPATH, relative from wri_abspath. @@ -927,21 +930,6 @@ svn_wc__db_base_get_lock_tokens_recursiv @{ */ -/* Set *PRISTINE_ABSPATH to the path to the pristine text file - identified by SHA1_CHECKSUM. Error if it does not exist. - - ### This is temporary - callers should not be looking at the file - directly. - - Allocate the path in RESULT_POOL. */ -svn_error_t * -svn_wc__db_pristine_get_path(const char **pristine_abspath, - svn_wc__db_t *db, - const char *wri_abspath, - const svn_checksum_t *checksum, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /* Set *PRISTINE_ABSPATH to the path under WCROOT_ABSPATH that will be used by the pristine text identified by SHA1_CHECKSUM. The file need not exist. @@ -956,7 +944,8 @@ svn_wc__db_pristine_get_future_path(cons /* If requested set *CONTENTS to a readable stream that will yield the pristine text identified by SHA1_CHECKSUM (must be a SHA-1 checksum) within the WC - identified by WRI_ABSPATH in DB. + identified by WRI_ABSPATH in DB. If the pristine is present in the store, + but dehydrated, set *CONTENTS to NULL. If requested set *SIZE to the size of the pristine stream in bytes, @@ -986,6 +975,9 @@ typedef struct svn_wc__db_install_data_t set to the MD-5 and SHA-1 checksums respectively of that file. MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted. + The contents of the pristine will be saved to disk if HYDRATED is true or + if the WC version or configuration doesn't allow dehydrated pristines. + Allocate the new stream, path and checksums in RESULT_POOL. */ svn_error_t * @@ -995,6 +987,7 @@ svn_wc__db_pristine_prepare_install(svn_ svn_checksum_t **md5_checksum, svn_wc__db_t *db, const char *wri_abspath, + svn_boolean_t hydrated, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -1077,14 +1070,26 @@ svn_wc__db_pristine_cleanup(svn_wc__db_t /* Set *PRESENT to true if the pristine store for WRI_ABSPATH in DB contains a pristine text with SHA-1 checksum SHA1_CHECKSUM, and to false otherwise. -*/ + If the pristine is present, set *HYDRATED to true if its contents are + currently available on disk, and to false otherwise. If the pristine + is not present, set *HYDRATED to false. */ svn_error_t * svn_wc__db_pristine_check(svn_boolean_t *present, + svn_boolean_t *hydrated, svn_wc__db_t *db, const char *wri_abspath, const svn_checksum_t *sha1_checksum, apr_pool_t *scratch_pool); +/* If the pristine store for WRI_ABSPATH in DB contains a pristine text with + SHA-1 checksum SHA1_CHECKSUM with its content available on disk, remove + that content and mark the pristine entry as "dehydrated". */ +svn_error_t * +svn_wc__db_pristine_dehydrate(svn_wc__db_t *db, + const char *wri_abspath, + const svn_checksum_t *sha1_checksum, + apr_pool_t *scratch_pool); + /* @defgroup svn_wc__db_external External management @{ */ @@ -2960,6 +2965,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_ const char *local_dir_abspath, const char *repos_root_url, const char *repos_uuid, + svn_boolean_t store_pristine, apr_pool_t *scratch_pool); /* Simply insert (or replace) one row in the EXTERNALS table. */ @@ -3102,6 +3108,77 @@ svn_wc__db_wclock_owns_lock(svn_boolean_ svn_boolean_t exact, apr_pool_t *scratch_pool); +/* @} */ + + +/* @defgroup svn_wc__db_textbase Working with text-bases + @{ +*/ + +/* The callback invoked by svn_wc__db_textbase_walk(). */ +typedef svn_error_t * (*svn_wc__db_textbase_walk_cb_t)( + svn_boolean_t *referenced_p, + void *baton, + const char *local_abspath, + int op_depth, + const svn_checksum_t *checksum, + svn_boolean_t have_props, + svn_boolean_t props_mod, + svn_filesize_t recorded_size, + apr_time_t recorded_time, + int max_op_depth, + apr_pool_t *scratch_pool); + +/* Walk the text-bases referenced by the nodes in the LOCAL_ABSPATH + tree, invoking the CALLBACK with information about each text-base + for a node. + + If the callback sets *REFERENCED_P to true, ensure that a text-base + reference exists for the node. If the callback sets *REFERENCED_P to + false, ensure that a text-base reference does not exist for the node. + + See the description of the `TEXTBASE_REFS` table in the schema. + */ +svn_error_t * +svn_wc__db_textbase_walk(svn_wc__db_t *db, + const char *local_abspath, + svn_wc__db_textbase_walk_cb_t callback, + void *callback_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* The callback invoked by svn_wc__db_textbase_sync(). */ +typedef svn_error_t * (*svn_wc__db_textbase_fetch_cb_t)( + void *baton, + const char *repos_root_url, + const char *repos_relpath, + svn_revnum_t revision, + svn_stream_t *contents, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + +/* Synchronize the state of the text-bases in DB. + + If ALLOW_HYDRATE is true, fetch the referenced but missing text-base + contents using the provided FETCH_CALLBACK and FETCH_BATON. + If ALLOW_DEHYDRATE is true, remove the on disk text-base contents + that is no longer referenced. + */ +svn_error_t * +svn_wc__db_textbase_sync(svn_wc__db_t *db, + const char *local_abspath, + svn_boolean_t allow_hydrate, + svn_boolean_t allow_dehydrate, + svn_wc__db_textbase_fetch_cb_t fetch_callback, + void *fetch_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); + + +/* @} */ /* @defgroup svn_wc__db_temp Various temporary functions during transition Modified: subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c Tue Dec 13 09:49:29 2022 @@ -94,50 +94,6 @@ get_pristine_fname(const char **pristine svn_error_t * -svn_wc__db_pristine_get_path(const char **pristine_abspath, - svn_wc__db_t *db, - const char *wri_abspath, - const svn_checksum_t *sha1_checksum, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - svn_boolean_t present; - - SVN_ERR_ASSERT(pristine_abspath != NULL); - SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); - SVN_ERR_ASSERT(sha1_checksum != NULL); - /* ### Transitional: accept MD-5 and look up the SHA-1. Return an error - * if the pristine text is not in the store. */ - if (sha1_checksum->kind != svn_checksum_sha1) - SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath, - sha1_checksum, - scratch_pool, scratch_pool)); - SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, wri_abspath, - scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - - SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum, - scratch_pool)); - if (! present) - return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL, - _("The pristine text with checksum '%s' was " - "not found"), - svn_checksum_to_cstring_display(sha1_checksum, - scratch_pool)); - - SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath, - sha1_checksum, - result_pool, scratch_pool)); - - return SVN_NO_ERROR; -} - -svn_error_t * svn_wc__db_pristine_get_future_path(const char **pristine_abspath, const char *wcroot_abspath, const svn_checksum_t *sha1_checksum, @@ -150,11 +106,58 @@ svn_wc__db_pristine_get_future_path(cons return SVN_NO_ERROR; } +/* Read *HAVE_ROW, *MD5_CHECKSUM, *SIZE, *HYDRATED for WCROOT:SHA1_CHECKSUM. + * If it returns with *HAVE_ROW=FALSE, other outputs are null/zero/false. + */ +static svn_error_t * +stmt_select_pristine(svn_boolean_t *have_row, + const svn_checksum_t **md5_checksum, + svn_filesize_t *size, + svn_boolean_t *hydrated, + svn_wc__db_wcroot_t *wcroot, + const svn_checksum_t *sha1_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + int stmt_num; + svn_sqlite__stmt_t *stmt; + + /* Check that this pristine text is present in the store. (The presence + * of the file is not sufficient.) */ + stmt_num = (wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE + ? STMT_SELECT_PRISTINE_F32 + : STMT_SELECT_PRISTINE_F31); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_num)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__step(have_row, stmt)); + if (!*have_row) + { + if (md5_checksum) + *md5_checksum = NULL; + if (size) + *size = 0; + if (hydrated) + *hydrated = FALSE; + return svn_sqlite__reset(stmt); + } + + if (md5_checksum) + SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool)); + if (size) + *size = svn_sqlite__column_int64(stmt, 1); + if (hydrated) + *hydrated = svn_sqlite__column_boolean(stmt, 2); + + SVN_ERR(svn_sqlite__reset(stmt)); + return SVN_NO_ERROR; +} + /* Set *CONTENTS to a readable stream from which the pristine text * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the - * pristine store of WCROOT. If SIZE is not null, set *SIZE to the size - * in bytes of that text. If that text is not in the pristine store, - * return an error. + * pristine store of WCROOT. If the pristine contents are currently not + * available on disk, set *CONTENTS to NULL. If SIZE is not null, set + * *SIZE to the size in bytes of that text. If that text is not in + * the pristine store, return an error. * * Even if the pristine text is removed from the store while it is being * read, the stream will remain valid and readable until it is closed. @@ -174,20 +177,14 @@ pristine_read_txn(svn_stream_t **content apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + svn_boolean_t hydrated; /* Check that this pristine text is present in the store. (The presence * of the file is not sufficient.) */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_PRISTINE_SIZE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - - if (size) - *size = svn_sqlite__column_int64(stmt, 0); - - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(stmt_select_pristine(&have_row, NULL, size, &hydrated, + wcroot, sha1_checksum, + result_pool, scratch_pool)); if (! have_row) { return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, @@ -196,19 +193,27 @@ pristine_read_txn(svn_stream_t **content sha1_checksum, scratch_pool)); } - /* Open the file as a readable stream. It will remain readable even when - * deleted from disk; APR guarantees that on Windows as well as Unix. - * - * We also don't enable APR_BUFFERED on this file to maximize throughput - * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers - * where needed in streams, there is no point in having another layer of - * buffers. */ if (contents) { - apr_file_t *file; - SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ, - APR_OS_DEFAULT, result_pool)); - *contents = svn_stream_from_aprfile2(file, FALSE, result_pool); + if (hydrated) + { + /* Open the file as a readable stream. It will remain readable even when + * deleted from disk; APR guarantees that on Windows as well as Unix. + * + * We also don't enable APR_BUFFERED on this file to maximize throughput + * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers + * where needed in streams, there is no point in having another layer of + * buffers. */ + + apr_file_t *file; + SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ, + APR_OS_DEFAULT, result_pool)); + *contents = svn_stream_from_aprfile2(file, FALSE, result_pool); + } + else + { + *contents = NULL; + } } return SVN_NO_ERROR; @@ -256,6 +261,53 @@ svn_wc__db_pristine_read(svn_stream_t ** } +struct svn_wc__db_install_data_t +{ + svn_wc__db_wcroot_t *wcroot; + svn_stream_t *inner_stream; + apr_off_t size; +}; + +static svn_error_t * +install_stream_write_fn(void *baton, const char *data, apr_size_t *len) +{ + svn_wc__db_install_data_t *install_data = baton; + + if (install_data->inner_stream) + SVN_ERR(svn_stream_write(install_data->inner_stream, data, len)); + + install_data->size += *len; + + return SVN_NO_ERROR; +} + +static svn_error_t * +install_stream_seek_fn(void *baton, const svn_stream_mark_t *mark) +{ + svn_wc__db_install_data_t *install_data = baton; + + if (!mark) + return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL); + + if (install_data->inner_stream) + SVN_ERR(svn_stream_reset(install_data->inner_stream)); + + install_data->size = 0; + + return SVN_NO_ERROR; +} + +static svn_error_t * +install_stream_close_fn(void *baton) +{ + svn_wc__db_install_data_t *install_data = baton; + + if (install_data->inner_stream) + SVN_ERR(svn_stream_close(install_data->inner_stream)); + + return SVN_NO_ERROR; +} + /* Return the absolute path to the temporary directory for pristine text files within WCROOT. */ static char * @@ -268,9 +320,8 @@ pristine_get_tempdir(svn_wc__db_wcroot_t PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL); } -/* Install the pristine text described by BATON into the pristine store of - * SDB. If it is already stored then just delete the new file - * BATON->tempfile_abspath. +/* Install the pristine text described by INSTALL_DATA into the pristine store + * of SDB. * * This function expects to be executed inside a SQLite txn that has already * acquired a 'RESERVED' lock. @@ -278,9 +329,8 @@ pristine_get_tempdir(svn_wc__db_wcroot_t * Implements 'notes/wc-ng/pristine-store' section A-3(a). */ static svn_error_t * -pristine_install_txn(svn_sqlite__db_t *sdb, - /* The path to the source file that is to be moved into place. */ - svn_stream_t *install_stream, +pristine_install_txn(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_install_data_t *install_data, /* The target path for the file (within the pristine store). */ const char *pristine_abspath, /* The pristine text's SHA-1 checksum. */ @@ -289,88 +339,70 @@ pristine_install_txn(svn_sqlite__db_t *s const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool) { + svn_stream_t *install_stream = install_data->inner_stream; + int stmt_num; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + svn_boolean_t hydrated; - /* If this pristine text is already present in the store, just keep it: - * delete the new one and return. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(stmt_select_pristine(&have_row, NULL, NULL, &hydrated, + wcroot, sha1_checksum, + scratch_pool, scratch_pool)); - if (have_row) + if (have_row && hydrated) { -#ifdef SVN_DEBUG - /* Consistency checks. Verify both files exist and match. - * ### We could check much more. */ - { - apr_finfo_t finfo; - apr_off_t size; - - SVN_ERR(svn_stream__install_finalize(NULL, &size, install_stream, - scratch_pool)); - - SVN_ERR(svn_io_stat(&finfo, pristine_abspath, APR_FINFO_SIZE, - scratch_pool)); - if (size != finfo.size) - { - return svn_error_createf( - SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL, - _("New pristine text '%s' has different size: %s versus %s"), - svn_checksum_to_cstring_display(sha1_checksum, scratch_pool), - apr_off_t_toa(scratch_pool, size), - apr_off_t_toa(scratch_pool, finfo.size)); - } - } -#endif + /* For now, ensure that we do not inadvertently dehydrate an existing + * hydrated entry, as there could be references to its content. */ + + if (install_stream) + SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool)); - /* Remove the temp file: it's already there */ - SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool)); return SVN_NO_ERROR; } - /* Move the file to its target location. (If it is already there, it is - * an orphan file and it doesn't matter if we overwrite it.) */ - { - apr_off_t size; + if (install_stream) + { + /* Move the file to its target location. (If it is already there, it is + * an orphan file and it doesn't matter if we overwrite it.) */ - svn_stream__install_set_read_only(install_stream, TRUE); + svn_stream__install_set_read_only(install_stream, TRUE); - SVN_ERR(svn_stream__install_finalize(NULL, &size, install_stream, - scratch_pool)); - SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath, - TRUE, scratch_pool)); - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size)); - SVN_ERR(svn_sqlite__insert(NULL, stmt)); - } + SVN_ERR(svn_stream__install_finalize(NULL, NULL, install_stream, + scratch_pool)); + SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath, + TRUE, scratch_pool)); + } + else + { + SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool)); + } + + stmt_num = (wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE + ? STMT_UPSERT_PRISTINE_F32 : STMT_UPSERT_PRISTINE_F31); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_num)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_int64(stmt, 3, install_data->size)); + if (wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE) + SVN_ERR(svn_sqlite__bind_int(stmt, 4, install_stream != NULL)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); return SVN_NO_ERROR; } -struct svn_wc__db_install_data_t -{ - svn_wc__db_wcroot_t *wcroot; - svn_stream_t *inner_stream; -}; - svn_error_t * -svn_wc__db_pristine_prepare_install(svn_stream_t **stream, - svn_wc__db_install_data_t **install_data, - svn_checksum_t **sha1_checksum, - svn_checksum_t **md5_checksum, +svn_wc__db_pristine_prepare_install(svn_stream_t **stream_p, + svn_wc__db_install_data_t **install_data_p, + svn_checksum_t **sha1_checksum_p, + svn_checksum_t **md5_checksum_p, svn_wc__db_t *db, const char *wri_abspath, + svn_boolean_t hydrated, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - const char *temp_dir_abspath; SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); @@ -378,24 +410,60 @@ svn_wc__db_pristine_prepare_install(svn_ wri_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); + SVN_ERR(svn_wc__db_pristine_prepare_install_internal( + stream_p, install_data_p, sha1_checksum_p, md5_checksum_p, + wcroot, hydrated, result_pool, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_pristine_prepare_install_internal(svn_stream_t **stream_p, + svn_wc__db_install_data_t **install_data_p, + svn_checksum_t **sha1_checksum_p, + svn_checksum_t **md5_checksum_p, + svn_wc__db_wcroot_t *wcroot, + svn_boolean_t hydrated, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stream_t *stream; + svn_wc__db_install_data_t *install_data; + const char *temp_dir_abspath; + temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool); - *install_data = apr_pcalloc(result_pool, sizeof(**install_data)); - (*install_data)->wcroot = wcroot; + install_data = apr_pcalloc(result_pool, sizeof(*install_data)); + install_data->wcroot = wcroot; - SVN_ERR_W(svn_stream__create_for_install(stream, - temp_dir_abspath, - result_pool, scratch_pool), - _("Unable to create pristine install stream")); + if (hydrated || wcroot->store_pristine) + { + SVN_ERR_W(svn_stream__create_for_install(&install_data->inner_stream, + temp_dir_abspath, + result_pool, scratch_pool), + _("Unable to create pristine install stream")); + } + else + { + install_data->inner_stream = NULL; + } - (*install_data)->inner_stream = *stream; + install_data->size = 0; - if (md5_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum, - svn_checksum_md5, FALSE, result_pool); - if (sha1_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum, - svn_checksum_sha1, FALSE, result_pool); + stream = svn_stream_create(install_data, result_pool); + svn_stream_set_write(stream, install_stream_write_fn); + svn_stream_set_seek(stream, install_stream_seek_fn); + svn_stream_set_close(stream, install_stream_close_fn); + + if (md5_checksum_p) + stream = svn_stream_checksummed2(stream, NULL, md5_checksum_p, + svn_checksum_md5, FALSE, result_pool); + if (sha1_checksum_p) + stream = svn_stream_checksummed2(stream, NULL, sha1_checksum_p, + svn_checksum_sha1, FALSE, result_pool); + + *stream_p = stream; + *install_data_p = install_data; return SVN_NO_ERROR; } @@ -421,8 +489,8 @@ svn_wc__db_pristine_install(svn_wc__db_i /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking * at the disk, to ensure no concurrent pristine install/delete txn. */ SVN_SQLITE__WITH_IMMEDIATE_TXN( - pristine_install_txn(wcroot->sdb, - install_data->inner_stream, pristine_abspath, + pristine_install_txn(wcroot, + install_data, pristine_abspath, sha1_checksum, md5_checksum, scratch_pool), wcroot->sdb); @@ -434,8 +502,12 @@ svn_error_t * svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_stream__install_delete(install_data->inner_stream, - scratch_pool)); + if (install_data->inner_stream) + SVN_ERR(svn_stream__install_delete(install_data->inner_stream, scratch_pool)); + + install_data->size = 0; + + return SVN_NO_ERROR; } @@ -449,7 +521,6 @@ svn_wc__db_pristine_get_md5(const svn_ch { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); @@ -460,20 +531,19 @@ svn_wc__db_pristine_get_md5(const svn_ch wri_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + SVN_ERR(stmt_select_pristine(&have_row, md5_checksum, NULL, NULL, + wcroot, sha1_checksum, + result_pool, scratch_pool)); if (!have_row) - return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt), + return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL, _("The pristine text with checksum '%s' was " "not found"), svn_checksum_to_cstring_display(sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool)); SVN_ERR_ASSERT((*md5_checksum)->kind == svn_checksum_md5); - return svn_error_trace(svn_sqlite__reset(stmt)); + return SVN_NO_ERROR; } @@ -516,84 +586,95 @@ svn_wc__db_pristine_get_sha1(const svn_c } /* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing - pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM and SIZE */ + pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM, SIZE and + HYDRATED. */ static svn_error_t * maybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot, svn_wc__db_wcroot_t *dst_wcroot, const svn_checksum_t *checksum, const svn_checksum_t *md5_checksum, apr_int64_t size, + svn_boolean_t hydrated, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { - const char *pristine_abspath; + int stmt_num; svn_sqlite__stmt_t *stmt; - svn_stream_t *src_stream; - svn_stream_t *dst_stream; - const char *tmp_abspath; - const char *src_abspath; int affected_rows; - svn_error_t *err; - SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb, - STMT_INSERT_OR_IGNORE_PRISTINE)); + stmt_num = (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE + ? STMT_INSERT_OR_IGNORE_PRISTINE_F32 + : STMT_INSERT_OR_IGNORE_PRISTINE_F31); + SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb, stmt_num)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size)); + if (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE) + SVN_ERR(svn_sqlite__bind_int(stmt, 4, hydrated)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); if (affected_rows == 0) return SVN_NO_ERROR; - SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath, - pristine_get_tempdir(dst_wcroot, - scratch_pool, - scratch_pool), - svn_io_file_del_on_pool_cleanup, - scratch_pool, scratch_pool)); + if (hydrated) + { + const char *pristine_abspath; + svn_stream_t *src_stream; + svn_stream_t *dst_stream; + const char *tmp_abspath; + const char *src_abspath; + svn_error_t *err; - SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum, - scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath, + pristine_get_tempdir(dst_wcroot, + scratch_pool, + scratch_pool), + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); - SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum, + scratch_pool, scratch_pool)); - /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */ - SVN_ERR(svn_stream_copy3(src_stream, dst_stream, - cancel_func, cancel_baton, - scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum, - scratch_pool, scratch_pool)); + /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */ + SVN_ERR(svn_stream_copy3(src_stream, dst_stream, + cancel_func, cancel_baton, + scratch_pool)); - /* Move the file to its target location. (If it is already there, it is - * an orphan file and it doesn't matter if we overwrite it.) */ - err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE, - scratch_pool); - - /* Maybe the directory doesn't exist yet? */ - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_t *err2; - - err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath, - scratch_pool), - APR_OS_DEFAULT, scratch_pool); - - if (err2) - /* Creating directory didn't work: Return all errors */ - return svn_error_trace(svn_error_compose_create(err, err2)); - else - /* We could create a directory: retry install */ - svn_error_clear(err); + SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum, + scratch_pool, scratch_pool)); - SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE, - scratch_pool)); + /* Move the file to its target location. (If it is already there, it is + * an orphan file and it doesn't matter if we overwrite it.) */ + err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE, + scratch_pool); + + /* Maybe the directory doesn't exist yet? */ + if (err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_t *err2; + + err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath, + scratch_pool), + APR_OS_DEFAULT, scratch_pool); + + if (err2) + /* Creating directory didn't work: Return all errors */ + return svn_error_trace(svn_error_compose_create(err, err2)); + else + /* We could create a directory: retry install */ + svn_error_clear(err); + + SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE, + scratch_pool)); + } + else + SVN_ERR(err); } - else - SVN_ERR(err); return SVN_NO_ERROR; } @@ -609,12 +690,15 @@ pristine_transfer_txn(svn_wc__db_wcroot_ void *cancel_baton, apr_pool_t *scratch_pool) { + int stmt_num; svn_sqlite__stmt_t *stmt; svn_boolean_t got_row; apr_pool_t *iterpool = svn_pool_create(scratch_pool); - SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb, - STMT_SELECT_COPY_PRISTINES)); + stmt_num = (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE + ? STMT_SELECT_COPY_PRISTINES_F32 + : STMT_SELECT_COPY_PRISTINES_F31); + SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb, stmt_num)); SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath)); /* This obtains an sqlite read lock on src_wcroot */ @@ -625,6 +709,7 @@ pristine_transfer_txn(svn_wc__db_wcroot_ const svn_checksum_t *checksum; const svn_checksum_t *md5_checksum; apr_int64_t size; + svn_boolean_t hydrated; svn_error_t *err; svn_pool_clear(iterpool); @@ -632,9 +717,11 @@ pristine_transfer_txn(svn_wc__db_wcroot_ SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool)); SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool)); size = svn_sqlite__column_int64(stmt, 2); + hydrated = svn_sqlite__column_boolean(stmt, 3); err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot, checksum, md5_checksum, size, + hydrated, cancel_func, cancel_baton, iterpool); @@ -714,19 +801,7 @@ pristine_remove_if_unreferenced_txn(svn_ /* If we removed the DB row, then remove the file. */ if (affected_rows > 0) - { - /* If the file is not present, something has gone wrong, but at this - * point it no longer matters. In a debug build, raise an error, but - * in a release build, it is more helpful to ignore it and continue. */ -#ifdef SVN_DEBUG - svn_boolean_t ignore_enoent = FALSE; -#else - svn_boolean_t ignore_enoent = TRUE; -#endif - - SVN_ERR(svn_io_remove_file2(pristine_abspath, ignore_enoent, - scratch_pool)); - } + SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool)); return SVN_NO_ERROR; } @@ -887,6 +962,7 @@ svn_wc__db_pristine_cleanup(svn_wc__db_t svn_error_t * svn_wc__db_pristine_check(svn_boolean_t *present, + svn_boolean_t *hydrated, svn_wc__db_t *db, const char *wri_abspath, const svn_checksum_t *sha1_checksum, @@ -894,7 +970,6 @@ svn_wc__db_pristine_check(svn_boolean_t { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); @@ -903,6 +978,9 @@ svn_wc__db_pristine_check(svn_boolean_t if (sha1_checksum->kind != svn_checksum_sha1) { *present = FALSE; + if (hydrated) + *hydrated = FALSE; + return SVN_NO_ERROR; } @@ -910,46 +988,58 @@ svn_wc__db_pristine_check(svn_boolean_t wri_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - /* A filestat is much cheaper than a sqlite transaction especially on NFS, - so first check if there is a pristine file and then if we are allowed - to use it. */ - { - const char *pristine_abspath; - svn_node_kind_t kind_on_disk; - svn_error_t *err; - - SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath, - sha1_checksum, scratch_pool, scratch_pool)); - err = svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool); -#ifdef WIN32 - if (err && err->apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED)) - { - svn_error_clear(err); - /* Possible race condition: The filename is locked, but there is no - file or dir with this name. Let's fall back on checking the DB. - - This case is triggered by the pristine store tests on deleting - a file that is still open via another handle, where this other - handle has a FILE_SHARE_DELETE share mode. - */ - } - else -#endif - if (err) - return svn_error_trace(err); - else if (kind_on_disk != svn_node_file) - { - *present = FALSE; - return SVN_NO_ERROR; - } - } - /* Check that there is an entry in the PRISTINE table. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE)); + SVN_ERR(stmt_select_pristine(&have_row, NULL, NULL, hydrated, + wcroot, sha1_checksum, + scratch_pool, scratch_pool)); + *present = have_row; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc__db_pristine_dehydrate(svn_wc__db_t *db, + const char *wri_abspath, + const svn_checksum_t *sha1_checksum, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + wri_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(svn_wc__db_pristine_dehydrate_internal(wcroot, sha1_checksum, + scratch_pool)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc__db_pristine_dehydrate_internal(svn_wc__db_wcroot_t *wcroot, + const svn_checksum_t *sha1_checksum, + apr_pool_t *scratch_pool) +{ + const char *pristine_abspath; + svn_sqlite__stmt_t *stmt; + + SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1); + + SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath, + sha1_checksum, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_PRISTINE_HYDRATED)); SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__bind_int(stmt, 2, FALSE)); + SVN_ERR(svn_sqlite__update(NULL, stmt)); + + SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool)); - *present = have_row; return SVN_NO_ERROR; } Modified: subversion/trunk/subversion/libsvn_wc/wc_db_private.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_private.h?rev=1905955&r1=1905954&r2=1905955&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/wc_db_private.h (original) +++ subversion/trunk/subversion/libsvn_wc/wc_db_private.h Tue Dec 13 09:49:29 2022 @@ -107,6 +107,10 @@ typedef struct svn_wc__db_wcroot_t { const char *local_abspath -> svn_wc_adm_access_t *adm_access */ apr_hash_t *access_cache; + /* Whether to store the pristine contents of all files on disk or + to fetch the contents on demand. */ + svn_boolean_t store_pristine; + } svn_wc__db_wcroot_t; @@ -126,6 +130,7 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_ apr_int64_t wc_id, int format, svn_boolean_t verify_format, + svn_boolean_t store_pristine, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -561,4 +566,23 @@ svn_wc__db_verify_db_full_internal(svn_w void *baton, apr_pool_t *scratch_pool); +/* Like svn_wc__db_pristine_prepare_install() but taking WCROOT instead + of DB+WRI_ABSPATH. */ +svn_error_t * +svn_wc__db_pristine_prepare_install_internal(svn_stream_t **stream_p, + svn_wc__db_install_data_t **install_data_p, + svn_checksum_t **sha1_checksum_p, + svn_checksum_t **md5_checksum_p, + svn_wc__db_wcroot_t *wcroot, + svn_boolean_t hydrated, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Like svn_wc__db_pristine_dehydrate() but taking WCROOT instead + of DB+WRI_ABSPATH. */ +svn_error_t * +svn_wc__db_pristine_dehydrate_internal(svn_wc__db_wcroot_t *wcroot, + const svn_checksum_t *sha1_checksum, + apr_pool_t *scratch_pool); + #endif /* WC_DB_PRIVATE_H */