[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PATCH][merge-tracking] libsvn_fs_util

From: Kamesh Jayachandran <kamesh_at_collab.net>
Date: 2006-09-12 13:22:42 CEST

> It doesn't look like anyone's come up with a portable way to do that
> yet (at least, not using static linkage, or build magic to dump the
> code directly into _fs and _base). What we discussed while Kamesh was
> visiting in Brisbane is to use the new subversion/include/private/
> area for the Subversion-private header files.
>
>
Sorry I failed to understand that now understood it :), now the attached
patch has that implemented.
>>> I'd suggest that we'd just be adding complexity if we made this a
>>> publicly-accessible library: we'd have to support a whole new set of
>>> interfaces in a binary-compatible fashion. On the other hand, if we
>>> make it private, we can change the implementation at will.
>>>
>>> It looks like you're planning to make it private, since you aren't
>>> installing a public include file. I'd just like to make sure that that's
>>> a deliberate choice rather than an accident of the current design.
>>>
>> It is an accident, I did not think about the BC issues and visiblity of
>> this libs to other modules other than libsvn_fs_fs and libsvn_fs_base.
>>
>
> If the include/private/ area is used for the svn_fs_util.h, should we
> bother worrying about BC issues? I tend to think no -- anyone
> violating our encapsulation strategy to that extent rather deserves
> any compat headaches that they receive as a result.
>
>
Do you want merge_info_indexer.h to be renamed to 'svn_fs_util.h' to be
renamed?
> ...
>
>>> If we do keep it as a shared library, you'll need to add version checks
>>> (i.e. svn_ver_check_list()) to it, and also add it to the existing checks
>>> in libsvn_fs_{base,fs}. If we make it static, that won't be necessary.
>>>
>>> Finally, while I prefer the 'util' name, I wonder whether it'd be better
>>> to call it something like 'libsvn_fs_subr' for parity with 'libsvn_subr'.
>>>
>> Sounds good.
>>
>
> This exact discussion came up one morning between Kamesh and Mike and
> myself. IIRC, while we were aware of the minor inconsistency it would
> cause, Mike and I still preferred "util", on the grounds that it's
> more obivous (no more "What's 'subr' stand for?" questions). I
> maintain the same preference today.
>
>
I remember this, I don't have any preference on the names, Ok with
either libsvn_util or libsvn_subr.
>>> [The really important question - whether this change actually makes sense
>>> in the context of the merge-tracking work - I'll leave to someone with
>>> more experience of that code.]
>>>
>
> It doesn't matter one way or the other. Mike mentioned that there is
> other FS code -- unrelated to merge tracking -- which should really
> live in a utility library, too.
>

[[[
Patch by: Kamesh Jayachandran <kamesh@collab.net>

Move mergeinfo indexing code out of libsvn_fs_fs to libsvn_fs_util(new module)
so that it can be reused by bdb repos.

The changes can be summarised as follows,
1. root_vtable_t's get_merge_info hook has been removed as implementation is
    'fs' independent and can be availed out of new module libsvn_fs_util's
    svn_fs_util_get_merge_info.

2. Exposing 'txn_mergeinfo' hook out of txn_vtable_t so that new merge_info
    indexer can make use of it to weave the information from the transaction.

3. libsvn_fs_util exposes the following public functions for libsvn_fs_fs,
    libsvn_fs_base and libsvn_fs modules to consume.
    - svn_fs_util_mtd_create - creates the fresh mergeinfo db with the schema
      consumed by libsvn_fs repo create hook.
    - svn_fs_util_mtd_update_merge_info_index - updates mergeinfo sqlite db
      consumed by the commit_body of libsvn_fs_fs currently later would be used
      by libsvn_fs_base.
    - svn_fs_util_get_merge_info - gets a mergeinfo for a committed revision
      for the set of paths, consumed by libsvn_fs.

4. Moving the relevant functions as detailed down against each file from
    libsvn_fs_fs/tree.c and libsvn_fs_fs/fs_fs.c to
    libsvn_fs_util/merge_info_indexer.c

* build.conf
  (private-includes): Adding 'subversion/include/private/*.h'
  (libsvn_fs_fs):
   No more depends on sqlite rather depends on libsvn_fs_util.
  (libsvn_fs_util): New module.

* subversion/libsvn_fs/fs-loader.h
  (txn_vtable_t):
   Added 'txn_mergeinfo' member.
  (root_vtable_t):
   Removed 'get_merge_info' member.

* subversion/libsvn_fs/fs-loader.c
  (svn_fs_create):
   Calling 'svn_fs_util_mtd_create' to create the fresh mergeinfo db with the
   schema.
  (svn_fs_get_merge_info):
   Calling 'svn_fs_util_get_merge_info' rather than root_vtable_t's
   get_merge_info hook.

* subversion/libsvn_fs_fs/fs.h
  Removed the inclusion of sqlite3.h header.
  (fs_fs_data_t):
   Removed the member 'mtd'.
  (fs_txn_data_t): Removed.
  (fs_sqlite_exec): Moved to a new module libsvn_fs_util and made 'static'
   there as it is consumed from the same single 'C' file. It is renamed as
   util_sqlite_exec.
  (SQLITE_ERR): Moved to a new header
   subversion/libsvn_fs_util/merge_info_indexer.h

* subversion/libsvn_fs_fs/tree.c
  (sqlite_tracer): Removed.
  (NEGATIVE_CACHE_RESULT): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c
  (parse_mergeinfo_from_db): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c
  (append_component_to_paths): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c
  (get_merge_info_for_path): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c
  (fs_get_merge_info): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c and renamed as
   'svn_fs_util_get_merge_info'.
  (root_vtable):
   Removed 'get_merge_info' member.

* subversion/libsvn_fs_fs/fs_fs.c
  Removed the inclusion of sqlite3.h header.
  (txn_vtable): populated the 'txn_mergeinfo' member.
  (path_mergeinfo_db): Removed.
  (sqlite_tracer): Removed.
  (svn_fs_fs__create_txn):
   Not populating the 'fs_txn_data_t' of txn vtable.
  (index_path_merge_info): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c
  (index_txn_merge_info): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c
  (update_merge_info_index): Moved to
   subversion/libsvn_fs_util/merge_info_indexer.c and made it a
   public function by name 'svn_fs_util_mtd_update_merge_info_index'.
  (commit_body): Calling 'svn_fs_util_mtd_update_merge_info_index'
   instead of local update_merge_info_index.
  (fs_sqlite_exec): Removed.
  (SVN_MTD_CREATE_SQL): Removed
  (svn_fs_fs__create): not creating the mergeinfo db here rather leave it to
   libsvn_fs.
  (svn_fs_fs__open_txn):
   No more populating the fsap_data member.

* subversion/libsvn_fs_base/tree.c
  (root_vtable):
   Removed 'get_merge_info' member.

* subversion/libsvn_fs_util
  New module.

* subversion/libsvn_fs_util/merge_info_indexer.c
  New file having functions borrowed from subversion/libsvn_fs_fs/tree.c and
  subversion/libsvn_fs_fs/fs_fs.c.
* subversion/include/private/merge_info_indexer.h
  New file.

]]]

Index: build.conf
===================================================================
--- build.conf (revision 21450)
+++ build.conf (working copy)
@@ -28,6 +28,7 @@
 includes = subversion/include/*.h
 include-wildcards = *.h *.i *.swg
 private-includes =
+ subversion/include/private/*.h
         subversion/bindings/swig/include/*.swg
         subversion/libsvn_delta/compose_delta.c
 private-built-includes =
@@ -239,7 +240,7 @@
 type = fs-module
 path = subversion/libsvn_fs_fs
 install = fsmod-lib
-libs = libsvn_delta libsvn_subr aprutil apriconv apr sqlite
+libs = libsvn_delta libsvn_subr aprutil apriconv apr libsvn_fs_util
 msvc-static = yes
 
 # General API for accessing repositories
@@ -302,6 +303,15 @@
 msvc-libs = advapi32.lib shfolder.lib
 msvc-static = yes
 
+# Low-level grab bag of utilities
+[libsvn_fs_util]
+type = lib
+install = fsmod-lib
+path = subversion/libsvn_fs_util
+libs = aprutil apriconv apr
+msvc-libs = advapi32.lib shfolder.lib
+msvc-static = yes
+
 # Working copy management lib
 [libsvn_wc]
 type = lib
Index: subversion/libsvn_fs_base/tree.c
===================================================================
--- subversion/libsvn_fs_base/tree.c (revision 21450)
+++ subversion/libsvn_fs_base/tree.c (working copy)
@@ -4373,7 +4373,6 @@
   base_contents_changed,
   base_get_file_delta_stream,
   base_merge,
- NULL,
   NULL
 };
 
Index: subversion/libsvn_fs_util/merge_info_indexer.c
===================================================================
--- subversion/libsvn_fs_util/merge_info_indexer.c (revision 0)
+++ subversion/libsvn_fs_util/merge_info_indexer.c (revision 0)
@@ -0,0 +1,549 @@
+/* merge_info_indexer.c
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2006 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <apr_general.h>
+#include <apr_pools.h>
+
+#include <sqlite3.h>
+
+#include "svn_fs.h"
+#include "svn_path.h"
+#include "svn_mergeinfo.h"
+
+#include "../include/private/merge_info_indexer.h"
+
+#include "../libsvn_fs/fs-loader.h"
+
+#include "svn_private_config.h"
+
+/* Following are defines that specify the textual elements of the
+ native filesystem directories and revision files. */
+
+/* Names of special files in the filesystem. */
+#define PATH_MERGEINFO_DB "mergeinfo.db" /* Contains mergeinfo. */
+
+/* We want to cache that we saw no mergeinfo for a path as well,
+ so we use a -1 converted to a pointer to represent this. */
+#define NEGATIVE_CACHE_RESULT ((void *)(-1))
+
+static const char *
+path_mergeinfo_db(const char *path, apr_pool_t *pool)
+{
+ return svn_path_join(path, PATH_MERGEINFO_DB, pool);
+}
+
+/*#define SQLITE3_DEBUG 1 */
+#ifdef SQLITE3_DEBUG
+static void
+sqlite_tracer (void *data, const char *sql)
+{
+ /* sqlite3 *db = data; */
+ fprintf (stderr, "SQLITE SQL is \"%s\"\n", sql);
+}
+#endif
+
+/* Execute SQL on the sqlite database, and raise an SVN error if the
+ result is not okay. */
+
+static svn_error_t *
+util_sqlite_exec (sqlite3 *db, const char *sql,
+ sqlite3_callback callback,
+ void *callbackdata)
+{
+ char *errmsg;
+ if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ errmsg);
+ return SVN_NO_ERROR;
+}
+
+const char SVN_MTD_CREATE_SQL[] = "pragma auto_vacuum = 1;"
+ APR_EOL_STR
+ "create table mergeinfo (revision integer not null, mergedfrom text not null, mergedto text not null, mergedrevstart integer not null, mergedrevend integer not null);"
+ APR_EOL_STR
+ "create index mi_mergedfrom_idx on mergeinfo (mergedfrom);"
+ APR_EOL_STR
+ "create index mi_mergedto_idx on mergeinfo (mergedto);"
+ APR_EOL_STR
+ "create index mi_revision_idx on mergeinfo (revision);"
+ APR_EOL_STR
+ "create table mergeinfo_changed (revision integer not null, path text not null);"
+ APR_EOL_STR
+ "create unique index mi_c_revpath_idx on mergeinfo_changed (revision, path);"
+ APR_EOL_STR
+ "create index mi_c_path_idx on mergeinfo_changed (path);"
+ APR_EOL_STR
+ "create index mi_c_revision_idx on mergeinfo_changed (revision);"
+ APR_EOL_STR;
+
+svn_error_t *
+svn_fs_util_mtd_create(const char *path,
+ apr_pool_t *pool)
+{
+ sqlite3 *db;
+
+ SQLITE_ERR(sqlite3_open(path_mergeinfo_db(path, pool), &db), db);
+#ifdef SQLITE3_DEBUG
+ sqlite3_trace (db, sqlite_tracer, db);
+#endif
+ SVN_ERR(util_sqlite_exec(db, SVN_MTD_CREATE_SQL, NULL, NULL));
+
+ SQLITE_ERR(sqlite3_close(db), db);
+
+ return SVN_NO_ERROR;
+}
+
+/* Create SQL to insert the necessary indexing data for all the merge
+ info lists of PATH, which is provided (unparsed) in MINFOSTRING. */
+static svn_error_t *
+index_path_merge_info(svn_revnum_t new_rev, sqlite3 *db, const char *path,
+ svn_string_t *minfostring, apr_pool_t *pool)
+{
+ apr_hash_t *minfo;
+ apr_hash_index_t *hi;
+ sqlite3_stmt *stmt;
+
+ SVN_ERR(svn_mergeinfo_parse(minfostring->data, &minfo, pool));
+
+ for (hi = apr_hash_first(pool, minfo);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *from;
+ apr_array_header_t *revlist;
+ const void *key;
+ void *val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+
+ from = key;
+ revlist = val;
+ if (from && revlist)
+ {
+ int i;
+ SQLITE_ERR(sqlite3_prepare(db,
+ "INSERT INTO mergeinfo (revision, mergedto, mergedfrom, mergedrevstart, mergedrevend) VALUES (?, ?, ?, ?, ?);",
+ -1, &stmt, NULL), db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 1, new_rev), db);
+ SQLITE_ERR(sqlite3_bind_text(stmt, 2, path, -1, SQLITE_TRANSIENT),
+ db);
+ SQLITE_ERR(sqlite3_bind_text(stmt, 3, from, -1, SQLITE_TRANSIENT),
+ db);
+ for (i = 0; i < revlist->nelts; i++)
+ {
+ svn_merge_range_t *range;
+
+ range = APR_ARRAY_IDX(revlist, i, svn_merge_range_t *);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 4, range->start),
+ db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 5, range->end),
+ db);
+ if (sqlite3_step(stmt) != SQLITE_DONE)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
+
+ SQLITE_ERR(sqlite3_reset(stmt), db);
+ }
+ SQLITE_ERR(sqlite3_finalize(stmt), db);
+ }
+ }
+ SQLITE_ERR (sqlite3_prepare(db,
+ "INSERT INTO mergeinfo_changed (revision, path) VALUES (?, ?);",
+ -1, &stmt, NULL),
+ db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 1, new_rev), db);
+
+ SQLITE_ERR(sqlite3_bind_text(stmt, 2, path, -1, SQLITE_TRANSIENT),
+ db);
+
+ if (sqlite3_step(stmt) != SQLITE_DONE)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
+
+ SQLITE_ERR(sqlite3_finalize(stmt), db);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Create the index for any merge info in TXN (a no-op if TXN has no
+ associated merge info). */
+static svn_error_t *
+index_txn_merge_info(svn_fs_txn_t *txn, svn_revnum_t new_rev,
+ sqlite3 *db, apr_pool_t *pool)
+{
+ apr_hash_t *minfoprops;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(txn->vtable->txn_mergeinfo(&minfoprops, txn, pool));
+
+ for (hi = apr_hash_first(pool, minfoprops);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *minfopath;
+ svn_string_t *minfostring;
+ const void *key;
+ void *val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+
+ minfopath = key;
+ minfostring = val;
+
+ SVN_ERR(index_path_merge_info(new_rev, db, minfopath, minfostring,
+ pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+/* Clean the merge info index for any previous failed commit with the
+ revision number as NEW_REV, and if the current transaction contains
+ merge info, record it. */
+svn_error_t *
+svn_fs_util_mtd_update_merge_info_index(svn_fs_txn_t *txn, svn_revnum_t new_rev,
+ svn_boolean_t txn_contains_merge_info,
+ apr_pool_t *pool)
+{
+ const char *deletestring;
+
+ sqlite3 *db;
+
+ SQLITE_ERR(sqlite3_open(path_mergeinfo_db(txn->fs->path, pool), &db), db);
+#ifdef SQLITE3_DEBUG
+ sqlite3_trace (db, sqlite_tracer, db);
+#endif
+ SVN_ERR(util_sqlite_exec(db, "begin transaction;", NULL, NULL));
+
+ // Cleanup the leftovers of any previous, failed FSFS transactions
+ // involving NEW_REV.
+ deletestring = apr_psprintf(pool,
+ "delete from mergeinfo_changed where revision = %ld;",
+ new_rev);
+ SVN_ERR(util_sqlite_exec(db, deletestring, NULL, NULL));
+ deletestring = apr_psprintf(pool,
+ "delete from mergeinfo where revision = %ld;",
+ new_rev);
+ SVN_ERR(util_sqlite_exec(db, deletestring, NULL, NULL));
+
+ // Record any merge info from the current transaction.
+ if (txn_contains_merge_info)
+ SVN_ERR(index_txn_merge_info(txn, new_rev, db, pool));
+
+ // This is moved here from commit_txn, because we don't want to
+ // write the final current file if the sqlite commit fails.
+ // On the other hand, if we commit the transaction and end up failing
+ // the current file, we just end up with inaccessible data in the
+ // database, not a real problem.
+ SVN_ERR(util_sqlite_exec(db, "commit transaction;", NULL, NULL));
+ SQLITE_ERR(sqlite3_close(db), db);
+
+ return SVN_NO_ERROR;
+}
+
+/* Helper for get_merge_info that retrieves merge info for a single
+ revision from the database and puts it into a mergeinfo hash. */
+static svn_error_t *
+parse_mergeinfo_from_db(sqlite3 *db,
+ const char *path,
+ svn_revnum_t rev,
+ apr_hash_t **result,
+ apr_pool_t *pool)
+{
+ sqlite3_stmt *stmt;
+ sqlite_int64 lastchanged_rev;
+ int sqlite_result;
+
+ SQLITE_ERR(sqlite3_prepare(db, "SELECT MAX(revision) from mergeinfo_changed"
+ " where path = ? and revision <= ?;",
+ -1, &stmt, NULL), db);
+ SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 2, rev), db);
+ sqlite_result = sqlite3_step(stmt);
+ if (sqlite_result != SQLITE_ROW)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
+
+ lastchanged_rev = sqlite3_column_int64(stmt, 0);
+
+ SQLITE_ERR(sqlite3_finalize(stmt), db);
+
+ SQLITE_ERR(sqlite3_prepare(db,
+ "SELECT mergedfrom, mergedrevstart,"
+ "mergedrevend from mergeinfo "
+ "where mergedto = ? and revision = ? "
+ "order by mergedfrom;",
+ -1, &stmt, NULL), db);
+ SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 2, lastchanged_rev), db);
+ sqlite_result = sqlite3_step(stmt);
+
+ /* It is possible the mergeinfo changed because of a delete, and
+ that the mergeinfo is now gone. If this is the case, we want
+ to do nothing but fallthrough into the count == 0 case */
+ if (sqlite_result == SQLITE_DONE)
+ {
+ *result = NULL;
+ return SVN_NO_ERROR;
+ }
+ else if (sqlite_result == SQLITE_ROW)
+ {
+ apr_array_header_t *pathranges;
+ const char *mergedfrom;
+ svn_revnum_t startrev;
+ svn_revnum_t endrev;
+ const char *lastmergedfrom = NULL;
+
+ *result = apr_hash_make(pool);
+ pathranges = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
+
+ while (sqlite_result == SQLITE_ROW)
+ {
+ svn_merge_range_t *temprange;
+
+ mergedfrom = (char *) sqlite3_column_text(stmt, 0);
+ startrev = sqlite3_column_int64(stmt, 1);
+ endrev = sqlite3_column_int64(stmt, 2);
+ if (lastmergedfrom && strcmp(mergedfrom, lastmergedfrom) != 0)
+ {
+ apr_hash_set(*result, mergedfrom, APR_HASH_KEY_STRING,
+ pathranges);
+ pathranges = apr_array_make(pool, 1,
+ sizeof(svn_merge_range_t *));
+ }
+ temprange = apr_pcalloc(pool, sizeof(*temprange));
+ temprange->start = startrev;
+ temprange->end = endrev;
+ APR_ARRAY_PUSH(pathranges, svn_merge_range_t *) = temprange;
+ sqlite_result = sqlite3_step(stmt);
+ lastmergedfrom = mergedfrom;
+ }
+ apr_hash_set(*result, mergedfrom, APR_HASH_KEY_STRING, pathranges);
+
+ if (sqlite_result != SQLITE_DONE)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
+ }
+ else
+ {
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
+ }
+ SQLITE_ERR(sqlite3_finalize(stmt), db);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Helper for get_merge_info_for_path that will append a string to each
+ path that exists in the mergeinfo hash. */
+static svn_error_t *
+append_component_to_paths(apr_hash_t **output,
+ apr_hash_t *input,
+ const char *toappend,
+ apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+ *output = apr_hash_make(pool);
+
+ for (hi = apr_hash_first(pool, input);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+ apr_ssize_t klen;
+ char *newpath;
+
+ apr_hash_this(hi, &key, &klen, &val);
+ newpath = svn_path_join((const char *)key, toappend, pool);
+ apr_hash_set(*output, newpath, APR_HASH_KEY_STRING, val);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Helper for get_merge_info that will recursively get merge info for
+ a single path. */
+static svn_error_t *
+get_merge_info_for_path(sqlite3 *db,
+ const char *path,
+ svn_revnum_t rev,
+ apr_hash_t *result,
+ apr_hash_t *cache,
+ svn_boolean_t setresult,
+ svn_boolean_t include_parents,
+ apr_pool_t *pool)
+{
+ apr_hash_t *cacheresult;
+ sqlite3_stmt *stmt;
+ int sqlite_result;
+ sqlite_int64 count;
+ svn_boolean_t has_no_mergeinfo = FALSE;
+
+ cacheresult = apr_hash_get(cache, path, APR_HASH_KEY_STRING);
+ if (cacheresult != 0)
+ {
+ if (cacheresult != NEGATIVE_CACHE_RESULT && setresult)
+ apr_hash_set(result, path, APR_HASH_KEY_STRING, cacheresult);
+ return SVN_NO_ERROR;
+ }
+
+ /* See if we have a mergeinfo_changed record for this path. If not,
+ then it can't have mergeinfo. */
+ SQLITE_ERR(sqlite3_prepare(db, "SELECT COUNT(*) from mergeinfo_changed"
+ " where path = ? and revision <= ?;",
+ -1, &stmt, NULL), db);
+
+ SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
+ SQLITE_ERR(sqlite3_bind_int64(stmt, 2, rev), db);
+ sqlite_result = sqlite3_step(stmt);
+ if (sqlite_result != SQLITE_ROW)
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
+ sqlite3_errmsg(db));
+
+ count = sqlite3_column_int64(stmt, 0);
+ SQLITE_ERR(sqlite3_finalize(stmt), db);
+
+ /* If we've got mergeinfo data, transform it from the db into a
+ mergeinfo hash */
+ if (count > 0)
+ {
+ apr_hash_t *pathresult = NULL;
+
+ SVN_ERR(parse_mergeinfo_from_db(db, path, rev, &pathresult, pool));
+ if (pathresult)
+ {
+ if (setresult)
+ apr_hash_set(result, path, APR_HASH_KEY_STRING, pathresult);
+ apr_hash_set(cache, path, APR_HASH_KEY_STRING, pathresult);
+ }
+ else
+ has_no_mergeinfo = TRUE;
+ }
+
+ /* If this path has no mergeinfo, and we are asked to, check our parent */
+ if ((count == 0 || has_no_mergeinfo) && include_parents)
+ {
+ svn_stringbuf_t *parentpath;
+
+ apr_hash_set(cache, path, APR_HASH_KEY_STRING, NEGATIVE_CACHE_RESULT);
+
+ /* It is possible we are already at the root. */
+ if (strcmp(path, "") == 0)
+ return SVN_NO_ERROR;
+
+ parentpath = svn_stringbuf_create(path, pool);
+ svn_path_remove_component(parentpath);
+
+ /* The repository, and the mergeinfo index, internally refer to
+ "/" as "" */
+ if (strcmp(parentpath->data, "/") == 0)
+ parentpath->data = "";
+
+ SVN_ERR(get_merge_info_for_path(db, parentpath->data, rev,
+ result, cache, FALSE, include_parents,
+ pool));
+ if (setresult)
+ {
+ /* Now translate the result for our parent to our path */
+ cacheresult = apr_hash_get(cache, parentpath->data,
+ APR_HASH_KEY_STRING);
+ if (cacheresult == NEGATIVE_CACHE_RESULT)
+ apr_hash_set(result, path, APR_HASH_KEY_STRING, NULL);
+ else if (cacheresult)
+ {
+ const char *p;
+ int i;
+ apr_hash_t *translatedhash;
+ char *toappend;
+
+ /* We want to append from the part after the / to the end of the
+ path string. */
+ toappend = apr_pcalloc(pool,
+ (strlen(path) - parentpath->len) + 1);
+ for (i = 0, p = &path[parentpath->len + 1]; *p; i++, p++)
+ *(toappend + i) = *p;
+ append_component_to_paths(&translatedhash, cacheresult,
+ toappend, pool);
+ apr_hash_set(result, path, APR_HASH_KEY_STRING,
+ translatedhash);
+ }
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+
+
+/* Get the merge info for a set of paths. */
+svn_error_t *
+svn_fs_util_get_merge_info(apr_hash_t **mergeinfo,
+ svn_fs_root_t *root,
+ const apr_array_header_t *paths,
+ svn_boolean_t include_parents,
+ apr_pool_t *pool)
+{
+ apr_hash_t *mergeinfo_cache = apr_hash_make (pool);
+ sqlite3 *db;
+ int i;
+ svn_revnum_t rev;
+
+ /* We require a revision root. */
+ if (root->is_txn_root)
+ return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
+ rev = svn_fs_revision_root_revision(root);
+
+ SQLITE_ERR(sqlite3_open(path_mergeinfo_db(root->fs->path, pool), &db), db);
+
+#ifdef SQLITE3_DEBUG
+ sqlite3_trace (db, sqlite_tracer, db);
+#endif
+
+ *mergeinfo = apr_hash_make (pool);
+ for (i = 0; i < paths->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
+
+ SVN_ERR (get_merge_info_for_path (db, path, rev, *mergeinfo,
+ mergeinfo_cache, TRUE,
+ include_parents, pool));
+ }
+
+ for (i = 0; i < paths->nelts; i++)
+ {
+ svn_stringbuf_t *mergestring;
+ apr_hash_t *currhash;
+ const char *path = APR_ARRAY_IDX(paths, i, const char *);
+
+ currhash = apr_hash_get(*mergeinfo, path, APR_HASH_KEY_STRING);
+ if (currhash)
+ {
+ SVN_ERR (svn_mergeinfo_sort(currhash, pool));
+ SVN_ERR (svn_mergeinfo_to_string(&mergestring, currhash, pool));
+ apr_hash_set(*mergeinfo, path, APR_HASH_KEY_STRING, mergestring->data);
+ }
+ }
+ SQLITE_ERR(sqlite3_close(db), db);
+ return SVN_NO_ERROR;
+}
Index: subversion/libsvn_fs/fs-loader.h
===================================================================
--- subversion/libsvn_fs/fs-loader.h (revision 21450)
+++ subversion/libsvn_fs/fs-loader.h (working copy)
@@ -184,6 +184,8 @@
                               const svn_string_t *value, apr_pool_t *pool);
   svn_error_t *(*root)(svn_fs_root_t **root_p, svn_fs_txn_t *txn,
                        apr_pool_t *pool);
+ svn_error_t *(*txn_mergeinfo)(apr_hash_t **minfoprops, svn_fs_txn_t *txn,
+ apr_pool_t *pool);
 } txn_vtable_t;
 
 
@@ -291,11 +293,6 @@
   svn_error_t *(*change_merge_info)(svn_fs_root_t *root, const char *path,
                                     apr_hash_t *info,
                                     apr_pool_t *pool);
- svn_error_t *(*get_merge_info)(apr_hash_t **minfohash,
- svn_fs_root_t *root,
- const apr_array_header_t *paths,
- svn_boolean_t include_parents,
- apr_pool_t *pool);
 } root_vtable_t;
 
 
Index: subversion/libsvn_fs/fs-loader.c
===================================================================
--- subversion/libsvn_fs/fs-loader.c (revision 21450)
+++ subversion/libsvn_fs/fs-loader.c (working copy)
@@ -353,6 +353,7 @@
   /* Perform the actual creation. */
   *fs_p = svn_fs_new(fs_config, pool);
   SVN_ERR(vtable->create(*fs_p, path, pool));
+ SVN_ERR(svn_fs_util_mtd_create(path, pool));
   return serialized_init(*fs_p, pool);
 }
 
@@ -734,8 +735,8 @@
                       svn_boolean_t include_parents,
                       apr_pool_t *pool)
 {
- return root->vtable->get_merge_info(minfohash, root, paths, include_parents,
- pool);
+ return svn_fs_util_get_merge_info(minfohash, root, paths, include_parents,
+ pool);
 }
 
 svn_error_t *
Index: subversion/libsvn_fs_fs/tree.c
===================================================================
--- subversion/libsvn_fs_fs/tree.c (revision 21450)
+++ subversion/libsvn_fs_fs/tree.c (working copy)
@@ -1078,305 +1078,7 @@
   return SVN_NO_ERROR;
 }
 
-/* #define SQLITE3_DEBUG 1 */
-#ifdef SQLITE3_DEBUG
-static void
-sqlite_tracer(void *data, const char *sql)
-{
- /* sqlite3 *db = data; */
- fprintf(stderr, "SQLITE SQL is \"%s\"\n", sql);
-}
-#endif
-
-/* We want to cache that we saw no mergeinfo for a path as well,
- so we use a -1 converted to a pointer to represent this. */
-#define NEGATIVE_CACHE_RESULT ((void *)(-1))
-
-/* Helper for get_merge_info that retrieves merge info for a single
- revision from the database and puts it into a mergeinfo hash. */
-static svn_error_t *
-parse_mergeinfo_from_db(sqlite3 *db,
- const char *path,
- svn_revnum_t rev,
- apr_hash_t **result,
- apr_pool_t *pool)
-{
- sqlite3_stmt *stmt;
- sqlite_int64 lastchanged_rev;
- int sqlite_result;
-
- SQLITE_ERR(sqlite3_prepare(db, "SELECT MAX(revision) from mergeinfo_changed"
- " where path = ? and revision <= ?;",
- -1, &stmt, NULL), db);
- SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 2, rev), db);
- sqlite_result = sqlite3_step(stmt);
- if (sqlite_result != SQLITE_ROW)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(db));
-
- lastchanged_rev = sqlite3_column_int64(stmt, 0);
-
- SQLITE_ERR(sqlite3_finalize(stmt), db);
-
- SQLITE_ERR(sqlite3_prepare(db,
- "SELECT mergedfrom, mergedrevstart,"
- "mergedrevend from mergeinfo "
- "where mergedto = ? and revision = ? "
- "order by mergedfrom;",
- -1, &stmt, NULL), db);
- SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 2, lastchanged_rev), db);
- sqlite_result = sqlite3_step(stmt);
-
- /* It is possible the mergeinfo changed because of a delete, and
- that the mergeinfo is now gone. If this is the case, we want
- to do nothing but fallthrough into the count == 0 case */
- if (sqlite_result == SQLITE_DONE)
- {
- *result = NULL;
- return SVN_NO_ERROR;
- }
- else if (sqlite_result == SQLITE_ROW)
- {
- apr_array_header_t *pathranges;
- const char *mergedfrom;
- svn_revnum_t startrev;
- svn_revnum_t endrev;
- const char *lastmergedfrom = NULL;
-
- *result = apr_hash_make(pool);
- pathranges = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
-
- while (sqlite_result == SQLITE_ROW)
- {
- svn_merge_range_t *temprange;
-
- mergedfrom = (char *) sqlite3_column_text(stmt, 0);
- startrev = sqlite3_column_int64(stmt, 1);
- endrev = sqlite3_column_int64(stmt, 2);
- if (lastmergedfrom && strcmp(mergedfrom, lastmergedfrom) != 0)
- {
- apr_hash_set(*result, mergedfrom, APR_HASH_KEY_STRING,
- pathranges);
- pathranges = apr_array_make(pool, 1,
- sizeof(svn_merge_range_t *));
- }
- temprange = apr_pcalloc(pool, sizeof(*temprange));
- temprange->start = startrev;
- temprange->end = endrev;
- APR_ARRAY_PUSH(pathranges, svn_merge_range_t *) = temprange;
- sqlite_result = sqlite3_step(stmt);
- lastmergedfrom = mergedfrom;
- }
- apr_hash_set(*result, mergedfrom, APR_HASH_KEY_STRING, pathranges);
-
- if (sqlite_result != SQLITE_DONE)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(db));
- }
- else
- {
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(db));
- }
- SQLITE_ERR(sqlite3_finalize(stmt), db);
-
- return SVN_NO_ERROR;
-}
-
-/* Helper for get_merge_info_for_path that will append a string to each
- path that exists in the mergeinfo hash. */
-static svn_error_t *
-append_component_to_paths(apr_hash_t **output,
- apr_hash_t *input,
- const char *toappend,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- *output = apr_hash_make(pool);
-
- for (hi = apr_hash_first(pool, input);
- hi;
- hi = apr_hash_next(hi))
- {
- const void *key;
- void *val;
- apr_ssize_t klen;
- char *newpath;
-
- apr_hash_this(hi, &key, &klen, &val);
- newpath = svn_path_join((const char *)key, toappend, pool);
- apr_hash_set(*output, newpath, APR_HASH_KEY_STRING, val);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Helper for get_merge_info that will recursively get merge info for
- a single path. */
-static svn_error_t *
-get_merge_info_for_path(sqlite3 *db,
- const char *path,
- svn_revnum_t rev,
- apr_hash_t *result,
- apr_hash_t *cache,
- svn_boolean_t setresult,
- svn_boolean_t include_parents,
- apr_pool_t *pool)
-{
- apr_hash_t *cacheresult;
- sqlite3_stmt *stmt;
- int sqlite_result;
- sqlite_int64 count;
- svn_boolean_t has_no_mergeinfo = FALSE;
-
- cacheresult = apr_hash_get(cache, path, APR_HASH_KEY_STRING);
- if (cacheresult != 0)
- {
- if (cacheresult != NEGATIVE_CACHE_RESULT && setresult)
- apr_hash_set(result, path, APR_HASH_KEY_STRING, cacheresult);
- return SVN_NO_ERROR;
- }
-
- /* See if we have a mergeinfo_changed record for this path. If not,
- then it can't have mergeinfo. */
- SQLITE_ERR(sqlite3_prepare(db, "SELECT COUNT(*) from mergeinfo_changed"
- " where path = ? and revision <= ?;",
- -1, &stmt, NULL), db);
-
- SQLITE_ERR(sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT), db);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 2, rev), db);
- sqlite_result = sqlite3_step(stmt);
- if (sqlite_result != SQLITE_ROW)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(db));
-
- count = sqlite3_column_int64(stmt, 0);
- SQLITE_ERR(sqlite3_finalize(stmt), db);
-
- /* If we've got mergeinfo data, transform it from the db into a
- mergeinfo hash */
- if (count > 0)
- {
- apr_hash_t *pathresult = NULL;
-
- SVN_ERR(parse_mergeinfo_from_db(db, path, rev, &pathresult, pool));
- if (pathresult)
- {
- if (setresult)
- apr_hash_set(result, path, APR_HASH_KEY_STRING, pathresult);
- apr_hash_set(cache, path, APR_HASH_KEY_STRING, pathresult);
- }
- else
- has_no_mergeinfo = TRUE;
- }
-
- /* If this path has no mergeinfo, and we are asked to, check our parent */
- if ((count == 0 || has_no_mergeinfo) && include_parents)
- {
- svn_stringbuf_t *parentpath;
-
- apr_hash_set(cache, path, APR_HASH_KEY_STRING, NEGATIVE_CACHE_RESULT);
-
- /* It is possible we are already at the root. */
- if (strcmp(path, "") == 0)
- return SVN_NO_ERROR;
-
- parentpath = svn_stringbuf_create(path, pool);
- svn_path_remove_component(parentpath);
-
- /* The repository, and the mergeinfo index, internally refer to
- "/" as "" */
- if (strcmp(parentpath->data, "/") == 0)
- parentpath->data = "";
-
- SVN_ERR(get_merge_info_for_path(db, parentpath->data, rev,
- result, cache, FALSE, include_parents,
- pool));
- if (setresult)
- {
- /* Now translate the result for our parent to our path */
- cacheresult = apr_hash_get(cache, parentpath->data,
- APR_HASH_KEY_STRING);
- if (cacheresult == NEGATIVE_CACHE_RESULT)
- apr_hash_set(result, path, APR_HASH_KEY_STRING, NULL);
- else if (cacheresult)
- {
- const char *p;
- int i;
- apr_hash_t *translatedhash;
- char *toappend;
-
- /* We want to append from the part after the / to the end of the
- path string. */
- toappend = apr_pcalloc(pool,
- (strlen(path) - parentpath->len) + 1);
- for (i = 0, p = &path[parentpath->len + 1]; *p; i++, p++)
- *(toappend + i) = *p;
- append_component_to_paths(&translatedhash, cacheresult,
- toappend, pool);
- apr_hash_set(result, path, APR_HASH_KEY_STRING,
- translatedhash);
- }
- }
- }
- return SVN_NO_ERROR;
-}
-
                         
-/* Get the merge info for a set of paths. */
-static svn_error_t *
-fs_get_merge_info(apr_hash_t **mergeinfo,
- svn_fs_root_t *root,
- const apr_array_header_t *paths,
- svn_boolean_t include_parents,
- apr_pool_t *pool)
-{
- apr_hash_t *mergeinfo_cache = apr_hash_make (pool);
- sqlite3 *db;
- int i;
- svn_revnum_t rev;
-
- /* We require a revision root. */
- if (root->is_txn_root)
- return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
- rev = svn_fs_revision_root_revision(root);
-
- SQLITE_ERR(sqlite3_open(svn_path_join(root->fs->path, "mergeinfo.db", pool),
- &db), db);
-#ifdef SQLITE3_DEBUG
- sqlite3_trace (db, sqlite_tracer, db);
-#endif
-
- *mergeinfo = apr_hash_make (pool);
- for (i = 0; i < paths->nelts; i++)
- {
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
-
- SVN_ERR (get_merge_info_for_path (db, path, rev, *mergeinfo,
- mergeinfo_cache, TRUE,
- include_parents, pool));
- }
-
- for (i = 0; i < paths->nelts; i++)
- {
- svn_stringbuf_t *mergestring;
- apr_hash_t *currhash;
- const char *path = APR_ARRAY_IDX(paths, i, const char *);
-
- currhash = apr_hash_get(*mergeinfo, path, APR_HASH_KEY_STRING);
- if (currhash)
- {
- SVN_ERR (svn_mergeinfo_sort(currhash, pool));
- SVN_ERR (svn_mergeinfo_to_string(&mergestring, currhash, pool));
- apr_hash_set(*mergeinfo, path, APR_HASH_KEY_STRING, mergestring->data);
- }
- }
- SQLITE_ERR(sqlite3_close(db), db);
- return SVN_NO_ERROR;
-}
-
 /* Change the merge info for a given path. */
 static svn_error_t *
 fs_change_merge_info(svn_fs_root_t *root,
@@ -3521,8 +3223,7 @@
   fs_contents_changed,
   fs_get_file_delta_stream,
   fs_merge,
- fs_change_merge_info,
- fs_get_merge_info
+ fs_change_merge_info
 };
 
 /* Construct a new root object in FS, allocated from POOL. */
Index: subversion/libsvn_fs_fs/fs_fs.c
===================================================================
--- subversion/libsvn_fs_fs/fs_fs.c (revision 21450)
+++ subversion/libsvn_fs_fs/fs_fs.c (working copy)
@@ -28,8 +28,6 @@
 #include <apr_md5.h>
 #include <apr_thread_mutex.h>
 
-#include <sqlite3.h>
-
 #include "svn_pools.h"
 #include "svn_fs.h"
 #include "svn_path.h"
@@ -48,6 +46,7 @@
 #include "fs_fs.h"
 #include "id.h"
 
+#include "../include/private/merge_info_indexer.h"
 #include "../libsvn_fs/fs-loader.h"
 
 #include "svn_private_config.h"
@@ -130,7 +129,8 @@
   svn_fs_fs__txn_prop,
   svn_fs_fs__txn_proplist,
   svn_fs_fs__change_txn_prop,
- svn_fs_fs__txn_root
+ svn_fs_fs__txn_root,
+ svn_fs_fs__txn_mergeinfo
 };
 
 /* Pathname helper functions */
@@ -159,12 +159,6 @@
   return svn_path_join(fs->path, PATH_LOCK_FILE, pool);
 }
 
-static const char *
-path_mergeinfo_db(svn_fs_t *fs, apr_pool_t *pool)
-{
- return svn_path_join(fs->path, PATH_MERGEINFO_DB, pool);
-}
-
 const char *
 svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool)
 {
@@ -303,16 +297,6 @@
   return SVN_NO_ERROR;
 }
 
-/*#define SQLITE3_DEBUG 1 */
-#ifdef SQLITE3_DEBUG
-static void
-sqlite_tracer (void *data, const char *sql)
-{
- /* sqlite3 *db = data; */
- fprintf (stderr, "SQLITE SQL is \"%s\"\n", sql);
-}
-#endif
-
 svn_error_t *
 svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool)
 {
@@ -2544,7 +2528,6 @@
 {
   svn_fs_txn_t *txn;
   svn_fs_id_t *root_id;
- fs_txn_data_t *ftd;
 
   txn = apr_pcalloc(pool, sizeof(*txn));
 
@@ -2555,8 +2538,6 @@
   txn->base_rev = rev;
 
   txn->vtable = &txn_vtable;
- ftd = apr_pcalloc(pool, sizeof(*ftd));
- txn->fsap_data = ftd;
   *txn_p = txn;
   
   /* Create a new root node for this transaction. */
@@ -3989,115 +3970,6 @@
   return SVN_NO_ERROR;
 }
 
-/* Create SQL to insert the necessary indexing data for all the merge
- info lists of PATH, which is provided (unparsed) in MINFOSTRING. */
-static svn_error_t *
-index_path_merge_info(svn_fs_txn_t *txn, svn_revnum_t new_rev,
- const char *path, svn_string_t *minfostring,
- apr_pool_t *pool)
-{
- apr_hash_t *minfo;
- apr_hash_index_t *hi;
- sqlite3_stmt *stmt;
- fs_txn_data_t *ftd;
-
- ftd = txn->fsap_data;
-
- SVN_ERR(svn_mergeinfo_parse(minfostring->data, &minfo, pool));
-
- for (hi = apr_hash_first(pool, minfo);
- hi != NULL;
- hi = apr_hash_next(hi))
- {
- const char *from;
- apr_array_header_t *revlist;
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
-
- from = key;
- revlist = val;
-
- if (from && revlist)
- {
- int i;
- SQLITE_ERR(sqlite3_prepare(ftd->mtd,
- "INSERT INTO mergeinfo (revision, mergedto, mergedfrom, mergedrevstart, mergedrevend) VALUES (?, ?, ?, ?, ?);",
- -1, &stmt, NULL), ftd->mtd);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 1, new_rev), ftd->mtd);
- SQLITE_ERR(sqlite3_bind_text(stmt, 2, path, -1, SQLITE_TRANSIENT),
- ftd->mtd);
- SQLITE_ERR(sqlite3_bind_text(stmt, 3, from, -1, SQLITE_TRANSIENT),
- ftd->mtd);
- for (i = 0; i < revlist->nelts; i++)
- {
- svn_merge_range_t *range;
-
- range = APR_ARRAY_IDX(revlist, i, svn_merge_range_t *);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 4, range->start),
- ftd->mtd);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 5, range->end),
- ftd->mtd);
- if (sqlite3_step(stmt) != SQLITE_DONE)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(ftd->mtd));
-
- SQLITE_ERR(sqlite3_reset(stmt), ftd->mtd);
- }
- SQLITE_ERR(sqlite3_finalize(stmt), ftd->mtd);
- }
- }
- SQLITE_ERR (sqlite3_prepare(ftd->mtd,
- "INSERT INTO mergeinfo_changed (revision, path) VALUES (?, ?);",
- -1, &stmt, NULL),
- ftd->mtd);
- SQLITE_ERR(sqlite3_bind_int64(stmt, 1, new_rev), ftd->mtd);
-
- SQLITE_ERR(sqlite3_bind_text(stmt, 2, path, -1, SQLITE_TRANSIENT),
- ftd->mtd);
-
- if (sqlite3_step(stmt) != SQLITE_DONE)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- sqlite3_errmsg(ftd->mtd));
-
- SQLITE_ERR(sqlite3_finalize(stmt), ftd->mtd);
-
- return SVN_NO_ERROR;
-}
-
-/* Create the index for any merge info in TXN (a no-op if TXN has no
- associated merge info). */
-static svn_error_t *
-index_txn_merge_info(svn_fs_txn_t *txn, svn_revnum_t new_rev,
- apr_pool_t *pool)
-{
- apr_hash_t *minfoprops;
- apr_hash_index_t *hi;
-
- SVN_ERR(svn_fs_fs__txn_mergeinfo(&minfoprops, txn, pool));
-
- for (hi = apr_hash_first(pool, minfoprops);
- hi != NULL;
- hi = apr_hash_next(hi))
- {
- const char *minfopath;
- svn_string_t *minfostring;
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
-
- minfopath = key;
- minfostring = val;
-
- SVN_ERR(index_path_merge_info(txn, new_rev, minfopath, minfostring,
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
 /* Baton used for commit_body below. */
 struct commit_baton {
   svn_revnum_t *new_rev_p;
@@ -4105,50 +3977,6 @@
   svn_fs_txn_t *txn;
 };
 
-/* Clean the merge info index for any previous failed commit with the
- revision number as NEW_REV, and if the current transaction contains
- merge info, record it. */
-static svn_error_t *
-update_merge_info_index(struct commit_baton *cb, svn_revnum_t new_rev,
- svn_boolean_t txn_contains_merge_info,
- apr_pool_t *pool)
-{
- const char *deletestring;
- fs_txn_data_t *ftd = cb->txn->fsap_data;
-
- SQLITE_ERR(sqlite3_open(path_mergeinfo_db(cb->fs, pool),
- &ftd->mtd), ftd->mtd);
-#ifdef SQLITE3_DEBUG
- sqlite3_trace (ftd->mtd, sqlite_tracer, ftd->mtd);
-#endif
- SVN_ERR(fs_sqlite_exec(ftd->mtd, "begin transaction;", NULL, NULL));
-
- /* Cleanup the leftovers of any previous, failed FSFS transactions
- involving NEW_REV. */
- deletestring = apr_psprintf(pool,
- "delete from mergeinfo_changed where revision = %ld;",
- new_rev);
- SVN_ERR(fs_sqlite_exec(ftd->mtd, deletestring, NULL, NULL));
- deletestring = apr_psprintf(pool,
- "delete from mergeinfo where revision = %ld;",
- new_rev);
- SVN_ERR(fs_sqlite_exec(ftd->mtd, deletestring, NULL, NULL));
-
- /* Record any merge info from the current transaction. */
- if (txn_contains_merge_info)
- SVN_ERR(index_txn_merge_info(cb->txn, new_rev, pool));
-
- /* This is moved here from commit_txn, because we don't want to
- write the final current file if the sqlite commit fails.
- On the other hand, if we commit the transaction and end up failing
- the current file, we just end up with inaccessible data in the
- database, not a real problem. */
- SVN_ERR(fs_sqlite_exec(ftd->mtd, "commit transaction;", NULL, NULL));
- SQLITE_ERR(sqlite3_close(ftd->mtd), ftd->mtd);
-
- return SVN_NO_ERROR;
-}
-
 /* The work-horse for svn_fs_fs__commit, called with the FS write lock.
    This implements the svn_fs_fs__with_write_lock() 'body' callback
    type. BATON is a 'struct commit_baton *'. */
@@ -4266,8 +4094,10 @@
   SVN_ERR(svn_fs_fs__move_into_place(revprop_filename, final_revprop,
                                      old_rev_filename, pool));
   
- SVN_ERR(update_merge_info_index(cb, new_rev, txn_contains_merge_info, pool));
+ svn_fs_util_mtd_update_merge_info_index(cb->txn, new_rev,
+ txn_contains_merge_info, pool);
 
+
   /* Update the 'current' file. */
   SVN_ERR(write_final_current(cb->fs, cb->txn->id, new_rev, start_node_id,
                               start_copy_id, pool));
@@ -4345,57 +4175,15 @@
   return svn_fs_fs__set_revision_proplist(fs, 0, proplist, fs->pool);
 }
 
-/* Execute SQL on the sqlite database, and raise an SVN error if the
- result is not okay. */
-
 svn_error_t *
-fs_sqlite_exec (sqlite3 *db, const char *sql,
- sqlite3_callback callback,
- void *callbackdata)
-{
- char *errmsg;
- if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
- errmsg);
- return SVN_NO_ERROR;
-}
-
-const char SVN_MTD_CREATE_SQL[] = "pragma auto_vacuum = 1;"
- APR_EOL_STR
- "create table mergeinfo (revision integer not null, mergedfrom text not null, mergedto text not null, mergedrevstart integer not null, mergedrevend integer not null);"
- APR_EOL_STR
- "create index mi_mergedfrom_idx on mergeinfo (mergedfrom);"
- APR_EOL_STR
- "create index mi_mergedto_idx on mergeinfo (mergedto);"
- APR_EOL_STR
- "create index mi_revision_idx on mergeinfo (revision);"
- APR_EOL_STR
- "create table mergeinfo_changed (revision integer not null, path text not null);"
- APR_EOL_STR
- "create unique index mi_c_revpath_idx on mergeinfo_changed (revision, path);"
- APR_EOL_STR
- "create index mi_c_path_idx on mergeinfo_changed (path);"
- APR_EOL_STR
- "create index mi_c_revision_idx on mergeinfo_changed (revision);"
- APR_EOL_STR;
-
-svn_error_t *
 svn_fs_fs__create(svn_fs_t *fs,
                   const char *path,
                   apr_pool_t *pool)
 {
   int format = SVN_FS_FS__FORMAT_NUMBER;
- fs_fs_data_t *ffd = fs->fsap_data;
 
   fs->path = apr_pstrdup(pool, path);
 
- SQLITE_ERR(sqlite3_open(path_mergeinfo_db(fs, pool), &ffd->mtd),
- ffd->mtd);
-#ifdef SQLITE3_DEBUG
- sqlite3_trace (ffd->mtd, sqlite_tracer, ffd->mtd);
-#endif
- SVN_ERR(fs_sqlite_exec(ffd->mtd, SVN_MTD_CREATE_SQL, NULL, NULL));
-
   SVN_ERR(svn_io_make_dir_recursively(svn_path_join(path, PATH_REVS_DIR,
                                                     pool),
                                       pool));
@@ -4422,10 +4210,6 @@
           (path_format(fs, pool), format, pool));
   ((fs_fs_data_t *) fs->fsap_data)->format = format;
   
- SQLITE_ERR(sqlite3_close(ffd->mtd), ffd->mtd);
-
- ffd->mtd = NULL;
-
   return SVN_NO_ERROR;
 }
 
@@ -4522,7 +4306,6 @@
   svn_fs_txn_t *txn;
   svn_node_kind_t kind;
   transaction_t *local_txn;
- fs_txn_data_t *ftd;
 
   /* First check to see if the directory exists. */
   SVN_ERR(svn_io_check_path(path_txn_dir(fs, name, pool), &kind, pool));
@@ -4543,8 +4326,6 @@
   txn->base_rev = svn_fs_fs__id_rev(local_txn->base_id);
 
   txn->vtable = &txn_vtable;
- ftd = apr_pcalloc(pool, sizeof(*ftd));
- txn->fsap_data = ftd;
   *txn_p = txn;
 
   return SVN_NO_ERROR;
Index: subversion/libsvn_fs_fs/fs.h
===================================================================
--- subversion/libsvn_fs_fs/fs.h (revision 21450)
+++ subversion/libsvn_fs_fs/fs.h (working copy)
@@ -22,7 +22,6 @@
 #include <apr_hash.h>
 #include <apr_md5.h>
 #include <apr_thread_mutex.h>
-#include <sqlite3.h>
 
 #include "svn_fs.h"
 
@@ -54,9 +53,6 @@
   apr_hash_t *dir_cache[NUM_DIR_CACHE_ENTRIES];
   apr_pool_t *dir_cache_pool[NUM_DIR_CACHE_ENTRIES];
 
- /* Merge tracking database. */
- sqlite3 *mtd;
-
   /* The format number of this FS. */
   int format;
 
@@ -71,14 +67,6 @@
 #endif
 } fs_fs_data_t;
 
-/* Transactions need their own private opened copy of the mergeinfo
- database so that their sql commands are not shared between each other. */
-typedef struct
-{
- /* Merge tracking database. */
- sqlite3 *mtd;
-} fs_txn_data_t;
-
 /* Return a canonicalized version of a filesystem PATH, allocated in
    POOL. While the filesystem API is pretty flexible about the
    incoming paths (they must be UTF-8 with '/' as separators, but they
@@ -205,23 +193,6 @@
 
 } change_t;
 
-/* Wrapper for sqlite_exec that returns an SVN error on errors.
- Parameters match those of sqlite_exec, meaning that DB is the
- database to operate on, SQL is the string of sql to submit, CB is
- the callback to call with the results, and DATA is the first
- argument given to callback. */
-svn_error_t *fs_sqlite_exec(sqlite3 *db , const char *sql,
- sqlite3_callback cb, void *data);
-
-/* SQLITE->SVN quick error wrap, much like SVN_ERR.
- XXX: This macro probably belongs elsehwere, like svn_sqlite.h or
- something. Later. */
-#define SQLITE_ERR(x, db) do \
-{ \
- if ((x) != SQLITE_OK) \
- return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL, \
- sqlite3_errmsg((db))); \
- } while (0)
 
 #ifdef __cplusplus
 }
Index: subversion/include/private/merge_info_indexer.h
===================================================================
--- subversion/include/private/merge_info_indexer.h (revision 0)
+++ subversion/include/private/merge_info_indexer.h (revision 0)
@@ -0,0 +1,45 @@
+/*
+ * merge_info_indexer.h: Declarations for the
+ * public functions of libsvn_fs_util to be consumed by only fs_* libs.
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2006 CollabNet. All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+svn_error_t *
+svn_fs_util_mtd_create(const char *path, apr_pool_t *pool);
+
+svn_error_t *
+svn_fs_util_mtd_update_merge_info_index(svn_fs_txn_t *txn, svn_revnum_t new_rev,
+ svn_boolean_t txn_contains_merge_info,
+ apr_pool_t *pool);
+
+/* Get the merge info for a set of paths. */
+svn_error_t *
+svn_fs_util_get_merge_info(apr_hash_t **mergeinfo,
+ svn_fs_root_t *root,
+ const apr_array_header_t *paths,
+ svn_boolean_t include_parents,
+ apr_pool_t *pool);
+
+/* SQLITE->SVN quick error wrap, much like SVN_ERR.
+ XXX: This macro probably belongs elsewhere, like svn_sqlite.h or
+ something. Later. */
+#define SQLITE_ERR(x, db) do \
+{ \
+ if ((x) != SQLITE_OK) \
+ return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL, \
+ sqlite3_errmsg((db))); \
+} while (0)
+

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Sep 12 13:23:17 2006

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.