Author: rhuijben
Date: Tue Jul 13 21:08:49 2010
New Revision: 963863

URL: http://svn.apache.org/viewvc?rev=963863&view=rev
Log:
Update the wc-db api for wclocks to have a single set of functions for
obtaining and releasing locks instead of a separate function for recording
and for knowing that we own the lock. Also add an initial implementation
for the lock behavior for when we switch to a single database.

* subversion/libsvn_wc/adm_ops.c
  (svn_wc_add4): Update caller.

* subversion/libsvn_wc/lock.c
  (pool_cleanup_locked): Update caller.
  (adm_access_alloc): Update caller.
  (close_single): Update caller.
  (svn_wc_adm_locked): Update caller.
  (svn_wc__write_check): Update caller.
  (svn_wc_locked2): Update caller.
  (svn_wc__acquire_write_lock): Update caller.
  (svn_wc__release_write_lock): Update caller and handle errors
    on unlocking when we don't have a lock to keep current behavior.

* subversion/libsvn_wc/log.c
  (cleanup_internal): Obtain lock with steal_lock mode, instead of ignoring
    the error.

* subversion/libsvn_wc/upgrade.c
  (upgrade_to_wcng): Update caller.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_FIND_WC_LOCK): New query to obtain existing locks below a
    new recursive lock.

* subversion/libsvn_wc/wc_db.c
  (relpath_op_depth): New helper function.
  (svn_wc__db_temp_forget_directory): Ignore error conditions to
    keep current (mostly broken) behavior.
  (wclock_obtain_baton): New struct.
  (wclock_steal): New helper function.
  (wclock_obtain_cb): New transaction helper function.
  (svn_wc__db_wclock_set): Renamed to ...
  (svn_wc__db_wclock_obtain): ... this. Use a transaction to verify
    current recursive locks and obtain a new lock. (Mostly unused in per
    directory db layout).
  (svn_wc__db_wclock_remove): Renamed to ...
  (svn_wc__db_wclock_release): ... this. Verify lock ownership and add
    support for single db operations.
  (svn_wc__db_temp_mark_locked): Remove function.
  (svn_wc__db_temp_own_lock): Rename to ...
  (svn_wc__db_wclock_owns_lock): ... this and allow checking for checking a root
    and/or multiple levels.

* subversion/libsvn_wc/wc_db.h
  (svn_wc__db_wclock_set): Rename to ...
  (svn_wc__db_wclock_obtain): ... this and document levels_to_lock.
  (svn_wc__db_wclocked): Add documentation.
  (svn_wc__db_wclock_remove): Rename to ...
  (svn_wc__db_wclock_release): ... this.
  (svn_wc__db_wclock_owns_lock): New function.
  (svn_wc__db_temp_mark_locked): Remove function.
  (svn_wc__db_temp_own_lock): Remove function.

* subversion/libsvn_wc/wc_db_pdh.c
  (determine_obstructed_file): #undef when using single_db.
  (svn_wc__db_pdh_create_wcroot): Initialize new lock store if single db.
  (svn_wc__db_pdh_parse_local_abspath): Disable obstruction check on
    single db.

* subversion/libsvn_wc/wc_db_private.h
  (svn_wc__db_wclock_t): New struct.
  (svn_wc__db_wcroot_t): Name struct (for debugging and svn diff -x -p)
    Add array of owned locks.
  (svn_wc__db_pdh_t): Remove obstructed_file and locked for single db.

Modified:
    subversion/trunk/subversion/libsvn_wc/adm_ops.c
    subversion/trunk/subversion/libsvn_wc/lock.c
    subversion/trunk/subversion/libsvn_wc/log.c
    subversion/trunk/subversion/libsvn_wc/upgrade.c
    subversion/trunk/subversion/libsvn_wc/wc-queries.sql
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/libsvn_wc/wc_db.h
    subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c
    subversion/trunk/subversion/libsvn_wc/wc_db_private.h

Modified: subversion/trunk/subversion/libsvn_wc/adm_ops.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/adm_ops.c?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/adm_ops.c (original)
+++ subversion/trunk/subversion/libsvn_wc/adm_ops.c Tue Jul 13 21:08:49 2010
@@ -1272,8 +1272,8 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
   if (kind == svn_node_dir && !exists)
     {
       /* Lock on parent needs to be propogated into the child db. */
-      SVN_ERR(svn_wc__db_wclock_set(db, local_abspath, 0, scratch_pool));
-      SVN_ERR(svn_wc__db_temp_mark_locked(db, local_abspath, scratch_pool));
+      SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
+                                       scratch_pool));
     }
 
 #if (SVN_WC__VERSION < SVN_WC__PROPS_IN_DB)

Modified: subversion/trunk/subversion/libsvn_wc/lock.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/lock.c?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/lock.c (original)
+++ subversion/trunk/subversion/libsvn_wc/lock.c Tue Jul 13 21:08:49 2010
@@ -204,7 +204,7 @@ pool_cleanup_locked(void *p)
             {
               /* There is no remaining work, so we're good to remove any
                  potential "physical" lock.  */
-              err = svn_wc__db_wclock_remove(db, lock->abspath, scratch_pool);
+              err = svn_wc__db_wclock_release(db, lock->abspath, scratch_pool);
             }
         }
       svn_error_clear(err);
@@ -312,7 +312,8 @@ adm_access_alloc(svn_wc_adm_access_t **a
       svn_boolean_t owns_lock;
 
       /* If the db already owns a lock, we can't add an extra lock record */
-      SVN_ERR(svn_wc__db_temp_own_lock(&owns_lock, db, path, scratch_pool));
+      SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, path, FALSE,
+                                          scratch_pool));
 
       /* If DB owns the lock, but when there is no access baton open for this
          directory, old access baton based code is trying to access data that
@@ -321,9 +322,8 @@ adm_access_alloc(svn_wc_adm_access_t **a
       if (!owns_lock
           || svn_wc__adm_retrieve_internal2(db, lock->abspath, scratch_pool))
         {
-          SVN_ERR(svn_wc__db_wclock_set(db, lock->abspath, 0, scratch_pool));
-
-          SVN_ERR(svn_wc__db_temp_mark_locked(db, lock->abspath, 
scratch_pool));
+          SVN_ERR(svn_wc__db_wclock_obtain(db, lock->abspath, 0, FALSE,
+                                           scratch_pool));
         }
     }
 
@@ -332,7 +332,7 @@ adm_access_alloc(svn_wc_adm_access_t **a
   if (err)
     return svn_error_compose_create(
                 err,
-                svn_wc__db_wclock_remove(db, lock->abspath, scratch_pool));
+                svn_wc__db_wclock_release(db, lock->abspath, scratch_pool));
 
   /* ### does this utf8 thing really/still apply??  */
   /* It's important that the cleanup handler is registered *after* at least
@@ -516,8 +516,9 @@ close_single(svn_wc_adm_access_t *adm_ac
     return SVN_NO_ERROR;
 
   /* Physically unlock if required */
-  SVN_ERR(svn_wc__db_temp_own_lock(&locked, adm_access->db,
-                                   adm_access->abspath, scratch_pool));
+  SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
+                                      adm_access->abspath, TRUE,
+                                      scratch_pool));
   if (locked)
     {
       if (!preserve_lock)
@@ -528,9 +529,9 @@ close_single(svn_wc_adm_access_t *adm_ac
              from the working copy.  It is an error for the lock to
              have disappeared if the administrative area still exists. */
 
-          svn_error_t *err = svn_wc__db_wclock_remove(adm_access->db,
-                                                      adm_access->abspath,
-                                                      scratch_pool);
+          svn_error_t *err = svn_wc__db_wclock_release(adm_access->db,
+                                                       adm_access->abspath,
+                                                       scratch_pool);
           if (err)
             {
               if (svn_wc__adm_area_exists(adm_access->abspath, scratch_pool))
@@ -1419,9 +1420,9 @@ svn_wc_adm_locked(const svn_wc_adm_acces
 {
   svn_boolean_t locked;
   apr_pool_t *subpool = svn_pool_create(adm_access->pool);
-  svn_error_t *err = svn_wc__db_temp_own_lock(&locked, adm_access->db,
-                                              adm_access->abspath,
-                                              subpool);
+  svn_error_t *err = svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
+                                                 adm_access->abspath, TRUE,
+                                                 subpool);
   svn_pool_destroy(subpool);
 
   if (err)
@@ -1441,7 +1442,8 @@ svn_wc__write_check(svn_wc__db_t *db,
 {
   svn_boolean_t locked;
 
-  SVN_ERR(svn_wc__db_temp_own_lock(&locked, db, local_abspath, scratch_pool));
+  SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, db, local_abspath, FALSE,
+                                      scratch_pool));
   if (!locked)
     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
                              _("No write-lock in '%s'"),
@@ -1461,8 +1463,8 @@ svn_wc_locked2(svn_boolean_t *locked_her
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
   if (locked_here != NULL)
-    SVN_ERR(svn_wc__db_temp_own_lock(locked_here, wc_ctx->db, local_abspath,
-                                     scratch_pool));
+    SVN_ERR(svn_wc__db_wclock_owns_lock(locked_here, wc_ctx->db, local_abspath,
+                                        FALSE, scratch_pool));
   if (locked != NULL)
     SVN_ERR(svn_wc__db_wclocked(locked, wc_ctx->db, local_abspath,
                                 scratch_pool));
@@ -1652,11 +1654,12 @@ svn_wc__acquire_write_lock(const char **
 
   /* We don't want to try and lock an unversioned directory that
      obstructs a versioned directory. */
-  err = svn_wc__internal_check_wc(&format, wc_ctx->db, local_abspath, 
iterpool);
+  err = svn_wc__internal_check_wc(&format, wc_ctx->db, local_abspath,
+                                  iterpool);
   if (!err && format)
     {
-      SVN_ERR(svn_wc__db_wclock_set(wc_ctx->db, local_abspath, 0, iterpool));
-      SVN_ERR(svn_wc__db_temp_mark_locked(wc_ctx->db, local_abspath, 
iterpool));
+      SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath, 0, FALSE,
+                                       iterpool));
     }
   svn_error_clear(err);
 
@@ -1677,6 +1680,7 @@ svn_wc__release_write_lock(svn_wc_contex
   apr_uint64_t id;
   svn_skel_t *work_item;
   svn_boolean_t locked_here;
+  svn_error_t *err;
   int i;
 
   SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, TRUE,
@@ -1713,10 +1717,20 @@ svn_wc__release_write_lock(svn_wc_contex
         SVN_ERR(svn_wc__release_write_lock(wc_ctx, child_abspath, iterpool));
     }
 
-  SVN_ERR(svn_wc__db_temp_own_lock(&locked_here, wc_ctx->db, local_abspath,
-                                   iterpool));
+  err = svn_wc__db_wclock_owns_lock(&locked_here, wc_ctx->db, local_abspath,
+                                    TRUE, iterpool);
+#ifndef SVN_WC__SINGLE_DB
+  if (err && err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
+    {
+      svn_error_clear(err);
+      locked_here = FALSE;
+    }
+  else
+#endif
+    SVN_ERR(err);
+
   if (locked_here)
-    SVN_ERR(svn_wc__db_wclock_remove(wc_ctx->db, local_abspath, iterpool));
+    SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, iterpool));
 
   svn_pool_destroy(iterpool);
 

Modified: subversion/trunk/subversion/libsvn_wc/log.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/log.c?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/log.c (original)
+++ subversion/trunk/subversion/libsvn_wc/log.c Tue Jul 13 21:08:49 2010
@@ -749,7 +749,6 @@ cleanup_internal(svn_wc__db_t *db,
                  apr_pool_t *scratch_pool)
 {
   int wc_format;
-  svn_error_t *err;
   const apr_array_header_t *children;
   int i;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -762,12 +761,7 @@ cleanup_internal(svn_wc__db_t *db,
   SVN_ERR(can_be_cleaned(&wc_format, db, adm_abspath, iterpool));
 
   /* Lock this working copy directory, or steal an existing lock */
-  err = svn_wc__db_wclock_set(db, adm_abspath, 0, iterpool);
-  if (err && err->apr_err == SVN_ERR_WC_LOCKED)
-    svn_error_clear(err);
-  else if (err)
-    return svn_error_return(err);
-  SVN_ERR(svn_wc__db_temp_mark_locked(db, adm_abspath, iterpool));
+  SVN_ERR(svn_wc__db_wclock_obtain(db, adm_abspath, 0, TRUE, iterpool));
 
   /* Run our changes before the subdirectories. We may not have to recurse
      if we blow away a subdir.  */
@@ -816,7 +810,7 @@ cleanup_internal(svn_wc__db_t *db,
   SVN_ERR(svn_wc__db_pristine_cleanup(db, adm_abspath, iterpool));
 
   /* All done, toss the lock */
-  SVN_ERR(svn_wc__db_wclock_remove(db, adm_abspath, iterpool));
+  SVN_ERR(svn_wc__db_wclock_release(db, adm_abspath, iterpool));
 
   svn_pool_destroy(iterpool);
 

Modified: subversion/trunk/subversion/libsvn_wc/upgrade.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/upgrade.c?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/trunk/subversion/libsvn_wc/upgrade.c Tue Jul 13 21:08:49 2010
@@ -1316,7 +1316,7 @@ upgrade_to_wcng(svn_wc__db_t *db,
      function bumps a working copy all the way to current.  */
   SVN_ERR(svn_wc__db_temp_reset_format(SVN_WC__VERSION, db, dir_abspath,
                                        iterpool));
-  SVN_ERR(svn_wc__db_wclock_set(db, dir_abspath, 0, iterpool));
+  SVN_ERR(svn_wc__db_wclock_obtain(db, dir_abspath, 0, FALSE, iterpool));
   SVN_ERR(svn_wc__write_upgraded_entries(db, sdb, repos_id, wc_id,
                                          dir_abspath, entries,
                                          iterpool));
@@ -1360,7 +1360,7 @@ upgrade_to_wcng(svn_wc__db_t *db,
      has run.  */
   /* ### well, actually.... we don't recursively delete subdir locks here,
      ### we rely upon their own upgrade processes to do it. */
-  SVN_ERR(svn_wc__db_wclock_remove(db, dir_abspath, iterpool));
+  SVN_ERR(svn_wc__db_wclock_release(db, dir_abspath, iterpool));
 
   /* Zap all the obsolete files. This removes the old-style lock file.  */
   wipe_obsolete_files(dir_abspath, iterpool);

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Tue Jul 13 21:08:49 
2010
@@ -391,6 +391,10 @@ WHERE wc_id = ?1 AND local_dir_relpath =
 DELETE FROM WC_LOCK
 WHERE wc_id = ?1 AND local_dir_relpath = ?2;
 
+-- STMT_FIND_WC_LOCK
+SELECT local_dir_relpath FROM WC_LOCK
+WHERE wc_id = ?1 AND local_dir_relpath LIKE ?2 ESCAPE '#';
+
 -- STMT_APPLY_CHANGES_TO_BASE
 /* translated_size and last_mod_time are not mentioned here because they will
    be tweaked after the working-file is installed.

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Tue Jul 13 21:08:49 2010
@@ -125,6 +125,22 @@
    NOTE: this should match the character used within wc-metadata.sql  */
 #define LIKE_ESCAPE_CHAR     "#"
 
+/* Calculates the depth of the relpath below "" */
+APR_INLINE static int relpath_op_depth(const char *relpath)
+{
+  int n = 1;
+  if (*relpath == '\0')
+    return 0;
+
+  do
+  {
+    if (*relpath == '/')
+      n++;
+  }
+  while (*(++relpath));
+
+  return n;
+}
 
 typedef struct insert_base_baton_t {
   /* common to all insertions into BASE */
@@ -6969,6 +6985,7 @@ svn_wc__db_temp_forget_directory(svn_wc_
       apr_ssize_t klen;
       void *val;
       svn_wc__db_pdh_t *pdh;
+      svn_error_t *err;
 
       apr_hash_this(hi, &key, &klen, &val);
       pdh = val;
@@ -6976,7 +6993,22 @@ svn_wc__db_temp_forget_directory(svn_wc_
       if (!svn_dirent_is_ancestor(local_dir_abspath, pdh->local_abspath))
         continue;
 
-      SVN_ERR(svn_wc__db_wclock_remove(db, pdh->local_abspath, scratch_pool));
+      if (pdh->locked)
+        {
+          err = svn_wc__db_wclock_release(db, pdh->local_abspath,
+                                          scratch_pool);
+          if (err
+              && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY
+                  || err->apr_err == SVN_ERR_WC_NOT_LOCKED))
+            {
+              svn_error_clear(err);
+            }
+          else
+            SVN_ERR(err);
+        }
+
+      SVN_ERR_ASSERT(!pdh->locked);
+
       apr_hash_set(db->dir_data, key, klen, NULL);
 
       if (pdh->wcroot && pdh->wcroot->sdb &&
@@ -7573,45 +7605,262 @@ svn_wc__db_temp_wcroot_tempdir(const cha
   return SVN_NO_ERROR;
 }
 
-
-svn_error_t *
-svn_wc__db_wclock_set(svn_wc__db_t *db,
-                      const char *local_abspath,
-                      int levels_to_lock,
-                      apr_pool_t *scratch_pool)
+/* Baton for wclock_obtain_cb() */
+struct wclock_obtain_baton
 {
+  svn_wc__db_t *db;
   svn_wc__db_pdh_t *pdh;
   const char *local_relpath;
+  const char *local_abspath;
+  int levels_to_lock;
+  svn_boolean_t steal_lock;
+};
+
+/* Helper for wclock_obtain_cb() to steal an existing lock */
+static svn_error_t *
+wclock_steal(svn_wc__db_wcroot_t *wcroot,
+             const char *local_relpath,
+             apr_pool_t *scratch_pool)
+{
   svn_sqlite__stmt_t *stmt;
-  svn_error_t *err;
 
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
 
-  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
-                              local_abspath, svn_sqlite__mode_readwrite,
-                              scratch_pool, scratch_pool));
-  VERIFY_USABLE_PDH(pdh);
+  SVN_ERR(svn_sqlite__step_done(stmt));
 
+  return SVN_NO_ERROR;
+}
+
+/* svn_sqlite__transaction_callback_t for svn_wc__db_wclock_obtain() */
+static svn_error_t *
+wclock_obtain_cb(void *baton,
+                 svn_sqlite__db_t* sdb,
+                 apr_pool_t *scratch_pool)
+{
+  struct wclock_obtain_baton *bt = baton;
+  svn_sqlite__stmt_t *stmt;
+  svn_wc__db_wcroot_t *wcroot = bt->pdh->wcroot;
+  svn_error_t *err;
+  const char *lock_relpath;
+  int max_depth;
+  int lock_depth;
+  svn_boolean_t got_row;
+
+#ifdef SVN_WC__SINGLE_DB
+  svn_wc__db_wclock_t lock;
+#else
   /* ### Can only lock this directory in the per-dir layout.  This is
      ### a temporary restriction until metadata gets centralised.
      ### Perhaps this should be a runtime error, rather than an
      ### assert?  Perhaps check the path is versioned? */
-  SVN_ERR_ASSERT(*local_relpath == '\0');
+  SVN_ERR_ASSERT(*bt->local_relpath == '\0');
+#endif
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+  /* Check if there are nodes locked below the new lock root */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+                            apr_pstrcat(scratch_pool,
+                                        escape_sqlite_like(bt->local_relpath,
+                                                           scratch_pool),
+                                        "/%", NULL)));
+
+  lock_depth = relpath_op_depth(bt->local_relpath);
+  max_depth = lock_depth + bt->levels_to_lock;
+
+  SVN_ERR(svn_sqlite__step(&got_row, stmt));
+
+  while (got_row)
+    {
+      const char *lock_abspath;
+      svn_boolean_t own_lock;
+
+      lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
+
+      /* If we are not locking with depth infinity, check if this lock
+         voids our lock request */
+      if (bt->levels_to_lock >= 0
+          && relpath_op_depth(lock_relpath) > max_depth)
+        {
+          SVN_ERR(svn_sqlite__step(&got_row, stmt));
+          continue;
+        }
+
+      lock_abspath = svn_dirent_join(wcroot->abspath,
+                                     lock_relpath, scratch_pool);
+
+      /* Check if we are the lock owner, because we should be able to
+         extend our lock. */
+      err = svn_wc__db_wclock_owns_lock(&own_lock, bt->db, lock_abspath,
+                                        TRUE, scratch_pool);
+
+      if (err)
+        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+
+      if (!own_lock && !bt->steal_lock)
+        {
+          err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
+                                   _("'%s' is already locked."),
+                                   svn_dirent_local_style(lock_abspath,
+                                                          scratch_pool));
+          return svn_error_createf(SVN_ERR_WC_LOCKED, err,
+                                   _("Working copy '%s' locked."),
+                                   svn_dirent_local_style(bt->local_abspath,
+                                                          scratch_pool));
+        }
+      else if (!own_lock)
+        {
+          err = wclock_steal(wcroot, lock_relpath, scratch_pool);
+
+          if (err)
+            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+        }
+
+      SVN_ERR(svn_sqlite__step(&got_row, stmt));
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (bt->steal_lock)
+    SVN_ERR(wclock_steal(wcroot, bt->local_relpath, scratch_pool));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
+  lock_relpath = bt->local_relpath;
+
+  while (TRUE)
+    {
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
+
+      SVN_ERR(svn_sqlite__step(&got_row, stmt));
+
+      if (got_row)
+        {
+          int levels = svn_sqlite__column_int(stmt, 0);
+          if (levels >= 0)
+            levels += relpath_op_depth(lock_relpath);
+
+          if (levels == -1 || levels >= lock_depth)
+            {
+              SVN_ERR(svn_sqlite__reset(stmt));
+
+              err = svn_error_createf(
+                              SVN_ERR_WC_LOCKED, NULL,
+                              _("'%s' is already locked."),
+                              svn_dirent_local_style(
+                                       svn_dirent_join(wcroot->abspath,
+                                                       lock_relpath,
+                                                       scratch_pool),
+                              scratch_pool));
+              return svn_error_createf(
+                              SVN_ERR_WC_LOCKED, err,
+                              _("Working copy '%s' locked."),
+                              svn_dirent_local_style(bt->local_abspath,
+                                                     scratch_pool));
+            }
+
+          break; /* There can't be interesting locks on higher nodes */
+        }
+
+      if (!*lock_relpath)
+        break;
+
+      lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
+    }
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_INSERT_WC_LOCK));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isi", pdh->wcroot->wc_id, local_relpath,
-                            (apr_int64_t) levels_to_lock));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isi", wcroot->wc_id,
+                            bt->local_relpath,
+                            (apr_int64_t) bt->levels_to_lock));
   err = svn_sqlite__insert(NULL, stmt);
   if (err)
     return svn_error_createf(SVN_ERR_WC_LOCKED, err,
                              _("Working copy '%s' locked"),
-                             svn_dirent_local_style(local_abspath,
+                             svn_dirent_local_style(bt->local_abspath,
                                                     scratch_pool));
 
+  /* And finally store that we obtained the lock */
+#ifdef SVN_WC__SINGLE_DB
+  lock.relpath = apr_pstrdup(wcroot->owned_locks->pool, bt->local_relpath);
+  lock.levels = bt->levels_to_lock;
+  APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
+#else
+  bt->pdh->locked = TRUE;
+#endif
+
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_wc__db_wclock_obtain(svn_wc__db_t *db,
+                         const char *local_abspath,
+                         int levels_to_lock,
+                         svn_boolean_t steal_lock,
+                         apr_pool_t *scratch_pool)
+{
+  struct wclock_obtain_baton baton;
+
+  SVN_ERR_ASSERT(levels_to_lock >= -1);
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&baton.pdh, &baton.local_relpath,
+                                             db, local_abspath,
+                                             svn_sqlite__mode_readwrite,
+                                             scratch_pool, scratch_pool));
+  VERIFY_USABLE_PDH(baton.pdh);
+
+  if (!steal_lock)
+#ifndef SVN_WC__SINGLE_DB
+    {
+      if (baton.pdh->locked)
+        return svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
+                                 _("'%s' is already locked."),
+                                 svn_dirent_local_style(local_abspath,
+                                                        scratch_pool));
+    }
+#else
+    {
+      int i;
+      svn_wc__db_wcroot_t *wcroot = baton.pdh->wcroot;
+      int depth = relpath_op_depth(baton.local_relpath);
+
+      for (i = 0; i < wcroot->owned_locks->nelts; i++)
+        {
+          svn_wc__db_wclock_t lock = APR_ARRAY_IDX(wcroot->owned_locks,
+                                                   i, svn_wc__db_wclock_t);
+
+          if ((*lock.relpath == '\0'
+               || svn_relpath_is_ancestor(lock.relpath, baton.local_relpath)
+              && (lock.levels == -1
+                  || (lock.levels + relpath_op_depth(lock.relpath)) >= depth)))
+            {
+              const char *lock_abspath
+                  = svn_dirent_join(baton.pdh->wcroot->abspath, lock.relpath,
+                                    scratch_pool);
+
+              return svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
+                                       _("'%s' is already locked via '%s'."),
+                                       svn_dirent_local_style(local_abspath,
+                                                              scratch_pool),
+                                       svn_dirent_local_style(lock_abspath,
+                                                              scratch_pool));
+            }
+        }
+    }
+#endif
+
+  baton.db = db;
+  baton.local_abspath = local_abspath;
+  baton.steal_lock = steal_lock;
+  baton.levels_to_lock = levels_to_lock;
+
+  return svn_error_return(
+            svn_sqlite__with_transaction(baton.pdh->wcroot->sdb,
+                                         wclock_obtain_cb,
+                                         &baton,
+                                         scratch_pool));
+}
+
 
 /* */
 static svn_error_t *
@@ -7674,60 +7923,135 @@ svn_wc__db_wclocked(svn_boolean_t *locke
 
 
 svn_error_t *
-svn_wc__db_wclock_remove(svn_wc__db_t *db,
-                         const char *local_abspath,
-                         apr_pool_t *scratch_pool)
+svn_wc__db_wclock_release(svn_wc__db_t *db,
+                          const char *local_abspath,
+                          apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+#ifdef SVN_WC__SINGLE_DB
+  int i;
+  apr_array_header_t *owned_locks;
+#endif
 
-  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
-                                 STMT_DELETE_WC_LOCK, scratch_pool));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
+                              local_abspath, svn_sqlite__mode_readwrite,
+                              scratch_pool, scratch_pool));
 
-  /* If we've just removed the "physical" lock, we also need to ensure we
-     don't continue to think we own the lock. */
-  pdh = svn_wc__db_pdh_get_or_create(db, local_abspath, FALSE, scratch_pool);
-  if (pdh)
-    pdh->locked = FALSE;
+  VERIFY_USABLE_PDH(pdh);
 
-  return SVN_NO_ERROR;
-}
+  /* First check and remove the owns-lock information as failure in
+     removing the db record implies that we have to steal the lock later. */
+#ifndef SVN_WC__SINGLE_DB
+  SVN_ERR_ASSERT(*local_relpath == '\0');
+  if (!pdh->locked)
+    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
+                             _("Working copy not locked at '%s'."),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
+  pdh->locked = FALSE;
+#else
+  owned_locks = pdh->wcroot->owned_locks;
+  for (i = 0; i < owned_locks->nelts; i++)
+    {
+      const char *lock_abspath = APR_ARRAY_IDX(owned_locks, i, const char*);
 
+      if (strcmp(lock_abspath, local_abspath) == 0)
+        break;
+    }
 
-svn_error_t *
-svn_wc__db_temp_mark_locked(svn_wc__db_t *db,
-                            const char *local_dir_abspath,
-                            apr_pool_t *scratch_pool)
-{
-  svn_wc__db_pdh_t *pdh;
+  if (i >= owned_locks->nelts)
+    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
+                             _("Working copy not locked at '%s'."),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
 
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
+  if (i < owned_locks->nelts)
+    {
+      owned_locks->nelts--;
 
-  pdh = svn_wc__db_pdh_get_or_create(db, local_dir_abspath, TRUE,
-                                     scratch_pool);
-  pdh->locked = TRUE;
+      /* Move the last item in the array to the to be deleted place */
+      if (owned_locks->nelts > 0)
+        APR_ARRAY_IDX(owned_locks, i, const char*) =
+                APR_ARRAY_IDX(owned_locks, owned_locks->nelts, const char*);
+    }
+#endif
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_DELETE_WC_LOCK));
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+
+  SVN_ERR(svn_sqlite__step_done(stmt));
 
   return SVN_NO_ERROR;
 }
 
-
 svn_error_t *
-svn_wc__db_temp_own_lock(svn_boolean_t *own_lock,
-                         svn_wc__db_t *db,
-                         const char *local_dir_abspath,
-                         apr_pool_t *scratch_pool)
+svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            svn_boolean_t exact,
+                            apr_pool_t *scratch_pool)
 {
   svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+#ifdef SVN_WC__SINGLE_DB
+  apr_array_header_t *owned_locks;
+  int lock_level, i;
+#endif
 
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
+                              local_abspath, svn_sqlite__mode_readwrite,
+                              scratch_pool, scratch_pool));
 
-  pdh = svn_wc__db_pdh_get_or_create(db, local_dir_abspath, FALSE,
-                                     scratch_pool);
-  *own_lock = (pdh != NULL && pdh->locked);
+  if (!pdh->wcroot)
+    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+                             _("The node '%s' was not found."),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
 
-  return SVN_NO_ERROR;
+  VERIFY_USABLE_PDH(pdh);
+  *own_lock = FALSE;
+#ifndef SVN_WC__SINGLE_DB
+  /* In !SINGLE_DB mode we only allow locking the directories */
+  if (*local_relpath == '\0')
+    *own_lock = (pdh != NULL && pdh->locked);
+#else
+  owned_locks = pdh->wcroot->owned_locks;
+  lock_level = relpath_op_depth(local_relpath);
+
+  if (exact)
+    for (i = 0; i < owned_locks->nelts; i++)
+      {
+        svn_wc__db_wclock_t lock = APR_ARRAY_IDX(owned_locks, i,
+                                                 svn_wc__db_wclock_t);
+
+        if (strcmp(lock.relpath, local_relpath) == 0)
+          {
+            *own_lock = TRUE;
+            return SVN_NO_ERROR;
+          }
+      }
+  else
+    for (i = 0; i < owned_locks->nelts; i++)
+      {
+        svn_wc__db_wclock_t lock = APR_ARRAY_IDX(owned_locks, i,
+                                                 svn_wc__db_wclock_t);
+
+        if ((*lock.relpath == '\0'
+             || svn_relpath_is_ancestor(lock.relpath, local_relpath))
+            && (lock.levels == -1
+                || relpath_op_depth(lock.relpath) + lock.levels >= lock_level))
+          {
+            *own_lock = TRUE;
+            return SVN_NO_ERROR;
+          }
+      }
+#endif
 
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.h Tue Jul 13 21:08:49 2010
@@ -2185,23 +2185,39 @@ svn_wc__db_wq_completed(svn_wc__db_t *db
    that they still function correctly, even in the new world.  'levels to
    lock' should not be exposed through the wc-ng APIs at all: users either
    get to lock the entire tree (rooted at some subdir, of course), or none.
+
+   An infinite depth lock is obtained with LEVELS_TO_LOCK set to -1, but until
+   we move to a single DB only depth 0 is supported.
 */
 svn_error_t *
-svn_wc__db_wclock_set(svn_wc__db_t *db,
-                      const char *local_abspath,
-                      int levels_to_lock,
-                      apr_pool_t *scratch_pool);
+svn_wc__db_wclock_obtain(svn_wc__db_t *db,
+                         const char *local_abspath,
+                         int levels_to_lock,
+                         svn_boolean_t steal_lock,
+                         apr_pool_t *scratch_pool);
 
+/* Check if somebody has a wclock on LOCAL_ABSPATH */
 svn_error_t *
 svn_wc__db_wclocked(svn_boolean_t *locked,
                     svn_wc__db_t *db,
                     const char *local_abspath,
                     apr_pool_t *scratch_pool);
 
+/* Release the previously obtained lock on LOCAL_ABSPATH */
 svn_error_t *
-svn_wc__db_wclock_remove(svn_wc__db_t *db,
-                         const char *local_abspath,
-                         apr_pool_t *scratch_pool);
+svn_wc__db_wclock_release(svn_wc__db_t *db,
+                          const char *local_abspath,
+                          apr_pool_t *scratch_pool);
+
+/* Checks whether DB currently owns a lock to operate on LOCAL_ABSPATH.
+   If EXACT is TRUE only lock roots are checked. */
+svn_error_t *
+svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            svn_boolean_t exact,
+                            apr_pool_t *scratch_pool);
+
 
 
 /* @defgroup svn_wc__db_temp Various temporary functions during transition
@@ -2371,19 +2387,6 @@ svn_wc__db_temp_wcroot_tempdir(const cha
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool);
 
-
-svn_error_t *
-svn_wc__db_temp_mark_locked(svn_wc__db_t *db,
-                            const char *local_dir_abspath,
-                            apr_pool_t *scratch_pool);
-
-
-svn_error_t *
-svn_wc__db_temp_own_lock(svn_boolean_t *own_lock,
-                         svn_wc__db_t *db,
-                         const char *local_dir_abspath,
-                         apr_pool_t *scratch_pool);
-
 svn_error_t *
 svn_wc__db_temp_op_set_base_incomplete(svn_wc__db_t *db,
                                        const char *local_dir_abspath,

Modified: subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c?rev=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_pdh.c Tue Jul 13 21:08:49 2010
@@ -85,7 +85,7 @@ get_old_version(int *version,
   return SVN_NO_ERROR;
 }
 
-
+#ifndef SVN_WC__SINGLE_DB
 /* The filesystem has a directory at LOCAL_RELPATH. Examine the metadata
    to determine if a *file* was supposed to be there.
 
@@ -129,6 +129,7 @@ determine_obstructed_file(svn_boolean_t 
 
   return svn_sqlite__reset(stmt);
 }
+#endif
 
 
 /* */
@@ -325,6 +326,12 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_
   (*wcroot)->sdb = sdb;
   (*wcroot)->wc_id = wc_id;
   (*wcroot)->format = format;
+#ifdef SVN_WC__SINGLE_DB
+  /* 8 concurrent locks is probably more than a typical wc_ng based svn client
+     uses. */
+  (*wcroot)->owned_locks = apr_array_make(result_pool, 8,
+                                          sizeof(svn_wc__db_wclock_t));
+#endif
 
   /* SDB will be NULL for pre-NG working copies. We only need to run a
      cleanup when the SDB is present.  */
@@ -689,6 +696,7 @@ svn_wc__db_pdh_parse_local_abspath(svn_w
             }
         }
 
+#ifndef SVN_WC__SINGLE_DB
       if (parent_pdh)
         {
           const char *lookfor_relpath = svn_dirent_basename(local_abspath,
@@ -710,6 +718,7 @@ svn_wc__db_pdh_parse_local_abspath(svn_w
               return SVN_NO_ERROR;
             }
         }
+#endif
     }
 
   /* The PDH is complete. Stash it into DB.  */

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=963863&r1=963862&r2=963863&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_private.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_private.h Tue Jul 13 21:08:49 
2010
@@ -56,11 +56,20 @@ struct svn_wc__db_t {
   apr_pool_t *state_pool;
 };
 
+/* Hold information about an owned lock */
+typedef struct svn_wc__db_wclock_t
+{
+  /* Relative path of the lock root */
+  const char *relpath;
+  /* Number of levels locked (0 for infinity) */
+  int levels;
+} svn_wc__db_wclock_t;
+
 /** Hold information about a WCROOT.
  *
  * This structure is referenced by all per-directory handles underneath it.
  */
-typedef struct {
+typedef struct svn_wc__db_wcroot_t {
   /* Location of this wcroot in the filesystem.  */
   const char *abspath;
 
@@ -75,6 +84,12 @@ typedef struct {
      format has not (yet) been determined, this will be UNKNOWN_FORMAT.  */
   int format;
 
+#ifdef SVN_WC__SINGLE_DB
+  /* Array of svn_wc__db_wclock_t fields (not pointers!).
+     Typically just one or two locks maximum. */
+  apr_array_header_t *owned_locks;
+#endif
+
 } svn_wc__db_wcroot_t;
 
 /**  Pristine Directory Handle
@@ -83,6 +98,7 @@ typedef struct {
  * a given working copy directory.
  */
 typedef struct svn_wc__db_pdh_t {
+#ifndef SVN_WC__SINGLE_DB
   /* This (versioned) working copy directory is obstructing what *should*
      be a file in the parent directory (according to its metadata).
 
@@ -90,6 +106,7 @@ typedef struct svn_wc__db_pdh_t {
 
      ### obstruction is only possible with per-dir wc.db databases.  */
   svn_boolean_t obstructed_file;
+#endif
 
   /* The absolute path to this working copy directory. */
   const char *local_abspath;
@@ -100,8 +117,10 @@ typedef struct svn_wc__db_pdh_t {
   /* The parent directory's per-dir information. */
   struct svn_wc__db_pdh_t *parent;
 
+#ifndef SVN_WC__SINGLE_DB
   /* Whether this process owns a write-lock on this directory. */
   svn_boolean_t locked;
+#endif
 
   /* Hold onto the old-style access baton that corresponds to this PDH.  */
   svn_wc_adm_access_t *adm_access;


Reply via email to