Modified: subversion/trunk/subversion/libsvn_wc/update_editor.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/update_editor.c?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/trunk/subversion/libsvn_wc/update_editor.c Tue Dec 13 09:49:29 
2022
@@ -48,6 +48,7 @@
 #include "conflicts.h"
 #include "translate.h"
 #include "workqueue.h"
+#include "textbase.h"
 
 #include "private/svn_subr_private.h"
 #include "private/svn_wc_private.h"
@@ -439,7 +440,8 @@ struct handler_baton
 
 /* Get an empty file in the temporary area for WRI_ABSPATH.  The file will
    not be set for automatic deletion, and the name will be returned in
-   TMP_FILENAME.
+   TMP_FILENAME_P.  Set *CLEANUP_WORK_ITEM_P to a new work item that will
+   remove the temporary file.
 
    This implementation creates a new empty file with a unique name.
 
@@ -451,19 +453,35 @@ struct handler_baton
    ### file name to create later.  A better way may not be readily available.
  */
 static svn_error_t *
-get_empty_tmp_file(const char **tmp_filename,
+get_empty_tmp_file(const char **tmp_filename_p,
+                   svn_skel_t **cleanup_work_item_p,
                    svn_wc__db_t *db,
                    const char *wri_abspath,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
   const char *temp_dir_abspath;
+  const char *tmp_filename;
+  svn_skel_t *work_item;
+  svn_error_t *err;
 
   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
                                          scratch_pool, scratch_pool));
-  SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
+  SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_filename, temp_dir_abspath,
                                    svn_io_file_del_none,
                                    scratch_pool, scratch_pool));
+  err = svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
+                                     tmp_filename,
+                                     result_pool, scratch_pool);
+  if (err)
+    {
+      return svn_error_compose_create(
+               err,
+               svn_io_remove_file2(tmp_filename, TRUE, scratch_pool));
+    }
+
+  *tmp_filename_p = tmp_filename;
+  *cleanup_work_item_p = work_item;
 
   return SVN_NO_ERROR;
 }
@@ -3726,10 +3744,10 @@ lazy_open_source(svn_stream_t **stream,
 {
   struct file_baton *fb = baton;
 
-  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
-                                   fb->local_abspath,
-                                   fb->original_checksum,
-                                   result_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_get_contents(stream, fb->edit_baton->db,
+                                        fb->local_abspath,
+                                        fb->original_checksum, FALSE,
+                                        result_pool, scratch_pool));
 
 
   return SVN_NO_ERROR;
@@ -3748,19 +3766,35 @@ lazy_open_target(svn_stream_t **stream_p
   svn_stream_t *pristine_install_stream;
   svn_wc__working_file_writer_t *file_writer;
   svn_stream_t *stream;
+  svn_boolean_t hydrated;
+
+  if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed)
+    {
+      hydrated = TRUE;
+    }
+  else if (fb->adding_file && !fb->add_existed)
+    {
+      /* Clean new file, hint that we don't need the text-base. */
+      hydrated = FALSE;
+    }
+  else
+    {
+      hydrated = TRUE;
+    }
 
   /* By convention return value is undefined on error, but we rely
      on HB->INSTALL_DATA value in window_handler() and abort
      INSTALL_STREAM if is not NULL on error.
      So we store INSTALL_DATA to local variable first, to leave
      HB->INSTALL_DATA unchanged on error. */
-  SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_install_stream,
-                                              &pristine_install_data,
-                                              &hb->new_text_base_sha1_checksum,
-                                              NULL,
-                                              fb->edit_baton->db,
-                                              fb->dir_baton->local_abspath,
-                                              result_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_prepare_install(&pristine_install_stream,
+                                           &pristine_install_data,
+                                           &hb->new_text_base_sha1_checksum,
+                                           NULL,
+                                           fb->edit_baton->db,
+                                           fb->local_abspath,
+                                           hydrated,
+                                           result_pool, scratch_pool));
 
   if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed)
     {
@@ -4063,17 +4097,19 @@ svn_wc__perform_file_merge(svn_skel_t **
      the textual changes into the working file. */
   const char *oldrev_str, *newrev_str, *mine_str;
   const char *merge_left;
-  svn_boolean_t delete_left = FALSE;
   const char *path_ext = "";
   const char *new_pristine_abspath;
   enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
   svn_skel_t *work_item;
+  svn_skel_t *cleanup_queue = NULL;
 
   *work_items = NULL;
 
-  SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
-                                       db, wri_abspath, new_checksum,
-                                       scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_setaside_wq(&new_pristine_abspath, &work_item,
+                                       db, local_abspath, new_checksum,
+                                       cancel_func, cancel_baton,
+                                       result_pool, scratch_pool));
+  cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool);
 
   /* If we have any file extensions we're supposed to
      preserve in generated conflict file names, then find
@@ -4107,14 +4143,19 @@ svn_wc__perform_file_merge(svn_skel_t **
 
   if (! original_checksum)
     {
-      SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
+      SVN_ERR(get_empty_tmp_file(&merge_left, &work_item, db, wri_abspath,
                                  result_pool, scratch_pool));
-      delete_left = TRUE;
+      cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool);
     }
   else
-    SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
-                                         original_checksum,
-                                         result_pool, scratch_pool));
+    {
+      SVN_ERR(svn_wc__textbase_setaside_wq(&merge_left, &work_item,
+                                           db, local_abspath,
+                                           original_checksum,
+                                           cancel_func, cancel_baton,
+                                           result_pool, scratch_pool));
+      cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool);
+    }
 
   /* Merge the changes from the old textbase to the new
      textbase into the file we're updating.
@@ -4135,16 +4176,9 @@ svn_wc__perform_file_merge(svn_skel_t **
                                  result_pool, scratch_pool));
 
   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
-  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
+  *work_items = svn_wc__wq_merge(*work_items, cleanup_queue, result_pool);
 
-  /* If we created a temporary left merge file, get rid of it. */
-  if (delete_left)
-    {
-      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
-                                           merge_left,
-                                           result_pool, scratch_pool));
-      *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
-    }
+  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
 
   return SVN_NO_ERROR;
 }
@@ -4729,9 +4763,9 @@ close_file(void *file_baton,
         }
       else
         {
-          SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, eb->db,
-                                           eb->wcroot_abspath, new_checksum,
-                                           scratch_pool, scratch_pool));
+          SVN_ERR(svn_wc__textbase_get_contents(&src_stream, eb->db,
+                                                fb->local_abspath, 
new_checksum,
+                                                FALSE, scratch_pool, 
scratch_pool));
         }
 
       SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, 
scratch_pool));
@@ -4871,6 +4905,7 @@ update_keywords_after_switch_cb(void *ba
   svn_boolean_t record_fileinfo;
   svn_skel_t *work_items;
   const char *install_from;
+  svn_skel_t *cleanup_work_item;
 
   propval = svn_hash_gets(props, SVN_PROP_KEYWORDS);
   if (!propval)
@@ -4896,11 +4931,18 @@ update_keywords_after_switch_cb(void *ba
       SVN_ERR(svn_stream_copy3(working_stream, install_from_stream,
                                eb->cancel_func, eb->cancel_baton,
                                scratch_pool));
+      SVN_ERR(svn_wc__wq_build_file_remove(&cleanup_work_item, eb->db,
+                                           local_abspath, install_from,
+                                           scratch_pool, scratch_pool));
       record_fileinfo = FALSE;
     }
   else
     {
-      install_from = NULL;
+      SVN_ERR(svn_wc__textbase_setaside_wq(&install_from,
+                                           &cleanup_work_item,
+                                           eb->db, local_abspath, NULL,
+                                           eb->cancel_func, eb->cancel_baton,
+                                           scratch_pool, scratch_pool));
       record_fileinfo = TRUE;
     }
 
@@ -4909,15 +4951,7 @@ update_keywords_after_switch_cb(void *ba
                                         eb->use_commit_times,
                                         record_fileinfo,
                                         scratch_pool, scratch_pool));
-  if (install_from)
-    {
-      svn_skel_t *work_item;
-
-      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
-                                           local_abspath, install_from,
-                                           scratch_pool, scratch_pool));
-      work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
-    }
+  work_items = svn_wc__wq_merge(work_items, cleanup_work_item, scratch_pool);
 
   SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items,
                             scratch_pool));
@@ -5606,33 +5640,39 @@ svn_wc_add_repos_file4(svn_wc_context_t
                                    entry_props, pool, pool));
   }
 
+  {
+    const char *tmp_dir_abspath;
+
+    SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
+                                           db, dir_abspath,
+                                           scratch_pool, scratch_pool));
+
+    SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
+                                   tmp_dir_abspath, svn_io_file_del_none,
+                                   scratch_pool, scratch_pool));
+  }
+
   /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
      it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
      NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
   if (copyfrom_url)
     {
-      SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents,
-                                                  &install_data,
-                                                  &new_text_base_sha1_checksum,
-                                                  &new_text_base_md5_checksum,
-                                                  wc_ctx->db, local_abspath,
-                                                  scratch_pool, scratch_pool));
+      svn_stream_t *install_stream;
+
+      SVN_ERR(svn_wc__textbase_prepare_install(&install_stream,
+                                               &install_data,
+                                               &new_text_base_sha1_checksum,
+                                               &new_text_base_md5_checksum,
+                                               wc_ctx->db, local_abspath,
+                                               copyfrom_url != NULL,
+                                               scratch_pool, scratch_pool));
+
+      tmp_base_contents = svn_stream_tee(install_stream,
+                                         tmp_base_contents,
+                                         scratch_pool);
     }
   else
     {
-      const char *tmp_dir_abspath;
-
-      /* We are not installing a PRISTINE file, but we use the same code to
-         create whatever we want to install */
-
-      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
-                                             db, dir_abspath,
-                                             scratch_pool, scratch_pool));
-
-      SVN_ERR(svn_stream_open_unique(&tmp_base_contents, 
&tmp_text_base_abspath,
-                                     tmp_dir_abspath, svn_io_file_del_none,
-                                     scratch_pool, scratch_pool));
-
       new_text_base_sha1_checksum = NULL;
       new_text_base_md5_checksum = NULL;
     }
@@ -5655,6 +5695,10 @@ svn_wc_add_repos_file4(svn_wc_context_t
       SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
                                cancel_func, cancel_baton, pool));
     }
+  else
+    {
+      source_abspath = tmp_text_base_abspath;
+    }
 
   /* Install new text base for copied files. Added files do NOT have a
      text base.  */
@@ -5690,42 +5734,20 @@ svn_wc_add_repos_file4(svn_wc_context_t
       new_text_base_md5_checksum = NULL;
     }
 
-  /* For added files without NEW_CONTENTS, then generate the working file
-     from the provided "pristine" contents.  */
-  if (new_contents == NULL && copyfrom_url == NULL)
-    source_abspath = tmp_text_base_abspath;
-
-  {
-    svn_boolean_t record_fileinfo;
+  /* Install the working copy file (with appropriate translation) from
+     the provided temporary file at SOURCE_ABSPATH.  */
+  SVN_ERR(svn_wc__wq_build_file_install(&work_item,
+                                        db, local_abspath,
+                                        source_abspath,
+                                        FALSE /* use_commit_times */,
+                                        TRUE  /* record_fileinfo */,
+                                        pool, pool));
+  all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
 
-    /* If new contents were provided, then we do NOT want to record the
-       file information. We assume the new contents do not match the
-       "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
-    record_fileinfo = (new_contents == NULL);
-
-    /* Install the working copy file (with appropriate translation) from
-       the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
-       installation from the pristine (available for copied/moved files),
-       or it will specify a temporary file where we placed a "pristine"
-       (for an added file) or a detranslated local-mods file.  */
-    SVN_ERR(svn_wc__wq_build_file_install(&work_item,
-                                          db, local_abspath,
-                                          source_abspath,
-                                          FALSE /* use_commit_times */,
-                                          record_fileinfo,
-                                          pool, pool));
-    all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
-
-    /* If we installed from somewhere besides the official pristine, then
-       it is a temporary file, which needs to be removed.  */
-    if (source_abspath != NULL)
-      {
-        SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
-                                             source_abspath,
-                                             pool, pool));
-        all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
-      }
-  }
+  SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
+                                       source_abspath,
+                                       pool, pool));
+  all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
 
   SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
                                   new_base_props,

Modified: subversion/trunk/subversion/libsvn_wc/upgrade.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/upgrade.c?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/trunk/subversion/libsvn_wc/upgrade.c Tue Dec 13 09:49:29 2022
@@ -1074,7 +1074,7 @@ migrate_text_bases(apr_hash_t **text_bas
 
         /* Insert a row into the pristine table. */
         SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
-                                          STMT_INSERT_OR_IGNORE_PRISTINE));
+                                          STMT_INSERT_OR_IGNORE_PRISTINE_F31));
         SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
         SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
         SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
@@ -2026,6 +2026,7 @@ svn_error_t *
 svn_wc__upgrade(svn_wc_context_t *wc_ctx,
                 const char *local_abspath,
                 int target_format,
+                svn_boolean_t store_pristine,
                 svn_wc_upgrade_get_repos_info_t repos_info_func,
                 void *repos_info_baton,
                 svn_cancel_func_t cancel_func,
@@ -2128,7 +2129,7 @@ svn_wc__upgrade(svn_wc_context_t *wc_ctx
                                    &data.repos_id, &data.wc_id,
                                    db, target_format, data.root_abspath,
                                    this_dir->repos, this_dir->uuid,
-                                   scratch_pool));
+                                   store_pristine, scratch_pool));
 
   /* Migrate the entries over to the new database.
    ### We need to think about atomicity here.

Modified: subversion/trunk/subversion/libsvn_wc/util.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/util.c?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/util.c (original)
+++ subversion/trunk/subversion/libsvn_wc/util.c Tue Dec 13 09:49:29 2022
@@ -37,6 +37,7 @@
 
 #include "wc.h"   /* just for prototypes of things in this .c file */
 #include "entries.h"
+#include "textbase.h"
 #include "private/svn_wc_private.h"
 
 #include "svn_private_config.h"
@@ -505,8 +506,9 @@ svn_wc__fetch_base_func(const char **fil
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR(svn_wc__db_pristine_get_path(filename, sfb->db, local_abspath,
-                                       checksum, scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_setaside(filename, sfb->db, local_abspath,
+                                    checksum, NULL, NULL,
+                                    result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_wc/wc-metadata.sql
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-metadata.sql?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-metadata.sql Tue Dec 13 09:49:29 
2022
@@ -708,12 +708,88 @@ WHERE l.op_depth = 0
 
 
 /* ------------------------------------------------------------------------- */
-/* Format 32 is identical to format 31.
-   Its purpose is as a place-holder in developing and testing the
-   multi-wc-format support feature. */
+/* Format 32 adds support for optional pristine contents with the
+   following schema changes:
+   - Add the 'hydrated' column to the PRISTINE table.
+   - Add the I_PRISTINE_UNREFERENCED index.
+   - Add the TEXTBASE_REFS table.
+   - Add the SETTINGS table. */
 -- STMT_UPGRADE_TO_32
+/* True iff the pristine contents are currently available on disk. */
+ALTER TABLE PRISTINE ADD COLUMN hydrated INTEGER NOT NULL DEFAULT 1;
+
+  /* Note: we use checksums to detect if the file contents have been modified
+     in textbase.c and in the svn_wc__internal_file_modified_p() function.
+
+     The new working copy format SHOULD incorporate a switch to a different
+     checksum type without known collisions.
+
+     For the updated pristine table schema, we MAY want to add a new column
+     containing a checksum of the first 8KB of the file to allow saying that
+     the file is modified without reading all its content.  That could speed
+     up the check for large modified files whose size did not change, for
+     example if they are allocated in certain extents.
+   */
+
+CREATE INDEX I_PRISTINE_UNREFERENCED ON PRISTINE (refcount, refcount=0);
+
+/* This table contains references to the on disk text-base contents.
+   Every row corresponds to a row in NODES table with the same key.
+   While a row is present is this table, the contents identified by the
+   corresponding NODES.checksum cannot be dehydrated from the pristine store.
+ */
+CREATE TABLE TEXTBASE_REFS (
+  /* Same key columns as in the NODES table */
+  wc_id  INTEGER NOT NULL,
+  local_relpath  TEXT NOT NULL,
+  op_depth  INTEGER NOT NULL,
+
+  PRIMARY KEY (wc_id, local_relpath, op_depth)
+  );
+
+DROP TRIGGER nodes_delete_trigger;
+
+CREATE TRIGGER nodes_delete_trigger
+AFTER DELETE ON nodes
+WHEN OLD.checksum IS NOT NULL
+BEGIN
+  UPDATE pristine SET refcount = refcount - 1
+  WHERE checksum = OLD.checksum;
+  DELETE FROM textbase_refs
+  WHERE wc_id = OLD.wc_id
+    AND local_relpath = OLD.local_relpath
+    AND op_depth = OLD.op_depth;
+END;
+
+DROP TRIGGER nodes_update_checksum_trigger;
+
+CREATE TRIGGER nodes_update_checksum_trigger
+AFTER UPDATE OF checksum ON nodes
+WHEN NEW.checksum IS NOT OLD.checksum
+  /* AND (NEW.checksum IS NOT NULL OR OLD.checksum IS NOT NULL) */
+BEGIN
+  UPDATE pristine SET refcount = refcount + 1
+  WHERE checksum = NEW.checksum;
+  UPDATE pristine SET refcount = refcount - 1
+  WHERE checksum = OLD.checksum;
+  DELETE FROM textbase_refs
+  WHERE wc_id = OLD.wc_id
+    AND local_relpath = OLD.local_relpath
+    AND op_depth = OLD.op_depth;
+END;
+
+/* This table contains settings of a working copy, identified by WC_ID. */
+CREATE TABLE SETTINGS (
+  wc_id  INTEGER NOT NULL REFERENCES WCROOT (id) PRIMARY KEY,
+  store_pristine  INTEGER
+);
+
 PRAGMA user_version = 32;
 
+/* ------------------------------------------------------------------------- */
+/* Format 33 ....  */
+/* -- STMT_UPGRADE_TO_33
+PRAGMA user_version = 33; */
 
 /* ------------------------------------------------------------------------- */
 /* When bumping the format, also update:

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Tue Dec 13 09:49:29 
2022
@@ -883,23 +883,33 @@ SELECT id, work FROM work_queue ORDER BY
 -- STMT_DELETE_WORK_ITEM
 DELETE FROM work_queue WHERE id = ?1
 
--- STMT_INSERT_OR_IGNORE_PRISTINE
+-- STMT_INSERT_OR_IGNORE_PRISTINE_F31
 INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount)
 VALUES (?1, ?2, ?3, 0)
 
--- STMT_INSERT_PRISTINE
+-- STMT_INSERT_OR_IGNORE_PRISTINE_F32
+INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount, 
hydrated)
+VALUES (?1, ?2, ?3, 0, ?4)
+
+-- STMT_UPSERT_PRISTINE_F31
 INSERT INTO pristine (checksum, md5_checksum, size, refcount)
 VALUES (?1, ?2, ?3, 0)
+ON CONFLICT(checksum) DO UPDATE SET size=?3
+
+-- STMT_UPSERT_PRISTINE_F32
+INSERT INTO pristine (checksum, md5_checksum, size, refcount, hydrated)
+VALUES (?1, ?2, ?3, 0, ?4)
+ON CONFLICT(checksum) DO UPDATE SET size=?3, hydrated=?4
 
--- STMT_SELECT_PRISTINE
-SELECT md5_checksum
+-- STMT_SELECT_PRISTINE_F31
+SELECT md5_checksum, size, 1
 FROM pristine
 WHERE checksum = ?1
 
--- STMT_SELECT_PRISTINE_SIZE
-SELECT size
+-- STMT_SELECT_PRISTINE_F32
+SELECT md5_checksum, size, hydrated
 FROM pristine
-WHERE checksum = ?1 LIMIT 1
+WHERE checksum = ?1
 
 -- STMT_SELECT_PRISTINE_BY_MD5
 SELECT checksum
@@ -915,9 +925,28 @@ WHERE refcount = 0
 DELETE FROM pristine
 WHERE checksum = ?1 AND refcount = 0
 
--- STMT_SELECT_COPY_PRISTINES
+-- STMT_SELECT_COPY_PRISTINES_F31
+/* For the root itself */
+SELECT n.checksum, md5_checksum, size, 1
+FROM nodes_current n
+LEFT JOIN pristine p ON n.checksum = p.checksum
+WHERE wc_id = ?1
+  AND n.local_relpath = ?2
+  AND n.checksum IS NOT NULL
+UNION ALL
+/* And all descendants */
+SELECT n.checksum, md5_checksum, size, 1
+FROM nodes n
+LEFT JOIN pristine p ON n.checksum = p.checksum
+WHERE wc_id = ?1
+  AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
+  AND op_depth >=
+      (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2)
+  AND n.checksum IS NOT NULL
+
+-- STMT_SELECT_COPY_PRISTINES_F32
 /* For the root itself */
-SELECT n.checksum, md5_checksum, size
+SELECT n.checksum, md5_checksum, size, p.hydrated
 FROM nodes_current n
 LEFT JOIN pristine p ON n.checksum = p.checksum
 WHERE wc_id = ?1
@@ -925,7 +954,7 @@ WHERE wc_id = ?1
   AND n.checksum IS NOT NULL
 UNION ALL
 /* And all descendants */
-SELECT n.checksum, md5_checksum, size
+SELECT n.checksum, md5_checksum, size, p.hydrated
 FROM nodes n
 LEFT JOIN pristine p ON n.checksum = p.checksum
 WHERE wc_id = ?1
@@ -934,6 +963,10 @@ WHERE wc_id = ?1
       (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2)
   AND n.checksum IS NOT NULL
 
+-- STMT_UPDATE_PRISTINE_HYDRATED
+UPDATE pristine SET hydrated = ?2
+WHERE checksum = ?1
+
 -- STMT_VACUUM
 VACUUM
 
@@ -1812,6 +1845,61 @@ SELECT lock_token, lock_owner, lock_comm
 FROM lock
 WHERE repos_id = ?1 AND (repos_relpath = ?2)
 
+-- STMT_TEXTBASE_ADD_REF
+INSERT OR IGNORE INTO textbase_refs (wc_id, local_relpath, op_depth)
+VALUES (?1, ?2, ?3)
+
+-- STMT_TEXTBASE_REMOVE_REF
+DELETE FROM textbase_refs
+WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
+
+-- STMT_TEXTBASE_WALK
+SELECT refs.wc_id IS NOT NULL,
+       nodes.local_relpath, nodes.op_depth,
+       nodes.checksum, nodes.properties,
+       nodes.translated_size, nodes.last_mod_time,
+       (SELECT properties FROM ACTUAL_NODE a
+        WHERE a.wc_id = ?1
+          AND a.local_relpath = nodes.local_relpath
+        LIMIT 1),
+       (SELECT MAX(op_depth)
+          FROM NODES w
+         WHERE w.wc_id = ?1
+           AND w.local_relpath = nodes.local_relpath)
+FROM nodes
+LEFT OUTER JOIN textbase_refs refs ON nodes.wc_id = refs.wc_id
+                                  AND nodes.local_relpath = refs.local_relpath
+                                  AND nodes.op_depth = refs.op_depth
+WHERE nodes.wc_id = ?1
+  AND (nodes.local_relpath = ?2
+       OR IS_STRICT_DESCENDANT_OF(nodes.local_relpath, ?2))
+  AND nodes.checksum IS NOT NULL
+
+-- STMT_TEXTBASE_SYNC
+SELECT pristine.checksum,
+       MIN(pristine.hydrated != 0),
+       MAX(refs.wc_id IS NOT NULL),
+       nodes.repos_path, nodes.repos_id, nodes.revision
+FROM nodes
+JOIN pristine ON nodes.wc_id = ?1
+             AND nodes.checksum = pristine.checksum
+             AND (nodes.local_relpath = ?2
+                  OR IS_STRICT_DESCENDANT_OF(nodes.local_relpath, ?2))
+LEFT OUTER JOIN textbase_refs refs ON nodes.wc_id = refs.wc_id
+                                  AND nodes.local_relpath = refs.local_relpath
+                                  AND nodes.op_depth = refs.op_depth
+GROUP BY pristine.checksum
+UNION ALL
+SELECT pristine.checksum, pristine.hydrated, 0, NULL, NULL, NULL
+FROM pristine WHERE refcount = 0
+
+-- STMT_SELECT_SETTINGS
+SELECT store_pristine FROM settings WHERE wc_id = ?1
+
+-- STMT_UPSERT_SETTINGS
+INSERT INTO settings (wc_id, store_pristine)
+VALUES (?1, ?2)
+ON CONFLICT(wc_id) DO UPDATE SET store_pristine=?2
 
 /* ------------------------------------------------------------------------- */
 

Modified: subversion/trunk/subversion/libsvn_wc/wc.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc.h?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc.h Tue Dec 13 09:49:29 2022
@@ -162,11 +162,10 @@ extern "C" {
  * == 1.10.x shipped with format 31
  * == 1.11.x shipped with format 31
  *
- * The bump to 32 is a no-op, used for testing the multi-wc-format branch.
- * Downgrading from format 32 to format 31 is just a matter of running
- * "PRAGMA user_version = 31;".
+ * The bump to 32 adds support for optional pristine contents; see the 
docstring
+ * of STMT_UPGRADE_TO_32 for details.
  *
- * == 1.15.x shipped with format 32
+ * == 1.15.x shipped with format 32 and multi-wc-format support
  *
  * Please document any further format changes here.
  */
@@ -220,6 +219,13 @@ extern "C" {
    sqlite_stat1 table on opening */
 #define SVN_WC__ENSURE_STAT1_TABLE 31
 
+/* Starting from this version, pristine content is optional and can be
+ * fetched on demand.  */
+#define SVN_WC__HAS_OPTIONAL_PRISTINE 32
+
+/* Starting from this version, the DB stores per-WC settings. */
+#define SVN_WC__HAS_SETTINGS 32
+
 /* Return a string indicating the released version (or versions) of
  * Subversion that used WC format number WC_FORMAT, or some other
  * suitable string if no released version used WC_FORMAT.
@@ -418,7 +424,7 @@ svn_wc__internal_file_modified_p(svn_boo
 
    Property changes sent by the update are provided in PROP_DIFF.
 
-   For a complete description, see svn_wc_merge5() for which this is
+   For a complete description, see svn_wc_merge6() for which this is
    the (loggy) implementation.
 
    *WORK_ITEMS will be allocated in RESULT_POOL. All temporary allocations
@@ -504,7 +510,7 @@ svn_wc__conflicted_for_update_p(svn_bool
                                 apr_pool_t *scratch_pool);
 
 
-/* Internal version of svn_wc_transmit_text_deltas3(). */
+/* Internal version of svn_wc_transmit_text_deltas4(). */
 svn_error_t *
 svn_wc__internal_transmit_text_deltas(svn_stream_t *tempstream,
                                       const svn_checksum_t 
**new_text_base_md5_checksum,
@@ -535,6 +541,7 @@ svn_wc__internal_ensure_adm(svn_wc__db_t
                             const char *repos_uuid,
                             svn_revnum_t revision,
                             svn_depth_t depth,
+                            svn_boolean_t store_pristine,
                             apr_pool_t *scratch_pool);
 
 

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Tue Dec 13 09:49:29 2022
@@ -1366,6 +1366,7 @@ init_db(/* output values */
         const char *root_node_repos_relpath,
         svn_revnum_t root_node_revision,
         svn_depth_t root_node_depth,
+        svn_boolean_t store_pristine,
         const char *wcroot_abspath,
         apr_pool_t *scratch_pool)
 {
@@ -1389,6 +1390,13 @@ init_db(/* output values */
   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
   SVN_ERR(svn_sqlite__insert(wc_id, stmt));
 
+  if (target_format >= SVN_WC__HAS_SETTINGS)
+    {
+      SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPSERT_SETTINGS));
+      SVN_ERR(svn_sqlite__bindf(stmt, "id", *wc_id, store_pristine));
+      SVN_ERR(svn_sqlite__insert(NULL, stmt));
+    }
+
   if (root_node_repos_relpath)
     {
       svn_wc__db_status_t status = svn_wc__db_status_normal;
@@ -1436,6 +1444,7 @@ create_db(svn_sqlite__db_t **sdb,
           const char *root_node_repos_relpath,
           svn_revnum_t root_node_revision,
           svn_depth_t root_node_depth,
+          svn_boolean_t store_pristine,
           svn_boolean_t exclusive,
           apr_int32_t timeout,
           apr_pool_t *result_pool,
@@ -1450,7 +1459,8 @@ create_db(svn_sqlite__db_t **sdb,
   SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
                                 *sdb, target_format, repos_root_url, 
repos_uuid,
                                 root_node_repos_relpath, root_node_revision,
-                                root_node_depth, dir_abspath, scratch_pool),
+                                root_node_depth, store_pristine, dir_abspath,
+                                scratch_pool),
                         *sdb);
 
   return SVN_NO_ERROR;
@@ -1466,6 +1476,7 @@ svn_wc__db_init(svn_wc__db_t *db,
                 const char *repos_uuid,
                 svn_revnum_t initial_rev,
                 svn_depth_t depth,
+                svn_boolean_t store_pristine,
                 apr_pool_t *scratch_pool)
 {
   svn_sqlite__db_t *sdb;
@@ -1495,7 +1506,7 @@ svn_wc__db_init(svn_wc__db_t *db,
   /* Create the SDB and insert the basic rows.  */
   SVN_ERR(create_db(&sdb, &repos_id, &wc_id, target_format, local_abspath,
                     repos_root_url, repos_uuid, SDB_FILE,
-                    repos_relpath, initial_rev, depth,
+                    repos_relpath, initial_rev, depth, store_pristine,
                     sqlite_exclusive, sqlite_timeout,
                     db->state_pool, scratch_pool));
 
@@ -1504,6 +1515,7 @@ svn_wc__db_init(svn_wc__db_t *db,
                         apr_pstrdup(db->state_pool, local_abspath),
                         sdb, wc_id, FORMAT_FROM_SDB,
                         FALSE /* auto-upgrade */,
+                        store_pristine,
                         db->state_pool, scratch_pool));
 
   /* Any previously cached children may now have a new WCROOT, most likely that
@@ -1537,10 +1549,11 @@ svn_wc__db_init(svn_wc__db_t *db,
 
 
 svn_error_t *
-svn_wc__db_get_format(int *format,
-                      svn_wc__db_t *db,
-                      const char *local_abspath,
-                      apr_pool_t *scratch_pool)
+svn_wc__db_get_settings(int *format_p,
+                        svn_boolean_t *store_pristine_p,
+                        svn_wc__db_t *db,
+                        const char *local_abspath,
+                        apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
@@ -1552,7 +1565,11 @@ svn_wc__db_get_format(int *format,
               local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  *format = wcroot->format;
+  if (format_p)
+    *format_p = wcroot->format;
+  if (store_pristine_p)
+    *store_pristine_p = wcroot->store_pristine;
+
   return SVN_NO_ERROR;
 }
 
@@ -13442,6 +13459,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_
                          const char *dir_abspath,
                          const char *repos_root_url,
                          const char *repos_uuid,
+                         svn_boolean_t store_pristine,
                          apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
@@ -13451,6 +13469,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_
                     dir_abspath, repos_root_url, repos_uuid,
                     SDB_FILE,
                     NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
+                    store_pristine,
                     TRUE /* exclusive */,
                     0 /* timeout */,
                     wc_db->state_pool, scratch_pool));
@@ -13460,6 +13479,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_
                                                    dir_abspath),
                                        *sdb, *wc_id, FORMAT_FROM_SDB,
                                        FALSE /* auto-upgrade */,
+                                       store_pristine,
                                        wc_db->state_pool, scratch_pool));
 
   /* The WCROOT is complete. Stash it into DB.  */

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.h Tue Dec 13 09:49:29 2022
@@ -310,17 +310,20 @@ svn_wc__db_init(svn_wc__db_t *db,
                 const char *repos_uuid,
                 svn_revnum_t initial_rev,
                 svn_depth_t depth,
+                svn_boolean_t store_pristine,
                 apr_pool_t *scratch_pool);
 
-/* Return the working copy format for LOCAL_ABSPATH in DB in *FORMAT.
+/* Return the working copy settings *FORMAT_P and *STORE_PRISTINE_P for
+   LOCAL_ABSPATH in DB.
 
    Use SCRATCH_POOL for temporary allocations.
 */
 svn_error_t *
-svn_wc__db_get_format(int *format,
-                      svn_wc__db_t *db,
-                      const char *local_abspath,
-                      apr_pool_t *scratch_pool);
+svn_wc__db_get_settings(int *format_p,
+                        svn_boolean_t *store_pristine_p,
+                        svn_wc__db_t *db,
+                        const char *local_abspath,
+                        apr_pool_t *scratch_pool);
 
 /* Compute the LOCAL_RELPATH for the given LOCAL_ABSPATH, relative
    from wri_abspath.
@@ -927,21 +930,6 @@ svn_wc__db_base_get_lock_tokens_recursiv
    @{
 */
 
-/* Set *PRISTINE_ABSPATH to the path to the pristine text file
-   identified by SHA1_CHECKSUM.  Error if it does not exist.
-
-   ### This is temporary - callers should not be looking at the file
-   directly.
-
-   Allocate the path in RESULT_POOL. */
-svn_error_t *
-svn_wc__db_pristine_get_path(const char **pristine_abspath,
-                             svn_wc__db_t *db,
-                             const char *wri_abspath,
-                             const svn_checksum_t *checksum,
-                             apr_pool_t *result_pool,
-                             apr_pool_t *scratch_pool);
-
 /* Set *PRISTINE_ABSPATH to the path under WCROOT_ABSPATH that will be
    used by the pristine text identified by SHA1_CHECKSUM.  The file
    need not exist.
@@ -956,7 +944,8 @@ svn_wc__db_pristine_get_future_path(cons
 
 /* If requested set *CONTENTS to a readable stream that will yield the pristine
    text identified by SHA1_CHECKSUM (must be a SHA-1 checksum) within the WC
-   identified by WRI_ABSPATH in DB.
+   identified by WRI_ABSPATH in DB.  If the pristine is present in the store,
+   but dehydrated, set *CONTENTS to NULL.
 
    If requested set *SIZE to the size of the pristine stream in bytes,
 
@@ -986,6 +975,9 @@ typedef struct svn_wc__db_install_data_t
    set to the MD-5 and SHA-1 checksums respectively of that file.
    MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted.
 
+   The contents of the pristine will be saved to disk if HYDRATED is true or
+   if the WC version or configuration doesn't allow dehydrated pristines.
+
    Allocate the new stream, path and checksums in RESULT_POOL.
  */
 svn_error_t *
@@ -995,6 +987,7 @@ svn_wc__db_pristine_prepare_install(svn_
                                     svn_checksum_t **md5_checksum,
                                     svn_wc__db_t *db,
                                     const char *wri_abspath,
+                                    svn_boolean_t hydrated,
                                     apr_pool_t *result_pool,
                                     apr_pool_t *scratch_pool);
 
@@ -1077,14 +1070,26 @@ svn_wc__db_pristine_cleanup(svn_wc__db_t
 
 /* Set *PRESENT to true if the pristine store for WRI_ABSPATH in DB contains
    a pristine text with SHA-1 checksum SHA1_CHECKSUM, and to false otherwise.
-*/
+   If the pristine is present, set *HYDRATED to true if its contents are
+   currently available on disk, and to false otherwise.  If the pristine
+   is not present, set *HYDRATED to false. */
 svn_error_t *
 svn_wc__db_pristine_check(svn_boolean_t *present,
+                          svn_boolean_t *hydrated,
                           svn_wc__db_t *db,
                           const char *wri_abspath,
                           const svn_checksum_t *sha1_checksum,
                           apr_pool_t *scratch_pool);
 
+/* If the pristine store for WRI_ABSPATH in DB contains a pristine text with
+   SHA-1 checksum SHA1_CHECKSUM with its content available on disk, remove
+   that content and mark the pristine entry as "dehydrated". */
+svn_error_t *
+svn_wc__db_pristine_dehydrate(svn_wc__db_t *db,
+                              const char *wri_abspath,
+                              const svn_checksum_t *sha1_checksum,
+                              apr_pool_t *scratch_pool);
+
 /* @defgroup svn_wc__db_external  External management
    @{ */
 
@@ -2960,6 +2965,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_
                          const char *local_dir_abspath,
                          const char *repos_root_url,
                          const char *repos_uuid,
+                         svn_boolean_t store_pristine,
                          apr_pool_t *scratch_pool);
 
 /* Simply insert (or replace) one row in the EXTERNALS table. */
@@ -3102,6 +3108,77 @@ svn_wc__db_wclock_owns_lock(svn_boolean_
                             svn_boolean_t exact,
                             apr_pool_t *scratch_pool);
 
+/* @} */
+
+
+/* @defgroup svn_wc__db_textbase  Working with text-bases
+   @{
+*/
+
+/* The callback invoked by svn_wc__db_textbase_walk(). */
+typedef svn_error_t * (*svn_wc__db_textbase_walk_cb_t)(
+  svn_boolean_t *referenced_p,
+  void *baton,
+  const char *local_abspath,
+  int op_depth,
+  const svn_checksum_t *checksum,
+  svn_boolean_t have_props,
+  svn_boolean_t props_mod,
+  svn_filesize_t recorded_size,
+  apr_time_t recorded_time,
+  int max_op_depth,
+  apr_pool_t *scratch_pool);
+
+/* Walk the text-bases referenced by the nodes in the LOCAL_ABSPATH
+   tree, invoking the CALLBACK with information about each text-base
+   for a node.
+
+   If the callback sets *REFERENCED_P to true, ensure that a text-base
+   reference exists for the node.  If the callback sets *REFERENCED_P to
+   false, ensure that a text-base reference does not exist for the node.
+
+   See the description of the `TEXTBASE_REFS` table in the schema.
+ */
+svn_error_t *
+svn_wc__db_textbase_walk(svn_wc__db_t *db,
+                         const char *local_abspath,
+                         svn_wc__db_textbase_walk_cb_t callback,
+                         void *callback_baton,
+                         svn_cancel_func_t cancel_func,
+                         void *cancel_baton,
+                         apr_pool_t *scratch_pool);
+
+/* The callback invoked by svn_wc__db_textbase_sync(). */
+typedef svn_error_t * (*svn_wc__db_textbase_fetch_cb_t)(
+  void *baton,
+  const char *repos_root_url,
+  const char *repos_relpath,
+  svn_revnum_t revision,
+  svn_stream_t *contents,
+  svn_cancel_func_t cancel_func,
+  void *cancel_baton,
+  apr_pool_t *scratch_pool);
+
+/* Synchronize the state of the text-bases in DB.
+
+   If ALLOW_HYDRATE is true, fetch the referenced but missing text-base
+   contents using the provided FETCH_CALLBACK and FETCH_BATON.
+   If ALLOW_DEHYDRATE is true, remove the on disk text-base contents
+   that is no longer referenced.
+ */
+svn_error_t *
+svn_wc__db_textbase_sync(svn_wc__db_t *db,
+                         const char *local_abspath,
+                         svn_boolean_t allow_hydrate,
+                         svn_boolean_t allow_dehydrate,
+                         svn_wc__db_textbase_fetch_cb_t fetch_callback,
+                         void *fetch_baton,
+                         svn_cancel_func_t cancel_func,
+                         void *cancel_baton,
+                         apr_pool_t *scratch_pool);
+
+
+/* @} */
 
 
 /* @defgroup svn_wc__db_temp Various temporary functions during transition

Modified: subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_pristine.c Tue Dec 13 09:49:29 
2022
@@ -94,50 +94,6 @@ get_pristine_fname(const char **pristine
 
 
 svn_error_t *
-svn_wc__db_pristine_get_path(const char **pristine_abspath,
-                             svn_wc__db_t *db,
-                             const char *wri_abspath,
-                             const svn_checksum_t *sha1_checksum,
-                             apr_pool_t *result_pool,
-                             apr_pool_t *scratch_pool)
-{
-  svn_wc__db_wcroot_t *wcroot;
-  const char *local_relpath;
-  svn_boolean_t present;
-
-  SVN_ERR_ASSERT(pristine_abspath != NULL);
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
-  SVN_ERR_ASSERT(sha1_checksum != NULL);
-  /* ### Transitional: accept MD-5 and look up the SHA-1.  Return an error
-   * if the pristine text is not in the store. */
-  if (sha1_checksum->kind != svn_checksum_sha1)
-    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
-                                         sha1_checksum,
-                                         scratch_pool, scratch_pool));
-  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
-
-  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
-                                             db, wri_abspath,
-                                             scratch_pool, scratch_pool));
-  VERIFY_USABLE_WCROOT(wcroot);
-
-  SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum,
-                                    scratch_pool));
-  if (! present)
-    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
-                             _("The pristine text with checksum '%s' was "
-                               "not found"),
-                             svn_checksum_to_cstring_display(sha1_checksum,
-                                                             scratch_pool));
-
-  SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath,
-                             sha1_checksum,
-                             result_pool, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_wc__db_pristine_get_future_path(const char **pristine_abspath,
                                     const char *wcroot_abspath,
                                     const svn_checksum_t *sha1_checksum,
@@ -150,11 +106,58 @@ svn_wc__db_pristine_get_future_path(cons
   return SVN_NO_ERROR;
 }
 
+/* Read *HAVE_ROW, *MD5_CHECKSUM, *SIZE, *HYDRATED for WCROOT:SHA1_CHECKSUM.
+ * If it returns with *HAVE_ROW=FALSE, other outputs are null/zero/false.
+ */
+static svn_error_t *
+stmt_select_pristine(svn_boolean_t *have_row,
+                     const svn_checksum_t **md5_checksum,
+                     svn_filesize_t *size,
+                     svn_boolean_t *hydrated,
+                     svn_wc__db_wcroot_t *wcroot,
+                     const svn_checksum_t *sha1_checksum,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
+{
+  int stmt_num;
+  svn_sqlite__stmt_t *stmt;
+
+  /* Check that this pristine text is present in the store.  (The presence
+   * of the file is not sufficient.) */
+  stmt_num = (wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE
+              ? STMT_SELECT_PRISTINE_F32
+              : STMT_SELECT_PRISTINE_F31);
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_num));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__step(have_row, stmt));
+  if (!*have_row)
+    {
+      if (md5_checksum)
+        *md5_checksum = NULL;
+      if (size)
+        *size = 0;
+      if (hydrated)
+        *hydrated = FALSE;
+      return svn_sqlite__reset(stmt);
+    }
+
+  if (md5_checksum)
+    SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool));
+  if (size)
+    *size = svn_sqlite__column_int64(stmt, 1);
+  if (hydrated)
+    *hydrated = svn_sqlite__column_boolean(stmt, 2);
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+  return SVN_NO_ERROR;
+}
+
 /* Set *CONTENTS to a readable stream from which the pristine text
  * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the
- * pristine store of WCROOT.  If SIZE is not null, set *SIZE to the size
- * in bytes of that text. If that text is not in the pristine store,
- * return an error.
+ * pristine store of WCROOT.  If the pristine contents are currently not
+ * available on disk, set *CONTENTS to NULL.  If SIZE is not null, set
+ * *SIZE to the size in bytes of that text.  If that text is not in
+ * the pristine store, return an error.
  *
  * Even if the pristine text is removed from the store while it is being
  * read, the stream will remain valid and readable until it is closed.
@@ -174,20 +177,14 @@ pristine_read_txn(svn_stream_t **content
                   apr_pool_t *result_pool,
                   apr_pool_t *scratch_pool)
 {
-  svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  svn_boolean_t hydrated;
 
   /* Check that this pristine text is present in the store.  (The presence
    * of the file is not sufficient.) */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_PRISTINE_SIZE));
-  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-
-  if (size)
-    *size = svn_sqlite__column_int64(stmt, 0);
-
-  SVN_ERR(svn_sqlite__reset(stmt));
+  SVN_ERR(stmt_select_pristine(&have_row, NULL, size, &hydrated,
+                               wcroot, sha1_checksum,
+                               result_pool, scratch_pool));
   if (! have_row)
     {
       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
@@ -196,19 +193,27 @@ pristine_read_txn(svn_stream_t **content
                                  sha1_checksum, scratch_pool));
     }
 
-  /* Open the file as a readable stream.  It will remain readable even when
-   * deleted from disk; APR guarantees that on Windows as well as Unix.
-   *
-   * We also don't enable APR_BUFFERED on this file to maximize throughput
-   * e.g. for fulltext comparison.  As we use SVN__STREAM_CHUNK_SIZE buffers
-   * where needed in streams, there is no point in having another layer of
-   * buffers. */
   if (contents)
     {
-      apr_file_t *file;
-      SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
-                               APR_OS_DEFAULT, result_pool));
-      *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
+      if (hydrated)
+        {
+          /* Open the file as a readable stream.  It will remain readable even 
when
+           * deleted from disk; APR guarantees that on Windows as well as Unix.
+           *
+           * We also don't enable APR_BUFFERED on this file to maximize 
throughput
+           * e.g. for fulltext comparison.  As we use SVN__STREAM_CHUNK_SIZE 
buffers
+           * where needed in streams, there is no point in having another 
layer of
+           * buffers. */
+
+          apr_file_t *file;
+          SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
+                                   APR_OS_DEFAULT, result_pool));
+          *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
+        }
+      else
+        {
+          *contents = NULL;
+        }
     }
 
   return SVN_NO_ERROR;
@@ -256,6 +261,53 @@ svn_wc__db_pristine_read(svn_stream_t **
 }
 
 
+struct svn_wc__db_install_data_t
+{
+  svn_wc__db_wcroot_t *wcroot;
+  svn_stream_t *inner_stream;
+  apr_off_t size;
+};
+
+static svn_error_t *
+install_stream_write_fn(void *baton, const char *data, apr_size_t *len)
+{
+  svn_wc__db_install_data_t *install_data = baton;
+
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream_write(install_data->inner_stream, data, len));
+
+  install_data->size += *len;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+install_stream_seek_fn(void *baton, const svn_stream_mark_t *mark)
+{
+  svn_wc__db_install_data_t *install_data = baton;
+
+  if (!mark)
+    return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
+
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream_reset(install_data->inner_stream));
+
+  install_data->size = 0;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+install_stream_close_fn(void *baton)
+{
+  svn_wc__db_install_data_t *install_data = baton;
+
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream_close(install_data->inner_stream));
+
+  return SVN_NO_ERROR;
+}
+
 /* Return the absolute path to the temporary directory for pristine text
    files within WCROOT. */
 static char *
@@ -268,9 +320,8 @@ pristine_get_tempdir(svn_wc__db_wcroot_t
                               PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL);
 }
 
-/* Install the pristine text described by BATON into the pristine store of
- * SDB.  If it is already stored then just delete the new file
- * BATON->tempfile_abspath.
+/* Install the pristine text described by INSTALL_DATA into the pristine store
+ * of SDB.
  *
  * This function expects to be executed inside a SQLite txn that has already
  * acquired a 'RESERVED' lock.
@@ -278,9 +329,8 @@ pristine_get_tempdir(svn_wc__db_wcroot_t
  * Implements 'notes/wc-ng/pristine-store' section A-3(a).
  */
 static svn_error_t *
-pristine_install_txn(svn_sqlite__db_t *sdb,
-                     /* The path to the source file that is to be moved into 
place. */
-                     svn_stream_t *install_stream,
+pristine_install_txn(svn_wc__db_wcroot_t *wcroot,
+                     svn_wc__db_install_data_t *install_data,
                      /* The target path for the file (within the pristine 
store). */
                      const char *pristine_abspath,
                      /* The pristine text's SHA-1 checksum. */
@@ -289,88 +339,70 @@ pristine_install_txn(svn_sqlite__db_t *s
                      const svn_checksum_t *md5_checksum,
                      apr_pool_t *scratch_pool)
 {
+  svn_stream_t *install_stream = install_data->inner_stream;
+  int stmt_num;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  svn_boolean_t hydrated;
 
-  /* If this pristine text is already present in the store, just keep it:
-   * delete the new one and return. */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE));
-  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  SVN_ERR(svn_sqlite__reset(stmt));
+  SVN_ERR(stmt_select_pristine(&have_row, NULL, NULL, &hydrated,
+                               wcroot, sha1_checksum,
+                               scratch_pool, scratch_pool));
 
-  if (have_row)
+  if (have_row && hydrated)
     {
-#ifdef SVN_DEBUG
-      /* Consistency checks.  Verify both files exist and match.
-       * ### We could check much more. */
-      {
-        apr_finfo_t finfo;
-        apr_off_t size;
-
-        SVN_ERR(svn_stream__install_finalize(NULL, &size, install_stream,
-                                             scratch_pool));
-
-        SVN_ERR(svn_io_stat(&finfo, pristine_abspath, APR_FINFO_SIZE,
-                            scratch_pool));
-        if (size != finfo.size)
-          {
-            return svn_error_createf(
-              SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
-              _("New pristine text '%s' has different size: %s versus %s"),
-              svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
-              apr_off_t_toa(scratch_pool, size),
-              apr_off_t_toa(scratch_pool, finfo.size));
-          }
-      }
-#endif
+      /* For now, ensure that we do not inadvertently dehydrate an existing
+       * hydrated entry, as there could be references to its content. */
+
+      if (install_stream)
+        SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
 
-      /* Remove the temp file: it's already there */
-      SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
       return SVN_NO_ERROR;
     }
 
-  /* Move the file to its target location.  (If it is already there, it is
-   * an orphan file and it doesn't matter if we overwrite it.) */
-  {
-    apr_off_t size;
+  if (install_stream)
+    {
+      /* Move the file to its target location.  (If it is already there, it is
+       * an orphan file and it doesn't matter if we overwrite it.) */
 
-    svn_stream__install_set_read_only(install_stream, TRUE);
+      svn_stream__install_set_read_only(install_stream, TRUE);
 
-    SVN_ERR(svn_stream__install_finalize(NULL, &size, install_stream,
-                                         scratch_pool));
-    SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
-                                       TRUE, scratch_pool));
-
-    SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE));
-    SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-    SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
-    SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
-    SVN_ERR(svn_sqlite__insert(NULL, stmt));
-  }
+      SVN_ERR(svn_stream__install_finalize(NULL, NULL, install_stream,
+                                           scratch_pool));
+      SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
+                                         TRUE, scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
+    }
+
+  stmt_num = (wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE
+              ? STMT_UPSERT_PRISTINE_F32 : STMT_UPSERT_PRISTINE_F31);
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_num));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__bind_int64(stmt, 3, install_data->size));
+  if (wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE)
+    SVN_ERR(svn_sqlite__bind_int(stmt, 4, install_stream != NULL));
+  SVN_ERR(svn_sqlite__insert(NULL, stmt));
 
   return SVN_NO_ERROR;
 }
 
-struct svn_wc__db_install_data_t
-{
-  svn_wc__db_wcroot_t *wcroot;
-  svn_stream_t *inner_stream;
-};
-
 svn_error_t *
-svn_wc__db_pristine_prepare_install(svn_stream_t **stream,
-                                    svn_wc__db_install_data_t **install_data,
-                                    svn_checksum_t **sha1_checksum,
-                                    svn_checksum_t **md5_checksum,
+svn_wc__db_pristine_prepare_install(svn_stream_t **stream_p,
+                                    svn_wc__db_install_data_t **install_data_p,
+                                    svn_checksum_t **sha1_checksum_p,
+                                    svn_checksum_t **md5_checksum_p,
                                     svn_wc__db_t *db,
                                     const char *wri_abspath,
+                                    svn_boolean_t hydrated,
                                     apr_pool_t *result_pool,
                                     apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  const char *temp_dir_abspath;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
 
@@ -378,24 +410,60 @@ svn_wc__db_pristine_prepare_install(svn_
                               wri_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
+  SVN_ERR(svn_wc__db_pristine_prepare_install_internal(
+            stream_p, install_data_p, sha1_checksum_p, md5_checksum_p,
+            wcroot, hydrated, result_pool, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_pristine_prepare_install_internal(svn_stream_t **stream_p,
+                                             svn_wc__db_install_data_t 
**install_data_p,
+                                             svn_checksum_t **sha1_checksum_p,
+                                             svn_checksum_t **md5_checksum_p,
+                                             svn_wc__db_wcroot_t *wcroot,
+                                             svn_boolean_t hydrated,
+                                             apr_pool_t *result_pool,
+                                             apr_pool_t *scratch_pool)
+{
+  svn_stream_t *stream;
+  svn_wc__db_install_data_t *install_data;
+  const char *temp_dir_abspath;
+
   temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
 
-  *install_data = apr_pcalloc(result_pool, sizeof(**install_data));
-  (*install_data)->wcroot = wcroot;
+  install_data = apr_pcalloc(result_pool, sizeof(*install_data));
+  install_data->wcroot = wcroot;
 
-  SVN_ERR_W(svn_stream__create_for_install(stream,
-                                           temp_dir_abspath,
-                                           result_pool, scratch_pool),
-            _("Unable to create pristine install stream"));
+  if (hydrated || wcroot->store_pristine)
+    {
+      SVN_ERR_W(svn_stream__create_for_install(&install_data->inner_stream,
+                                               temp_dir_abspath,
+                                               result_pool, scratch_pool),
+                _("Unable to create pristine install stream"));
+    }
+  else
+    {
+      install_data->inner_stream = NULL;
+    }
 
-  (*install_data)->inner_stream = *stream;
+  install_data->size = 0;
 
-  if (md5_checksum)
-    *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
-                                      svn_checksum_md5, FALSE, result_pool);
-  if (sha1_checksum)
-    *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
-                                      svn_checksum_sha1, FALSE, result_pool);
+  stream = svn_stream_create(install_data, result_pool);
+  svn_stream_set_write(stream, install_stream_write_fn);
+  svn_stream_set_seek(stream, install_stream_seek_fn);
+  svn_stream_set_close(stream, install_stream_close_fn);
+
+  if (md5_checksum_p)
+    stream = svn_stream_checksummed2(stream, NULL, md5_checksum_p,
+                                     svn_checksum_md5, FALSE, result_pool);
+  if (sha1_checksum_p)
+    stream = svn_stream_checksummed2(stream, NULL, sha1_checksum_p,
+                                     svn_checksum_sha1, FALSE, result_pool);
+
+  *stream_p = stream;
+  *install_data_p = install_data;
 
   return SVN_NO_ERROR;
 }
@@ -421,8 +489,8 @@ svn_wc__db_pristine_install(svn_wc__db_i
   /* Ensure the SQL txn has at least a 'RESERVED' lock before we start looking
    * at the disk, to ensure no concurrent pristine install/delete txn. */
   SVN_SQLITE__WITH_IMMEDIATE_TXN(
-    pristine_install_txn(wcroot->sdb,
-                         install_data->inner_stream, pristine_abspath,
+    pristine_install_txn(wcroot,
+                         install_data, pristine_abspath,
                          sha1_checksum, md5_checksum,
                          scratch_pool),
     wcroot->sdb);
@@ -434,8 +502,12 @@ svn_error_t *
 svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data,
                                   apr_pool_t *scratch_pool)
 {
-  return svn_error_trace(svn_stream__install_delete(install_data->inner_stream,
-                                                    scratch_pool));
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream__install_delete(install_data->inner_stream, 
scratch_pool));
+
+  install_data->size = 0;
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -449,7 +521,6 @@ svn_wc__db_pristine_get_md5(const svn_ch
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
@@ -460,20 +531,19 @@ svn_wc__db_pristine_get_md5(const svn_ch
                               wri_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
-  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  SVN_ERR(stmt_select_pristine(&have_row, md5_checksum, NULL, NULL,
+                               wcroot, sha1_checksum,
+                               result_pool, scratch_pool));
   if (!have_row)
-    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
+    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
                              _("The pristine text with checksum '%s' was "
                                "not found"),
                              svn_checksum_to_cstring_display(sha1_checksum,
                                                              scratch_pool));
 
-  SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool));
   SVN_ERR_ASSERT((*md5_checksum)->kind == svn_checksum_md5);
 
-  return svn_error_trace(svn_sqlite__reset(stmt));
+  return SVN_NO_ERROR;
 }
 
 
@@ -516,84 +586,95 @@ svn_wc__db_pristine_get_sha1(const svn_c
 }
 
 /* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing
-   pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM and SIZE */
+   pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM, SIZE and
+   HYDRATED. */
 static svn_error_t *
 maybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot,
                             svn_wc__db_wcroot_t *dst_wcroot,
                             const svn_checksum_t *checksum,
                             const svn_checksum_t *md5_checksum,
                             apr_int64_t size,
+                            svn_boolean_t hydrated,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
                             apr_pool_t *scratch_pool)
 {
-  const char *pristine_abspath;
+  int stmt_num;
   svn_sqlite__stmt_t *stmt;
-  svn_stream_t *src_stream;
-  svn_stream_t *dst_stream;
-  const char *tmp_abspath;
-  const char *src_abspath;
   int affected_rows;
-  svn_error_t *err;
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
-                                    STMT_INSERT_OR_IGNORE_PRISTINE));
+  stmt_num = (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE
+              ? STMT_INSERT_OR_IGNORE_PRISTINE_F32
+              : STMT_INSERT_OR_IGNORE_PRISTINE_F31);
+  SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb, stmt_num));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
   SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
+  if (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE)
+    SVN_ERR(svn_sqlite__bind_int(stmt, 4, hydrated));
 
   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
   if (affected_rows == 0)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
-                                 pristine_get_tempdir(dst_wcroot,
-                                                      scratch_pool,
-                                                      scratch_pool),
-                                 svn_io_file_del_on_pool_cleanup,
-                                 scratch_pool, scratch_pool));
+  if (hydrated)
+    {
+      const char *pristine_abspath;
+      svn_stream_t *src_stream;
+      svn_stream_t *dst_stream;
+      const char *tmp_abspath;
+      const char *src_abspath;
+      svn_error_t *err;
 
-  SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
-                             scratch_pool, scratch_pool));
+      SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
+                                     pristine_get_tempdir(dst_wcroot,
+                                                          scratch_pool,
+                                                          scratch_pool),
+                                     svn_io_file_del_on_pool_cleanup,
+                                     scratch_pool, scratch_pool));
 
-  SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
-                                   scratch_pool, scratch_pool));
+      SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
+                                 scratch_pool, scratch_pool));
 
-  /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
-  SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
-                           cancel_func, cancel_baton,
-                           scratch_pool));
+      SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
+                                       scratch_pool, scratch_pool));
 
-  SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
-                             scratch_pool, scratch_pool));
+      /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? 
*/
+      SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
+                               cancel_func, cancel_baton,
+                               scratch_pool));
 
-  /* Move the file to its target location.  (If it is already there, it is
-   * an orphan file and it doesn't matter if we overwrite it.) */
-  err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
-                            scratch_pool);
-
-  /* Maybe the directory doesn't exist yet? */
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
-    {
-      svn_error_t *err2;
-
-      err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
-                                                scratch_pool),
-                             APR_OS_DEFAULT, scratch_pool);
-
-      if (err2)
-        /* Creating directory didn't work: Return all errors */
-        return svn_error_trace(svn_error_compose_create(err, err2));
-      else
-        /* We could create a directory: retry install */
-        svn_error_clear(err);
+      SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, 
checksum,
+                                 scratch_pool, scratch_pool));
 
-      SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
-                                  scratch_pool));
+      /* Move the file to its target location.  (If it is already there, it is
+       * an orphan file and it doesn't matter if we overwrite it.) */
+      err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
+                                scratch_pool);
+
+      /* Maybe the directory doesn't exist yet? */
+      if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+        {
+          svn_error_t *err2;
+
+          err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
+                                                    scratch_pool),
+                                 APR_OS_DEFAULT, scratch_pool);
+
+          if (err2)
+            /* Creating directory didn't work: Return all errors */
+            return svn_error_trace(svn_error_compose_create(err, err2));
+          else
+            /* We could create a directory: retry install */
+            svn_error_clear(err);
+
+          SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
+                                      scratch_pool));
+        }
+      else
+        SVN_ERR(err);
     }
-  else
-    SVN_ERR(err);
 
   return SVN_NO_ERROR;
 }
@@ -609,12 +690,15 @@ pristine_transfer_txn(svn_wc__db_wcroot_
                        void *cancel_baton,
                        apr_pool_t *scratch_pool)
 {
+  int stmt_num;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t got_row;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
-                                    STMT_SELECT_COPY_PRISTINES));
+  stmt_num = (dst_wcroot->format >= SVN_WC__HAS_OPTIONAL_PRISTINE
+              ? STMT_SELECT_COPY_PRISTINES_F32
+              : STMT_SELECT_COPY_PRISTINES_F31);
+  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb, stmt_num));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
 
   /* This obtains an sqlite read lock on src_wcroot */
@@ -625,6 +709,7 @@ pristine_transfer_txn(svn_wc__db_wcroot_
       const svn_checksum_t *checksum;
       const svn_checksum_t *md5_checksum;
       apr_int64_t size;
+      svn_boolean_t hydrated;
       svn_error_t *err;
 
       svn_pool_clear(iterpool);
@@ -632,9 +717,11 @@ pristine_transfer_txn(svn_wc__db_wcroot_
       SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool));
       SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool));
       size = svn_sqlite__column_int64(stmt, 2);
+      hydrated = svn_sqlite__column_boolean(stmt, 3);
 
       err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot,
                                         checksum, md5_checksum, size,
+                                        hydrated,
                                         cancel_func, cancel_baton,
                                         iterpool);
 
@@ -714,19 +801,7 @@ pristine_remove_if_unreferenced_txn(svn_
 
   /* If we removed the DB row, then remove the file. */
   if (affected_rows > 0)
-    {
-      /* If the file is not present, something has gone wrong, but at this
-       * point it no longer matters.  In a debug build, raise an error, but
-       * in a release build, it is more helpful to ignore it and continue. */
-#ifdef SVN_DEBUG
-      svn_boolean_t ignore_enoent = FALSE;
-#else
-      svn_boolean_t ignore_enoent = TRUE;
-#endif
-
-      SVN_ERR(svn_io_remove_file2(pristine_abspath, ignore_enoent,
-                                  scratch_pool));
-    }
+    SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -887,6 +962,7 @@ svn_wc__db_pristine_cleanup(svn_wc__db_t
 
 svn_error_t *
 svn_wc__db_pristine_check(svn_boolean_t *present,
+                          svn_boolean_t *hydrated,
                           svn_wc__db_t *db,
                           const char *wri_abspath,
                           const svn_checksum_t *sha1_checksum,
@@ -894,7 +970,6 @@ svn_wc__db_pristine_check(svn_boolean_t
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
@@ -903,6 +978,9 @@ svn_wc__db_pristine_check(svn_boolean_t
   if (sha1_checksum->kind != svn_checksum_sha1)
     {
       *present = FALSE;
+      if (hydrated)
+        *hydrated = FALSE;
+
       return SVN_NO_ERROR;
     }
 
@@ -910,46 +988,58 @@ svn_wc__db_pristine_check(svn_boolean_t
                               wri_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  /* A filestat is much cheaper than a sqlite transaction especially on NFS,
-     so first check if there is a pristine file and then if we are allowed
-     to use it. */
-  {
-    const char *pristine_abspath;
-    svn_node_kind_t kind_on_disk;
-    svn_error_t *err;
-
-    SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
-                               sha1_checksum, scratch_pool, scratch_pool));
-    err = svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool);
-#ifdef WIN32
-    if (err && err->apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
-      {
-        svn_error_clear(err);
-        /* Possible race condition: The filename is locked, but there is no
-           file or dir with this name. Let's fall back on checking the DB.
-
-           This case is triggered by the pristine store tests on deleting
-           a file that is still open via another handle, where this other
-           handle has a FILE_SHARE_DELETE share mode.
-         */
-      }
-    else
-#endif
-    if (err)
-      return svn_error_trace(err);
-    else if (kind_on_disk != svn_node_file)
-      {
-        *present = FALSE;
-        return SVN_NO_ERROR;
-      }
-  }
-
   /* Check that there is an entry in the PRISTINE table. */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
+  SVN_ERR(stmt_select_pristine(&have_row, NULL, NULL, hydrated,
+                               wcroot, sha1_checksum,
+                               scratch_pool, scratch_pool));
+  *present = have_row;
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__db_pristine_dehydrate(svn_wc__db_t *db,
+                              const char *wri_abspath,
+                              const svn_checksum_t *sha1_checksum,
+                              apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                              wri_abspath, scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_ERR(svn_wc__db_pristine_dehydrate_internal(wcroot, sha1_checksum,
+                                                 scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__db_pristine_dehydrate_internal(svn_wc__db_wcroot_t *wcroot,
+                                       const svn_checksum_t *sha1_checksum,
+                                       apr_pool_t *scratch_pool)
+{
+  const char *pristine_abspath;
+  svn_sqlite__stmt_t *stmt;
+
+  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
+
+  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
+                             sha1_checksum,
+                             scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_PRISTINE_HYDRATED));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  SVN_ERR(svn_sqlite__reset(stmt));
+  SVN_ERR(svn_sqlite__bind_int(stmt, 2, FALSE));
+  SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+  SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
 
-  *present = have_row;
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_wc/wc_db_private.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_private.h?rev=1905955&r1=1905954&r2=1905955&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_private.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_private.h Tue Dec 13 09:49:29 
2022
@@ -107,6 +107,10 @@ typedef struct svn_wc__db_wcroot_t {
      const char *local_abspath -> svn_wc_adm_access_t *adm_access */
   apr_hash_t *access_cache;
 
+  /* Whether to store the pristine contents of all files on disk or
+     to fetch the contents on demand. */
+  svn_boolean_t store_pristine;
+
 } svn_wc__db_wcroot_t;
 
 
@@ -126,6 +130,7 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_
                              apr_int64_t wc_id,
                              int format,
                              svn_boolean_t verify_format,
+                             svn_boolean_t store_pristine,
                              apr_pool_t *result_pool,
                              apr_pool_t *scratch_pool);
 
@@ -561,4 +566,23 @@ svn_wc__db_verify_db_full_internal(svn_w
                                    void *baton,
                                    apr_pool_t *scratch_pool);
 
+/* Like svn_wc__db_pristine_prepare_install() but taking WCROOT instead
+   of DB+WRI_ABSPATH. */
+svn_error_t *
+svn_wc__db_pristine_prepare_install_internal(svn_stream_t **stream_p,
+                                             svn_wc__db_install_data_t 
**install_data_p,
+                                             svn_checksum_t **sha1_checksum_p,
+                                             svn_checksum_t **md5_checksum_p,
+                                             svn_wc__db_wcroot_t *wcroot,
+                                             svn_boolean_t hydrated,
+                                             apr_pool_t *result_pool,
+                                             apr_pool_t *scratch_pool);
+
+/* Like svn_wc__db_pristine_dehydrate() but taking WCROOT instead
+   of DB+WRI_ABSPATH. */
+svn_error_t *
+svn_wc__db_pristine_dehydrate_internal(svn_wc__db_wcroot_t *wcroot,
+                                       const svn_checksum_t *sha1_checksum,
+                                       apr_pool_t *scratch_pool);
+
 #endif /* WC_DB_PRIVATE_H */


Reply via email to