Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-metadata.sql URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-metadata.sql?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-metadata.sql (original) +++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-metadata.sql Fri Aug 27 13:54:33 2021 @@ -104,10 +104,27 @@ CREATE TABLE PRISTINE ( /* Alternative MD5 checksum used for communicating with older repositories. Not strictly guaranteed to be unique among table rows. */ - md5_checksum TEXT NOT NULL + md5_checksum TEXT NOT NULL, + + /* True iff the pristine contents are currently available on disk. */ + hydrated INTEGER NOT NULL + + /* 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_MD5 ON PRISTINE (md5_checksum); +CREATE INDEX I_PRISTINE_UNREFERENCED ON PRISTINE (refcount, refcount=0); /* ------------------------------------------------------------------------- */ @@ -511,6 +528,10 @@ 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; CREATE TRIGGER nodes_update_checksum_trigger @@ -522,6 +543,10 @@ BEGIN 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; CREATE TABLE EXTERNALS ( @@ -562,6 +587,22 @@ CREATE UNIQUE INDEX I_EXTERNALS_DEFINED def_local_relpath, local_relpath); +/* ------------------------------------------------------------------------- */ + +/* 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) + ); + PRAGMA user_version = -- define: SVN_WC__VERSION @@ -697,11 +738,64 @@ WHERE l.op_depth = 0 AND ((l.repos_id IS NOT r.repos_id) OR (l.repos_path IS NOT RELPATH_SKIP_JOIN(r.local_relpath, r.repos_path, l.local_relpath))) +/* ------------------------------------------------------------------------- */ + +/* Format 32 adds support for optional text-base 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. */ +-- STMT_UPGRADE_TO_32 +ALTER TABLE PRISTINE ADD COLUMN hydrated INTEGER NOT NULL DEFAULT 1; + +CREATE INDEX I_PRISTINE_UNREFERENCED ON PRISTINE (refcount, refcount=0); + +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; + +PRAGMA user_version = 32; /* ------------------------------------------------------------------------- */ -/* Format 32 .... */ -/* -- STMT_UPGRADE_TO_32 -PRAGMA user_version = 32; */ +/* Format 33 .... */ +/* -- STMT_UPGRADE_TO_33 +PRAGMA user_version = 33; */ /* ------------------------------------------------------------------------- */
Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-queries.sql URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-queries.sql?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-queries.sql (original) +++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-queries.sql Fri Aug 27 13:54:33 2021 @@ -884,23 +884,22 @@ SELECT id, work FROM work_queue ORDER BY DELETE FROM work_queue WHERE id = ?1 -- STMT_INSERT_OR_IGNORE_PRISTINE -INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount) -VALUES (?1, ?2, ?3, 0) +INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount, hydrated) +VALUES (?1, ?2, ?3, 0, ?4) --- STMT_INSERT_PRISTINE -INSERT INTO pristine (checksum, md5_checksum, size, refcount) -VALUES (?1, ?2, ?3, 0) +-- STMT_UPSERT_PRISTINE +/* ### Probably need to bump the minimum SQLite version for UPSERT support + https://www.sqlite.org/lang_UPSERT.html + */ +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 +SELECT md5_checksum, size, hydrated FROM pristine WHERE checksum = ?1 --- STMT_SELECT_PRISTINE_SIZE -SELECT size -FROM pristine -WHERE checksum = ?1 LIMIT 1 - -- STMT_SELECT_PRISTINE_BY_MD5 SELECT checksum FROM pristine @@ -917,7 +916,7 @@ WHERE checksum = ?1 AND refcount = 0 -- STMT_SELECT_COPY_PRISTINES /* 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 +924,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 +933,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 +1815,53 @@ 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 /* ------------------------------------------------------------------------- */ Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc.h URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc.h?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc.h (original) +++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc.h Fri Aug 27 13:54:33 2021 @@ -164,7 +164,7 @@ extern "C" { * Please document any further format changes here. */ -#define SVN_WC__VERSION 31 +#define SVN_WC__VERSION 32 /* Formats <= this have no concept of "revert text-base/props". */ Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db.h URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db.h?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db.h (original) +++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db.h Fri Aug 27 13:54:33 2021 @@ -958,6 +958,8 @@ 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. + If HYDRATED is true, the contents of the pristine will be saved to disk. + Allocate the new stream, path and checksums in RESULT_POOL. */ svn_error_t * @@ -967,6 +969,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); @@ -1049,14 +1052,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 @{ */ @@ -3091,6 +3106,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_hydrate_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 HYDRATE_CALLBACK and HYDRATE_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_hydrate_cb_t hydrate_callback, + void *hydrate_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/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_pristine.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_pristine.c?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_pristine.c (original) +++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_pristine.c Fri Aug 27 13:54:33 2021 @@ -108,9 +108,10 @@ svn_wc__db_pristine_get_future_path(cons /* 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. @@ -132,16 +133,18 @@ pristine_read_txn(svn_stream_t **content { 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__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)); if (size) - *size = svn_sqlite__column_int64(stmt, 0); + *size = svn_sqlite__column_int64(stmt, 1); + + hydrated = svn_sqlite__column_boolean(stmt, 2); SVN_ERR(svn_sqlite__reset(stmt)); if (! have_row) @@ -152,19 +155,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; @@ -212,6 +223,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 * @@ -224,9 +282,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. @@ -235,8 +292,7 @@ pristine_get_tempdir(svn_wc__db_wcroot_t */ 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, + 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. */ @@ -245,85 +301,73 @@ 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; 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)); if (have_row) + hydrated = svn_sqlite__column_boolean(stmt, 2); + else + hydrated = FALSE; + + SVN_ERR(svn_sqlite__reset(stmt)); + + 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)); + } + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_UPSERT_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, install_data->size)); + 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_stream_t *stream; + svn_wc__db_install_data_t *install_data; svn_wc__db_wcroot_t *wcroot; const char *local_relpath; const char *temp_dir_abspath; @@ -336,22 +380,37 @@ svn_wc__db_pristine_prepare_install(svn_ 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")); - - (*install_data)->inner_stream = *stream; - - 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); + if (hydrated) + { + 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->size = 0; + + 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; } @@ -378,7 +437,7 @@ svn_wc__db_pristine_install(svn_wc__db_i * 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, + install_data, pristine_abspath, sha1_checksum, md5_checksum, scratch_pool), wcroot->sdb); @@ -390,8 +449,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; } @@ -472,84 +535,91 @@ 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; 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)); 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)); + 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; } @@ -581,6 +651,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); @@ -588,9 +659,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); @@ -670,19 +743,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; } @@ -843,6 +904,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, @@ -859,6 +921,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; } @@ -866,46 +931,50 @@ 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(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (hydrated) + *hydrated = svn_sqlite__column_boolean(stmt, 2); + SVN_ERR(svn_sqlite__reset(stmt)); *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; + const char *pristine_abspath; + svn_sqlite__stmt_t *stmt; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); + 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(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__bind_int(stmt, 2, FALSE)); + SVN_ERR(svn_sqlite__update(NULL, stmt)); + + SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool)); + + return SVN_NO_ERROR; +} Added: subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_textbase.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_textbase.c?rev=1892650&view=auto ============================================================================== --- subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_textbase.c (added) +++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_textbase.c Fri Aug 27 13:54:33 2021 @@ -0,0 +1,356 @@ +/* + * wc_db_textbase.c: working with text-bases + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#define SVN_WC__I_AM_WC_DB + +#include "svn_pools.h" +#include "svn_dirent_uri.h" + +#include "wc.h" +#include "wc_db.h" +#include "wc-queries.h" +#include "wc_db_private.h" + +/* ### Copied from wc_db.c: SQLITE_PROPERTIES_AVAILABLE() */ +#define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \ + (svn_sqlite__column_bytes(stmt, i) > 2) + +/* ### Copied from wc_db.c: get_recorded_size() */ +static svn_filesize_t +get_recorded_size(svn_sqlite__stmt_t *stmt, int slot) +{ + if (svn_sqlite__column_is_null(stmt, slot)) + return SVN_INVALID_FILESIZE; + return svn_sqlite__column_int64(stmt, slot); +} + +static svn_error_t * +textbase_add_ref(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_TEXTBASE_ADD_REF)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, op_depth)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +textbase_remove_ref(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_TEXTBASE_REMOVE_REF)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, op_depth)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + + return SVN_NO_ERROR; +} + +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) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + svn_sqlite__stmt_t *stmt; + apr_pool_t *iterpool; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, scratch_pool, + scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_TEXTBASE_WALK)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + + iterpool = svn_pool_create(scratch_pool); + while (1) + { + svn_boolean_t have_row; + const char *node_relpath; + int op_depth; + const svn_checksum_t *checksum; + const char *node_abspath; + svn_boolean_t have_props; + svn_filesize_t recorded_size; + apr_time_t recorded_time; + svn_boolean_t props_mod; + int max_op_depth; + svn_boolean_t have_ref; + svn_boolean_t want_ref; + svn_error_t *err; + + svn_pool_clear(iterpool); + + if (cancel_func) + { + err = cancel_func(cancel_baton); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (!have_row) + break; + + have_ref = svn_sqlite__column_boolean(stmt, 0); + node_relpath = svn_sqlite__column_text(stmt, 1, NULL); + node_abspath = svn_dirent_join(wcroot->abspath, node_relpath, iterpool); + op_depth = svn_sqlite__column_int(stmt, 2); + + err = svn_sqlite__column_checksum(&checksum, stmt, 3, iterpool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + have_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 4); + recorded_size = get_recorded_size(stmt, 5); + recorded_time = svn_sqlite__column_int64(stmt, 6); + props_mod = !svn_sqlite__column_is_null(stmt, 7); + max_op_depth = svn_sqlite__column_int(stmt, 8); + + err = callback(&want_ref, callback_baton, + node_abspath, op_depth, checksum, have_props, props_mod, + recorded_size, recorded_time, max_op_depth, iterpool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + if (have_ref && !want_ref) + { + err = textbase_remove_ref(wcroot, node_relpath, op_depth, iterpool); + } + else if (!have_ref && want_ref) + { + err = textbase_add_ref(wcroot, node_relpath, op_depth, iterpool); + } + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + svn_pool_destroy(iterpool); + + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +textbase_hydrate(svn_wc__db_t *db, + const char *wri_abspath, + svn_wc__db_textbase_hydrate_cb_t hydrate_callback, + void *hydrate_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + const svn_checksum_t *checksum, + const char *repos_root_url, + const char *repos_relpath, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + svn_stream_t *install_stream; + svn_wc__db_install_data_t *install_data; + svn_checksum_t *sha1_checksum; + svn_checksum_t *md5_checksum; + svn_error_t *err; + + /* ### Use svn_wc__db_wcroot_t */ + + SVN_ERR(svn_wc__db_pristine_prepare_install(&install_stream, &install_data, + &sha1_checksum, &md5_checksum, + db, wri_abspath, TRUE, + scratch_pool, scratch_pool)); + + err = hydrate_callback(hydrate_baton, repos_root_url, + repos_relpath, revision, + install_stream, + cancel_func, cancel_baton, + scratch_pool); + if (err) + { + return svn_error_compose_create( + err, svn_wc__db_pristine_install_abort(install_data, + scratch_pool)); + } + + err = svn_wc__db_pristine_install(install_data, sha1_checksum, + md5_checksum, scratch_pool); + if (err) + { + return svn_error_compose_create( + err, svn_wc__db_pristine_install_abort(install_data, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +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_hydrate_cb_t hydrate_callback, + void *hydrate_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + svn_sqlite__stmt_t *stmt; + apr_pool_t *iterpool; + const char *repos_root_url; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, scratch_pool, + scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_TEXTBASE_SYNC)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + + repos_root_url = NULL; + iterpool = svn_pool_create(scratch_pool); + while (1) + { + svn_boolean_t have_row; + const svn_checksum_t *checksum; + svn_boolean_t hydrated; + svn_boolean_t referenced; + svn_error_t *err; + + svn_pool_clear(iterpool); + + if (cancel_func) + { + err = cancel_func(cancel_baton); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (!have_row) + break; + + err = svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + hydrated = svn_sqlite__column_boolean(stmt, 1); + referenced = svn_sqlite__column_boolean(stmt, 2); + + if (!hydrated && referenced) + { + if (allow_hydrate) + { + const char *repos_relpath; + svn_revnum_t revision; + + repos_relpath = svn_sqlite__column_text(stmt, 3, NULL); + if (!repos_relpath) + { + return svn_error_createf( + SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt), + _("Unexpected entry for '%s'"), + svn_checksum_to_cstring_display(checksum, iterpool)); + } + + if (!repos_root_url) + { + apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 4); + + if (repos_id < 0) + { + return svn_error_createf( + SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt), + _("Unexpected entry for '%s'"), + svn_checksum_to_cstring_display(checksum, iterpool)); + } + + err = svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot, + repos_id, scratch_pool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + + if (!repos_relpath) + { + return svn_error_createf( + SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt), + _("Unexpected entry for '%s'"), + svn_checksum_to_cstring_display(checksum, iterpool)); + } + + revision = svn_sqlite__column_revnum(stmt, 5); + if (!SVN_IS_VALID_REVNUM(revision)) + { + return svn_error_createf( + SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt), + _("Unexpected entry for '%s'"), + svn_checksum_to_cstring_display(checksum, iterpool)); + } + + err = textbase_hydrate(db, local_abspath, hydrate_callback, + hydrate_baton, cancel_func, cancel_baton, + checksum, repos_root_url, repos_relpath, + revision, iterpool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + } + else if (hydrated && !referenced) + { + if (allow_dehydrate) + { + err = svn_wc__db_pristine_dehydrate(db, local_abspath, + checksum, iterpool); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + } + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + svn_pool_destroy(iterpool); + + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; +} Propchange: subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_textbase.c ------------------------------------------------------------------------------ svn:eol-style = native Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/authz_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/authz_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/authz_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/authz_tests.py Fri Aug 27 13:54:33 2021 @@ -1613,6 +1613,7 @@ def authz_log_censor_revprops(sbox): '-r1', sbox.repo_url]) @Skip(svntest.main.is_ra_type_file) +@Wimp("Applying delta to a local mod needs access to the text base") def remove_access_after_commit(sbox): "remove a subdir with authz file" Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/basic_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/basic_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/basic_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/basic_tests.py Fri Aug 27 13:54:33 2021 @@ -355,6 +355,7 @@ def basic_mkdir_wc_with_parents(sbox): #---------------------------------------------------------------------- +@Wimp("Relies on wc.text_base_path()") def basic_commit_corruption(sbox): "basic corruption detection on commit" @@ -420,6 +421,7 @@ def basic_commit_corruption(sbox): expected_status) #---------------------------------------------------------------------- +@Wimp("Relies on wc.text_base_path()") def basic_update_corruption(sbox): "basic corruption detection on update" Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/diff_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/diff_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/diff_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/diff_tests.py Fri Aug 27 13:54:33 2021 @@ -3063,6 +3063,7 @@ def diff_wrong_extension_type(sbox): 'diff', '-x', sbox.wc_dir, '-r', '1') # Check the order of the arguments for an external diff tool +@Wimp("Relies on wc.text_base_path()") def diff_external_diffcmd(sbox): "svn diff --diff-cmd provides the correct arguments" Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/revert_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/revert_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/revert_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/revert_tests.py Fri Aug 27 13:54:33 2021 @@ -243,6 +243,15 @@ def revert_from_wc_root(sbox): svntest.actions.run_and_verify_status('', expected_output) @Issue(1663) +# Walking the text-bases automatically repairs timestamps, so now the +# first and the second reverts in this test behave identically, as if +# 'svn cleanup' or any other command that repairs the timestamps had been +# called beforehand. Judging by the second part of the test, we're fine +# with revert doing nothing in that case, but that essentially contradicts +# the expectation in its first part. +# +# I temporarily mark the test XFail. See r1101730 and r1101817 for details. +@XFail() def revert_reexpand_keyword(sbox): "revert reexpands manually contracted keyword" Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/trans_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/trans_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/trans_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/trans_tests.py Fri Aug 27 13:54:33 2021 @@ -213,6 +213,7 @@ def keywords_off(path): ### This test is know to fail when Subversion is built in very deep ### directory structures, caused by SVN_KEYWORD_MAX_LEN being defined ### as 255. +@Wimp("Relies on wc.text_base_path()") def keywords_from_birth(sbox): "commit new files with keywords active from birth" @@ -551,6 +552,7 @@ def update_modified_with_translation(sbo # after the commit, the file and its text-base have been changed to # have the new line-ending style. @Issue(1085) +@Wimp("Relies on wc.text_base_path()") def eol_change_is_text_mod(sbox): "committing eol-style change forces text send" Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/update_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/update_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/update_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/update_tests.py Fri Aug 27 13:54:33 2021 @@ -5138,6 +5138,9 @@ def revive_children_of_copy(sbox): raise svntest.Failure('psi unexpectedly non-existent') @SkipUnless(svntest.main.is_os_windows) +# Needs work: the access denied error now happens when we use the +# working file as the base in update_editor.c:apply_textdelta(). +@Wimp("Need to update the access denied handling in update_editor.c") def skip_access_denied(sbox): """access denied paths should be skipped""" @@ -6779,6 +6782,7 @@ def update_add_conflicted_deep(sbox): # This final update used to segfault using 1.9.0 and 1.9.1 sbox.simple_update('A/z/z', 3) +@Wimp("The error message has changed") def missing_tmp_update(sbox): "missing tmp update caused segfault" Modified: subversion/branches/pristines-on-demand/subversion/tests/cmdline/upgrade_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/cmdline/upgrade_tests.py?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/cmdline/upgrade_tests.py (original) +++ subversion/branches/pristines-on-demand/subversion/tests/cmdline/upgrade_tests.py Fri Aug 27 13:54:33 2021 @@ -119,9 +119,10 @@ def check_pristine(sbox, files): for file in files: file_path = sbox.ospath(file) file_text = open(file_path, 'r').read() - file_pristine = open(svntest.wc.text_base_path(file_path), 'r').read() - if (file_text != file_pristine): - raise svntest.Failure("pristine mismatch for '%s'" % (file)) + # The file at wc.text_base_path() may not exist: + # file_pristine = open(svntest.wc.text_base_path(file_path), 'r').read() + # if (file_text != file_pristine): + # raise svntest.Failure("pristine mismatch for '%s'" % (file)) def check_dav_cache(dir_path, wc_id, expected_dav_caches): dot_svn = svntest.main.get_admin_name() @@ -138,9 +139,8 @@ def check_dav_cache(dir_path, wc_id, exp minor = sqlite_ver[1] patch = sqlite_ver[2] - if major < 3 or (major == 3 and minor < 6) \ - or (major == 3 and minor == 6 and patch < 18): - return # We need a newer SQLite + if major < 3 or (major == 3 and minor < 9): + return # We need a newer SQLite for local_relpath, expected_dav_cache in expected_dav_caches.items(): # NODES conversion is complete enough that we can use it if it exists @@ -797,6 +797,7 @@ def delete_in_copy_upgrade(sbox): 'b347d1da69df9a6a70433ceeaa0d46c8483e8c03']]) +@Wimp("Can't fetch pristines: the working copy points to file:///tmp/repo") def replaced_files(sbox): "upgrade with base and working replaced files" @@ -1371,6 +1372,7 @@ def upgrade_1_7_dir_external(sbox): svntest.actions.run_and_verify_svn(None, [], 'upgrade', sbox.wc_dir) @SkipUnless(svntest.wc.python_sqlite_can_read_wc) +@Wimp("Test calls status on a non-upgraded wc") def auto_analyze(sbox): """automatic SQLite ANALYZE""" Modified: subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/db-test.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/db-test.c?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/db-test.c (original) +++ subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/db-test.c Fri Aug 27 13:54:33 2021 @@ -87,7 +87,7 @@ static const char * const TESTING_DATA = "insert into repository values (2, '" ROOT_TWO "', '" UUID_TWO "'); " "insert into wcroot values (1, null); " - "insert into pristine values ('$sha1$" SHA1_1 "', NULL, 15, 1, '$md5 $" MD5_1 "'); " + "insert into pristine values ('$sha1$" SHA1_1 "', NULL, 15, 1, '$md5 $" MD5_1 "', 1); " ); #define NOT_MOVED FALSE, NULL Modified: subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/entries-compat.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/entries-compat.c?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/entries-compat.c (original) +++ subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/entries-compat.c Fri Aug 27 13:54:33 2021 @@ -86,7 +86,7 @@ static const char * const TESTING_DATA = "insert into repository values (2, '" ROOT_TWO "', '" UUID_TWO "'); " "insert into wcroot values (1, null); " - "insert into pristine values ('$sha1$" SHA1_1 "', NULL, 15, 1, '$md5 $" MD5_1 "'); " + "insert into pristine values ('$sha1$" SHA1_1 "', NULL, 15, 1, '$md5 $" MD5_1 "', 1); " ); #define NOT_MOVED FALSE, NULL Modified: subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/pristine-store-test.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/pristine-store-test.c?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/pristine-store-test.c (original) +++ subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/pristine-store-test.c Fri Aug 27 13:54:33 2021 @@ -94,7 +94,7 @@ pristine_write_read(const svn_test_opts_ SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, &install_data, &data_sha1, &data_md5, - db, wc_abspath, + db, wc_abspath, TRUE, pool, pool)); sz = strlen(data); @@ -104,10 +104,12 @@ pristine_write_read(const svn_test_opts_ /* Ensure it's not already in the store. */ { svn_boolean_t present; + svn_boolean_t hydrated; - SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, - pool)); + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); SVN_TEST_ASSERT(! present); + SVN_TEST_ASSERT(! hydrated); } /* Install the new pristine file, referenced by its checksum. */ @@ -117,10 +119,12 @@ pristine_write_read(const svn_test_opts_ /* Ensure it is now found in the store. */ { svn_boolean_t present; + svn_boolean_t hydrated; - SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, - pool)); + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); SVN_TEST_ASSERT(present); + SVN_TEST_ASSERT(hydrated); } /* Look up its MD-5 from its SHA-1, and check it's the same MD-5. */ @@ -161,10 +165,12 @@ pristine_write_read(const svn_test_opts_ /* Ensure it's no longer found in the store. */ { svn_boolean_t present; + svn_boolean_t hydrated; - SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, - pool)); + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); SVN_TEST_ASSERT(! present); + SVN_TEST_ASSERT(! hydrated); } return SVN_NO_ERROR; @@ -191,7 +197,7 @@ pristine_delete_while_open(const svn_tes SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, &install_data, &data_sha1, &data_md5, - db, wc_abspath, + db, wc_abspath, TRUE, pool, pool)); sz = strlen(data); @@ -221,10 +227,12 @@ pristine_delete_while_open(const svn_tes * an orphan, depending on the implementation.) */ { svn_boolean_t present; + svn_boolean_t hydrated; - SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, - pool)); + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); SVN_TEST_ASSERT(! present); + SVN_TEST_ASSERT(! hydrated); } /* Close the read stream */ @@ -264,7 +272,7 @@ reject_mismatching_text(const svn_test_o SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, &install_data, &data_sha1, &data_md5, - db, wc_abspath, + db, wc_abspath, TRUE, pool, pool)); sz = strlen(data); @@ -286,7 +294,7 @@ reject_mismatching_text(const svn_test_o SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, &install_data, &data_sha1, &data_md5, - db, wc_abspath, + db, wc_abspath, TRUE, pool, pool)); sz = strlen(data2); @@ -306,6 +314,219 @@ reject_mismatching_text(const svn_test_o #endif } +static svn_error_t * +pristine_install_dehydrated(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_wc__db_t *db; + const char *wc_abspath; + + svn_wc__db_install_data_t *install_data; + svn_stream_t *pristine_stream; + apr_size_t sz; + + const char data[] = "Blah"; + svn_checksum_t *data_sha1, *data_md5; + + SVN_ERR(create_repos_and_wc(&wc_abspath, &db, + "pristine_install_dehydrated", opts, pool)); + + /* Write DATA into a new temporary pristine file, set PRISTINE_TMP_ABSPATH + * to its path and set DATA_SHA1 and DATA_MD5 to its checksums. */ + SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, + &install_data, + &data_sha1, &data_md5, + db, wc_abspath, FALSE, + pool, pool)); + + sz = strlen(data); + SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); + SVN_ERR(svn_stream_close(pristine_stream)); + + /* Ensure it's not already in the store. */ + { + svn_boolean_t present; + svn_boolean_t hydrated; + + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); + SVN_TEST_ASSERT(! present); + SVN_TEST_ASSERT(! hydrated); + } + + /* Install the new pristine file, referenced by its checksum. */ + SVN_ERR(svn_wc__db_pristine_install(install_data, + data_sha1, data_md5, pool)); + + /* Ensure it is now found in the store. */ + { + svn_boolean_t present; + svn_boolean_t hydrated; + + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); + SVN_TEST_ASSERT(present); + SVN_TEST_ASSERT(! hydrated); + } + + /* Look up its MD-5 from its SHA-1, and check it's the same MD-5. */ + { + const svn_checksum_t *looked_up_md5; + + SVN_ERR(svn_wc__db_pristine_get_md5(&looked_up_md5, db, wc_abspath, + data_sha1, pool, pool)); + SVN_TEST_ASSERT(looked_up_md5->kind == svn_checksum_md5); + SVN_TEST_ASSERT(svn_checksum_match(data_md5, looked_up_md5)); + } + + /* Check the saved pristine size and try to read the pristine text back. */ + { + svn_stream_t *actual_contents; + svn_filesize_t actual_size; + + SVN_ERR(svn_wc__db_pristine_read(&actual_contents, &actual_size, + db, wc_abspath, data_sha1, pool, pool)); + SVN_TEST_ASSERT(actual_contents == NULL); + SVN_TEST_INT_ASSERT(actual_size, sz); + } + + /* Trivially test the "remove if unreferenced" API: it's not referenced + so we should be able to remove it. */ + { + svn_error_t *err; + svn_stream_t *data_read_back; + + SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool)); + err = svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, + data_sha1, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); + } + + /* Ensure it's no longer found in the store. */ + { + svn_boolean_t present; + svn_boolean_t hydrated; + + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); + SVN_TEST_ASSERT(! present); + SVN_TEST_ASSERT(! hydrated); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +pristine_dehydrate(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_wc__db_t *db; + const char *wc_abspath; + + svn_wc__db_install_data_t *install_data; + svn_stream_t *pristine_stream; + apr_size_t sz; + + const char data[] = "Blah"; + svn_string_t *data_string = svn_string_create(data, pool); + svn_checksum_t *data_sha1, *data_md5; + + SVN_ERR(create_repos_and_wc(&wc_abspath, &db, + "pristine_dehydrate", opts, pool)); + + /* Write DATA into a new temporary pristine file, set PRISTINE_TMP_ABSPATH + * to its path and set DATA_SHA1 and DATA_MD5 to its checksums. */ + SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, + &install_data, + &data_sha1, &data_md5, + db, wc_abspath, TRUE, + pool, pool)); + + sz = strlen(data); + SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); + SVN_ERR(svn_stream_close(pristine_stream)); + + /* Install the new pristine file, referenced by its checksum. */ + SVN_ERR(svn_wc__db_pristine_install(install_data, + data_sha1, data_md5, pool)); + + /* Check the state of the pristine. */ + { + svn_boolean_t present; + svn_boolean_t hydrated; + + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); + SVN_TEST_ASSERT(present); + SVN_TEST_ASSERT(hydrated); + } + + /* Dehydrate the pristine. */ + SVN_ERR(svn_wc__db_pristine_dehydrate(db, wc_abspath, data_sha1, pool)); + + /* Check the state of the pristine. */ + { + svn_boolean_t present; + svn_boolean_t hydrated; + + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); + SVN_TEST_ASSERT(present); + SVN_TEST_ASSERT(! hydrated); + } + + /* Check the saved pristine size and try to read the pristine text back. */ + { + svn_stream_t *actual_contents; + svn_filesize_t actual_size; + + SVN_ERR(svn_wc__db_pristine_read(&actual_contents, &actual_size, + db, wc_abspath, data_sha1, pool, pool)); + SVN_TEST_ASSERT(actual_contents == NULL); + SVN_TEST_INT_ASSERT(actual_size, sz); + } + + /* Rehydrate it by installing the pristine again. */ + SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, + &install_data, + &data_sha1, &data_md5, + db, wc_abspath, TRUE, + pool, pool)); + + sz = strlen(data); + SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); + SVN_ERR(svn_stream_close(pristine_stream)); + + SVN_ERR(svn_wc__db_pristine_install(install_data, + data_sha1, data_md5, pool)); + + /* Check the state of the pristine. */ + { + svn_boolean_t present; + svn_boolean_t hydrated; + + SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, db, wc_abspath, + data_sha1, pool)); + SVN_TEST_ASSERT(present); + SVN_TEST_ASSERT(hydrated); + } + + /* Read the pristine text back and verify it's the same content. */ + { + svn_stream_t *data_stream = svn_stream_from_string(data_string, pool); + svn_stream_t *data_read_back; + svn_boolean_t same; + + SVN_ERR(svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, + data_sha1, pool, pool)); + SVN_ERR(svn_stream_contents_same2(&same, data_read_back, data_stream, + pool)); + SVN_TEST_ASSERT(same); + } + + return SVN_NO_ERROR; +} + static int max_threads = -1; @@ -318,6 +539,10 @@ static struct svn_test_descriptor_t test "pristine_delete_while_open"), SVN_TEST_OPTS_PASS(reject_mismatching_text, "reject_mismatching_text"), + SVN_TEST_OPTS_PASS(pristine_install_dehydrated, + "pristine_install_dehydrated"), + SVN_TEST_OPTS_PASS(pristine_dehydrate, + "pristine_dehydrate"), SVN_TEST_NULL }; Modified: subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/utils.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/utils.c?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/utils.c (original) +++ subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/utils.c Fri Aug 27 13:54:33 2021 @@ -405,28 +405,22 @@ sbox_wc_copy_url(svn_test__sandbox_t *b, svn_error_t * sbox_wc_revert(svn_test__sandbox_t *b, const char *path, svn_depth_t depth) { - const char *abspath = sbox_wc_path(b, path); - const char *dir_abspath; - const char *lock_root_abspath; + svn_client_ctx_t *ctx; + apr_array_header_t *paths; - if (strcmp(abspath, b->wc_abspath)) - dir_abspath = svn_dirent_dirname(abspath, b->pool); - else - dir_abspath = abspath; + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + + paths = apr_array_make(b->pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(paths, const char *) = sbox_wc_path(b, path); + + SVN_ERR(svn_client_revert4(paths, depth, + NULL /* changelists */, + FALSE /* clear_changelists */, + FALSE /* metadata_only */, + TRUE /*added_keep_local*/, + ctx, + b->pool)); - SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx, - dir_abspath, FALSE /* lock_anchor */, - b->pool, b->pool)); - SVN_ERR(svn_wc_revert6(b->wc_ctx, abspath, depth, - FALSE /* use_commit_times */, - NULL /* changelist_filter */, - FALSE /* clear_changelists */, - FALSE /* metadata_only */, - TRUE /*added_keep_local*/, - NULL, NULL, /* cancel baton + func */ - NULL, NULL, /* notify baton + func */ - b->pool)); - SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, lock_root_abspath, b->pool)); return SVN_NO_ERROR; } @@ -576,27 +570,14 @@ svn_error_t * sbox_wc_resolve(svn_test__sandbox_t *b, const char *path, svn_depth_t depth, svn_wc_conflict_choice_t conflict_choice) { - const char *lock_abspath; - svn_error_t *err; + svn_client_ctx_t *ctx; + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); - SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, b->wc_ctx, - sbox_wc_path(b, path), - b->pool, b->pool)); - err = svn_wc__resolve_conflicts(b->wc_ctx, sbox_wc_path(b, path), - depth, - TRUE /* resolve_text */, - "" /* resolve_prop (ALL props) */, - TRUE /* resolve_tree */, - conflict_choice, - NULL, NULL, /* conflict func */ - NULL, NULL, /* cancellation */ - NULL, NULL, /* notification */ - b->pool); - - err = svn_error_compose_create(err, svn_wc__release_write_lock(b->wc_ctx, - lock_abspath, - b->pool)); - return err; + SVN_ERR(svn_client_resolve(sbox_wc_path(b, path), depth, conflict_choice, + ctx, b->pool)); + + return SVN_NO_ERROR; } svn_error_t * Modified: subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-queries-test.c URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-queries-test.c?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-queries-test.c (original) +++ subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-queries-test.c Fri Aug 27 13:54:33 2021 @@ -102,14 +102,14 @@ static const int slow_statements[] = STMT_SELECT_PRESENT_HIGHEST_WORKING_NODES_BY_BASENAME_AND_KIND, STMT_SELECT_COPIES_OF_REPOS_RELPATH, - /* Designed as slow to avoid penalty on other queries */ - STMT_SELECT_UNREFERENCED_PRISTINES, - /* Slow, but just if foreign keys are enabled: * STMT_DELETE_PRISTINE_IF_UNREFERENCED, */ STMT_HAVE_STAT1_TABLE, /* Queries sqlite_master which has no index */ + /* Currently uses a temporary B-tree for GROUP BY */ + STMT_TEXTBASE_SYNC, + -1 /* final marker */ }; Modified: subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-test-queries.sql URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-test-queries.sql?rev=1892650&r1=1892649&r2=1892650&view=diff ============================================================================== --- subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-test-queries.sql (original) +++ subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-test-queries.sql Fri Aug 27 13:54:33 2021 @@ -61,10 +61,10 @@ INSERT INTO actual_node (local_relpath, VALUES (?1, ?2, ?3, 1) -- STMT_ENSURE_EMPTY_PRISTINE -INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount) +INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount, hydrated) VALUES ('$sha1$da39a3ee5e6b4b0d3255bfef95601890afd80709', '$md5 $d41d8cd98f00b204e9800998ecf8427e', - 0, 0) + 0, 0, 1) -- STMT_NODES_SET_FILE UPDATE nodes
