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


Reply via email to