1 /* mergeinfo-sqlite-index.c
3 * ====================================================================
4 * Copyright (c) 2006-2007 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
25 #include <apr_general.h>
26 #include <apr_pools.h>
32 #include "svn_mergeinfo.h"
33 #include "svn_pools.h"
35 #include "private/svn_dep_compat.h"
36 #include "private/svn_fs_sqlite.h"
37 #include "private/svn_fs_mergeinfo.h"
38 #include "../libsvn_fs/fs-loader.h"
39 #include "svn_private_config.h"
41 #include "sqlite-util.h"
44 * A general warning about the mergeinfo tables:
46 * The sqlite transaction is committed (immediately) before the actual
47 * FS transaction is committed. Thus, any query against any mergeinfo
48 * table MUST contain a guard on the revision column guaranteeing that
49 * the returned rows have a revision value no greater than some
50 * known-committed revision number!
54 /* This is a macro implementation of svn_fs_revision_root_revision(), which
55 we cannot call from here, because it would create a circular dependency. */
56 #define REV_ROOT_REV(root) \
57 ((root)->is_txn_root ? SVN_INVALID_REVNUM : (root)->rev)
59 /* We want to cache that we saw no mergeinfo for a path as well,
60 so we use a -1 converted to a pointer to represent this. */
61 #define NEGATIVE_CACHE_RESULT ((void *)(-1))
63 /* A flow-control helper macro for sending processing to the 'cleanup'
64 label when the local variable 'err' is not SVN_NO_ERROR. */
65 #define MAYBE_CLEANUP if (err) goto cleanup
69 get_mergeinfo_for_path(sqlite3
*db
,
74 svn_mergeinfo_inheritance_t inherit
,
77 /* Represents "no mergeinfo". */
78 static svn_merge_range_t no_mergeinfo
= { SVN_INVALID_REVNUM
,
82 /* Insert the necessary indexing data into the DB for all the merges
83 on PATH as of NEW_REV, which is provided (unparsed) in
84 MERGEINFO_STR. Use POOL for temporary allocations.*/
86 index_path_mergeinfo(svn_revnum_t new_rev
,
89 svn_string_t
*mergeinfo_str
,
92 apr_hash_t
*mergeinfo
;
94 svn_fs__sqlite_stmt_t
*stmt
;
95 svn_boolean_t remove_mergeinfo
= FALSE
;
97 SVN_ERR(svn_mergeinfo_parse(&mergeinfo
, mergeinfo_str
->data
, pool
));
99 if (apr_hash_count(mergeinfo
) == 0)
101 /* All mergeinfo has been removed from PATH (or explicitly set
102 to "none", if there previously was no mergeinfo). Find all
103 previous mergeinfo, and (further below) insert dummy records
104 representing "no mergeinfo" for all its previous merge
107 Even though POOL is for temporary allocations, invocation of
108 get_mergeinfo_for_path() necessitates its own sub-pool. */
109 apr_pool_t
*subpool
= svn_pool_create(pool
);
110 apr_hash_t
*cache
= apr_hash_make(subpool
);
111 remove_mergeinfo
= TRUE
;
112 SVN_ERR(get_mergeinfo_for_path(db
, path
, new_rev
- 1, NULL
, cache
,
113 svn_mergeinfo_inherited
, subpool
));
114 mergeinfo
= apr_hash_get(cache
, path
, APR_HASH_KEY_STRING
);
115 if (mergeinfo
== NEGATIVE_CACHE_RESULT
)
118 mergeinfo
= svn_mergeinfo_dup(mergeinfo
, pool
);
120 svn_pool_destroy(subpool
);
122 if (mergeinfo
== NULL
)
123 /* There was previously no mergeinfo, inherited or explicit,
128 for (hi
= apr_hash_first(NULL
, mergeinfo
);
130 hi
= apr_hash_next(hi
))
133 apr_array_header_t
*rangelist
;
137 apr_hash_this(hi
, &key
, NULL
, &val
);
141 if (from
&& rangelist
)
144 SVN_ERR(svn_fs__sqlite_prepare(&stmt
, db
,
145 "INSERT INTO mergeinfo (revision, "
146 "mergedfrom, mergedto, mergedrevstart, "
147 "mergedrevend, inheritable) VALUES (?, "
148 "?, ?, ?, ?, ?);", pool
));
149 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 1, new_rev
));
150 SVN_ERR(svn_fs__sqlite_bind_text(stmt
, 2, from
));
151 SVN_ERR(svn_fs__sqlite_bind_text(stmt
, 3, path
));
153 if (remove_mergeinfo
)
155 /* Explicitly set "no mergeinfo" for PATH, which may've
156 previously had only inherited mergeinfo. */
157 #if APR_VERSION_AT_LEAST(1, 3, 0)
158 apr_array_clear(rangelist
);
160 /* Use of an iterpool would be overkill here. */
161 rangelist
= apr_array_make(pool
, 1, sizeof(&no_mergeinfo
));
163 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = &no_mergeinfo
;
166 for (i
= 0; i
< rangelist
->nelts
; i
++)
168 const svn_merge_range_t
*range
=
169 APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
170 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 4, range
->start
));
171 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 5, range
->end
));
172 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 6, range
->inheritable
));
173 SVN_ERR(svn_fs__sqlite_step_done(stmt
));
175 SVN_ERR(svn_fs__sqlite_reset(stmt
));
177 SVN_ERR(svn_fs__sqlite_finalize(stmt
));
181 SVN_ERR(svn_fs__sqlite_prepare(&stmt
, db
,
182 "INSERT INTO mergeinfo_changed (revision, "
183 "path) VALUES (?, ?);", pool
));
184 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 1, new_rev
));
185 SVN_ERR(svn_fs__sqlite_bind_text(stmt
, 2, path
));
187 SVN_ERR(svn_fs__sqlite_step_done(stmt
));
189 SVN_ERR(svn_fs__sqlite_finalize(stmt
));
195 /* Index the mergeinfo for each path in MERGEINFO_FOR_PATHS (a
196 mapping of const char * -> to svn_string_t *). */
198 index_txn_mergeinfo(sqlite3
*db
,
199 svn_revnum_t new_rev
,
200 apr_hash_t
*mergeinfo_for_paths
,
203 apr_hash_index_t
*hi
;
205 for (hi
= apr_hash_first(pool
, mergeinfo_for_paths
);
207 hi
= apr_hash_next(hi
))
212 apr_hash_this(hi
, &path
, NULL
, &mergeinfo
);
213 SVN_ERR(index_path_mergeinfo(new_rev
, db
, (const char *) path
,
214 (svn_string_t
*) mergeinfo
, pool
));
220 table_has_any_rows_with_rev(svn_boolean_t
*has_any
,
226 /* Note that we can't use the bind API for table names. (And if
227 we're sprintfing once, we might as well plug in the revision
228 while we're at it; it's safe.) */
229 const char *selection
= apr_psprintf(pool
,
230 "SELECT 1 from %s WHERE "
233 svn_fs__sqlite_stmt_t
*stmt
;
235 SVN_ERR(svn_fs__sqlite_prepare(&stmt
, db
, selection
, pool
));
236 SVN_ERR(svn_fs__sqlite_step(has_any
, stmt
));
237 SVN_ERR(svn_fs__sqlite_finalize(stmt
));
242 /* Remove any mergeinfo already stored at NEW_REV from DB. (This will
243 exist if a previous transaction failed between sqlite
244 commit-transaction and svn commit-transaction time, say.) If
245 AVOID_NOOP_DELETE is true, only run the delete commands if there's
246 definitely data there to delete.
249 clean_tables(sqlite3
*db
,
250 svn_revnum_t new_rev
,
251 svn_boolean_t avoid_noop_delete
,
254 const char *deletestring
;
256 if (avoid_noop_delete
)
258 svn_boolean_t has_any
;
259 SVN_ERR(table_has_any_rows_with_rev(&has_any
, db
, "mergeinfo",
263 SVN_ERR(table_has_any_rows_with_rev(&has_any
, db
, "mergeinfo_changed",
270 deletestring
= apr_psprintf(pool
,
271 "DELETE FROM mergeinfo_changed WHERE "
274 SVN_ERR(svn_fs__sqlite_exec(db
, deletestring
));
276 deletestring
= apr_psprintf(pool
,
277 "DELETE FROM mergeinfo WHERE revision = %ld;",
279 SVN_ERR(svn_fs__sqlite_exec(db
, deletestring
));
284 /* Clean the mergeinfo index for any previous failed commit with the
285 revision number as NEW_REV, and if the current transaction contains
286 mergeinfo, record it. */
288 svn_fs_mergeinfo__update_index(svn_fs_txn_t
*txn
, svn_revnum_t new_rev
,
289 apr_hash_t
*mergeinfo_for_paths
,
294 apr_pool_t
*subpool
= svn_pool_create(pool
);
296 SVN_ERR(svn_fs__sqlite_open(&db
, txn
->fs
->path
, subpool
));
297 err
= svn_fs__sqlite_exec(db
, "BEGIN TRANSACTION;");
300 /* Clean up old data. (If we're going to write to the DB anyway,
301 there's no reason to do extra checks to avoid no-op DELETEs.) */
302 err
= clean_tables(db
,
304 (mergeinfo_for_paths
== NULL
),
308 /* Record any mergeinfo from the current transaction. */
309 if (mergeinfo_for_paths
)
311 err
= index_txn_mergeinfo(db
, new_rev
, mergeinfo_for_paths
, subpool
);
315 /* This is moved here from FSFS's commit_txn, because we don't want to
316 * write the final current file if the sqlite commit fails.
317 * On the other hand, if we commit the transaction and end up failing
318 * the current file, we just end up with inaccessible data in the
319 * database, not a real problem. */
320 err
= svn_fs__sqlite_exec(db
, "COMMIT TRANSACTION;");
324 err
= svn_fs__sqlite_close(db
, err
);
325 svn_pool_destroy(subpool
);
329 /* Helper for get_mergeinfo_for_path() that retrieves mergeinfo for
330 PATH at the revision LASTMERGED_REV, returning it in the mergeinfo
331 hash *RESULT (with rangelist elements in ascending order). Perform
332 all allocations in POOL. */
334 parse_mergeinfo_from_db(sqlite3
*db
,
336 svn_revnum_t lastmerged_rev
,
340 svn_fs__sqlite_stmt_t
*stmt
;
341 svn_boolean_t got_row
;
343 SVN_ERR(svn_fs__sqlite_prepare(&stmt
, db
,
344 "SELECT mergedfrom, mergedrevstart, "
345 "mergedrevend, inheritable FROM mergeinfo "
346 "WHERE mergedto = ? AND revision = ? "
347 "ORDER BY mergedfrom, mergedrevstart;",
349 SVN_ERR(svn_fs__sqlite_bind_text(stmt
, 1, path
));
350 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 2, lastmerged_rev
));
351 SVN_ERR(svn_fs__sqlite_step(&got_row
, stmt
));
353 /* It is possible the mergeinfo changed because of a delete, and
354 that the mergeinfo is now gone. */
359 apr_array_header_t
*pathranges
;
360 const char *mergedfrom
;
361 svn_revnum_t startrev
;
363 svn_boolean_t inheritable
;
364 const char *lastmergedfrom
= NULL
;
366 *result
= apr_hash_make(pool
);
367 pathranges
= apr_array_make(pool
, 1, sizeof(svn_merge_range_t
*));
371 mergedfrom
= svn_fs__sqlite_column_text(stmt
, 0);
372 startrev
= svn_fs__sqlite_column_revnum(stmt
, 1);
373 endrev
= svn_fs__sqlite_column_revnum(stmt
, 2);
374 inheritable
= svn_fs__sqlite_column_boolean(stmt
, 3);
376 mergedfrom
= apr_pstrdup(pool
, mergedfrom
);
377 if (lastmergedfrom
&& strcmp(mergedfrom
, lastmergedfrom
) != 0)
379 /* This iteration over the result set starts a group of
380 mergeinfo with a different merge source. */
381 apr_hash_set(*result
, lastmergedfrom
, APR_HASH_KEY_STRING
,
383 pathranges
= apr_array_make(pool
, 1,
384 sizeof(svn_merge_range_t
*));
387 /* Filter out invalid revision numbers, which are assumed to
388 represent dummy records indicating that a merge source
389 has no mergeinfo for PATH. */
390 if (SVN_IS_VALID_REVNUM(startrev
) && SVN_IS_VALID_REVNUM(endrev
))
392 svn_merge_range_t
*range
= apr_pcalloc(pool
, sizeof(*range
));
393 range
->start
= startrev
;
395 range
->inheritable
= inheritable
;
396 APR_ARRAY_PUSH(pathranges
, svn_merge_range_t
*) = range
;
399 SVN_ERR(svn_fs__sqlite_step(&got_row
, stmt
));
400 lastmergedfrom
= mergedfrom
;
403 apr_hash_set(*result
, mergedfrom
, APR_HASH_KEY_STRING
, pathranges
);
406 SVN_ERR(svn_fs__sqlite_finalize(stmt
));
411 /* Helper for get_mergeinfo_for_path() that will append PATH_TO_APPEND
412 to each path that exists in the mergeinfo hash INPUT, and return a
413 new mergeinfo hash in *OUTPUT. Perform all allocations in POOL. */
415 append_component_to_paths(apr_hash_t
**output
,
417 const char *path_to_append
,
420 apr_hash_index_t
*hi
;
421 *output
= apr_hash_make(pool
);
423 for (hi
= apr_hash_first(pool
, input
); hi
; hi
= apr_hash_next(hi
))
429 apr_hash_this(hi
, &key
, NULL
, &val
);
430 newpath
= svn_path_join((const char *) key
, path_to_append
,
431 apr_hash_pool_get(*output
));
432 apr_hash_set(*output
, newpath
, APR_HASH_KEY_STRING
, val
);
439 /* Helper for svn_fs_mergeinfo__get_mergeinfo().
441 Update CACHE (and RESULT iff RESULT is non-null) with mergeinfo for
442 PATH at REV, retrieved from DB.
444 If INHERIT is svn_mergeinfo_explicit, then retrieve only explicit
445 mergeinfo on PATH. Else if it is svn_mergeinfo_nearest_ancestor,
446 then retrieve the mergeinfo for PATH's parent, recursively. Else
447 if it is svn_mergeinfo_inherited, then:
449 - If PATH had any explicit merges committed on or before REV,
450 retrieve the explicit mergeinfo for PATH;
452 - Else, retrieve mergeinfo for PATH's parent, recursively.
454 Perform all allocations in POOL. Due to the nature of APR pools,
455 and the recursion in this function, invoke this function using a
456 sub-pool. To preserve RESULT, use mergeinfo_hash_dup() before
457 clearing or destroying POOL.
460 get_mergeinfo_for_path(sqlite3
*db
,
465 svn_mergeinfo_inheritance_t inherit
,
468 apr_hash_t
*path_mergeinfo
;
469 svn_fs__sqlite_stmt_t
*stmt
;
470 svn_revnum_t lastmerged_rev
;
472 if (inherit
== svn_mergeinfo_nearest_ancestor
)
474 /* Looking for (possibly inherited) mergeinfo from PATH ancestors. */
479 /* Look up the explicit mergeinfo for PATH, starting with the
480 cache, then moving on to the SQLite index. */
481 path_mergeinfo
= apr_hash_get(cache
, path
, APR_HASH_KEY_STRING
);
484 /* We already had a mergeinfo lookup attempt cached. */
485 if (result
&& path_mergeinfo
!= NEGATIVE_CACHE_RESULT
)
486 apr_hash_set(result
, path
, APR_HASH_KEY_STRING
, path_mergeinfo
);
490 /* See if we have a mergeinfo_changed record for this path. If not,
491 then it can't have mergeinfo. */
492 SVN_ERR(svn_fs__sqlite_prepare(&stmt
, db
,
493 "SELECT MAX(revision) FROM "
494 "mergeinfo_changed WHERE path = ? AND "
495 "revision <= ?;", pool
));
497 SVN_ERR(svn_fs__sqlite_bind_text(stmt
, 1, path
));
498 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 2, rev
));
499 SVN_ERR(svn_fs__sqlite_step_row(stmt
));
501 lastmerged_rev
= svn_fs__sqlite_column_revnum(stmt
, 0);
502 SVN_ERR(svn_fs__sqlite_finalize(stmt
));
504 /* If we've got mergeinfo data, transform it from the db into a
505 mergeinfo hash. Either way, cache whether we found mergeinfo. */
506 if (lastmerged_rev
> 0)
508 SVN_ERR(parse_mergeinfo_from_db(db
, path
, lastmerged_rev
,
509 &path_mergeinfo
, pool
));
513 apr_hash_set(result
, path
, APR_HASH_KEY_STRING
,
515 apr_hash_set(cache
, path
, APR_HASH_KEY_STRING
, path_mergeinfo
);
518 apr_hash_set(cache
, path
, APR_HASH_KEY_STRING
,
519 NEGATIVE_CACHE_RESULT
);
522 } /* inherit != svn_mergeinfo_nearest_ancestor */
524 /* If we want only this path's parent's mergeinfo or this path has no
525 mergeinfo, and we are asked to, check PATH's nearest ancestor. */
526 if ((lastmerged_rev
== 0 && inherit
== svn_mergeinfo_inherited
)
527 || inherit
== svn_mergeinfo_nearest_ancestor
)
529 svn_stringbuf_t
*parentpath
;
531 /* It is possible we are already at the root. */
532 if (strcmp(path
, "") == 0)
535 parentpath
= svn_stringbuf_create(path
, pool
);
536 svn_path_remove_component(parentpath
);
538 /* The repository and the mergeinfo index internally refer to
539 the root path as "" rather than "/". */
540 if (strcmp(parentpath
->data
, "/") == 0)
541 svn_stringbuf_set(parentpath
, "");
543 SVN_ERR(get_mergeinfo_for_path(db
, parentpath
->data
, rev
,
544 NULL
, cache
, svn_mergeinfo_inherited
,
546 path_mergeinfo
= apr_hash_get(cache
, parentpath
->data
,
547 APR_HASH_KEY_STRING
);
548 if (path_mergeinfo
== NEGATIVE_CACHE_RESULT
)
549 apr_hash_set(cache
, path
, APR_HASH_KEY_STRING
, NULL
);
550 else if (path_mergeinfo
)
552 /* Now translate the result for our parent to our path. */
553 apr_hash_t
*translated_mergeinfo
;
554 const char *to_append
= &path
[parentpath
->len
+ 1];
556 /* But first remove all non-inheritable revision ranges. */
557 SVN_ERR(svn_mergeinfo_inheritable(&path_mergeinfo
, path_mergeinfo
,
558 NULL
, SVN_INVALID_REVNUM
,
559 SVN_INVALID_REVNUM
, pool
));
560 append_component_to_paths(&translated_mergeinfo
, path_mergeinfo
,
562 apr_hash_set(cache
, path
, APR_HASH_KEY_STRING
, translated_mergeinfo
);
564 apr_hash_set(result
, path
, APR_HASH_KEY_STRING
,
565 translated_mergeinfo
);
572 /* Get the mergeinfo for all of the children of PATH in REV. Return
573 the results in PATH_MERGEINFO. PATH_MERGEINFO should already be
574 created prior to calling this function, but it's value may change
575 as additional mergeinfos are added to it. Returned values are
576 allocated in POOL, while temporary values are allocated in a
579 get_mergeinfo_for_children(sqlite3
*db
,
582 apr_hash_t
*path_mergeinfo
,
583 svn_fs_mergeinfo_filter_func_t filter_func
,
584 void *filter_func_baton
,
587 svn_fs__sqlite_stmt_t
*stmt
;
588 apr_pool_t
*subpool
= svn_pool_create(pool
);
590 svn_boolean_t got_row
;
592 /* Get all paths under us. */
593 SVN_ERR(svn_fs__sqlite_prepare(&stmt
, db
,
594 "SELECT MAX(revision), path "
595 "FROM mergeinfo_changed "
596 "WHERE path LIKE ? AND revision <= ? "
597 "GROUP BY path;", pool
));
599 like_path
= apr_psprintf(subpool
, "%s/%%", path
);
601 SVN_ERR(svn_fs__sqlite_bind_text(stmt
, 1, like_path
));
602 SVN_ERR(svn_fs__sqlite_bind_int64(stmt
, 2, rev
));
604 SVN_ERR(svn_fs__sqlite_step(&got_row
, stmt
));
607 svn_revnum_t lastmerged_rev
;
608 const char *merged_path
;
610 svn_pool_clear(subpool
);
612 lastmerged_rev
= svn_fs__sqlite_column_revnum(stmt
, 0);
613 merged_path
= svn_fs__sqlite_column_text(stmt
, 1);
615 /* If we've got a merged revision, go get the mergeinfo from the db */
616 if (lastmerged_rev
> 0)
618 apr_hash_t
*db_mergeinfo
;
619 svn_boolean_t omit
= FALSE
;
621 SVN_ERR(parse_mergeinfo_from_db(db
, merged_path
, lastmerged_rev
,
622 &db_mergeinfo
, subpool
));
625 SVN_ERR(filter_func(filter_func_baton
, &omit
, merged_path
,
626 db_mergeinfo
, subpool
));
629 SVN_ERR(svn_mergeinfo_merge(path_mergeinfo
, db_mergeinfo
, pool
));
632 SVN_ERR(svn_fs__sqlite_step(&got_row
, stmt
));
635 SVN_ERR(svn_fs__sqlite_finalize(stmt
));
636 svn_pool_destroy(subpool
);
641 /* Return a deep copy of MERGEINFO_HASH (allocated in POOL), which is
642 a hash of paths -> mergeinfo hashes. */
644 mergeinfo_hash_dup(apr_hash_t
*mergeinfo_hash
, apr_pool_t
*pool
)
646 apr_hash_t
*new_hash
= apr_hash_make(pool
);
647 apr_hash_index_t
*hi
;
648 for (hi
= apr_hash_first(NULL
, mergeinfo_hash
); hi
; hi
= apr_hash_next(hi
))
654 apr_hash_this(hi
, &path
, &klen
, &mergeinfo
);
655 apr_hash_set(new_hash
, path
, klen
,
656 svn_mergeinfo_dup((apr_hash_t
*) mergeinfo
,
657 apr_hash_pool_get(new_hash
)));
662 /* Get the mergeinfo for a set of paths, returned in *MERGEINFO_HASH
663 as a hash of mergeinfo hashes keyed by each path. Returned values
664 are allocated in POOL, while temporary values are allocated in a
667 get_mergeinfo(sqlite3
*db
,
668 apr_hash_t
**mergeinfo_hash
,
670 const apr_array_header_t
*paths
,
671 svn_mergeinfo_inheritance_t inherit
,
674 apr_pool_t
*subpool
= svn_pool_create(pool
);
675 apr_hash_t
*result_hash
= apr_hash_make(subpool
);
676 apr_hash_t
*cache_hash
= apr_hash_make(subpool
);
679 for (i
= 0; i
< paths
->nelts
; i
++)
681 const char *path
= APR_ARRAY_IDX(paths
, i
, const char *);
682 SVN_ERR(get_mergeinfo_for_path(db
, path
, rev
, result_hash
, cache_hash
,
683 inherit
, apr_hash_pool_get(result_hash
)));
686 *mergeinfo_hash
= mergeinfo_hash_dup(result_hash
, pool
);
687 svn_pool_destroy(subpool
);
691 /* Get the mergeinfo for a set of paths. Returned values are
692 allocated in POOL, while temporary values are allocated in a
695 svn_fs_mergeinfo__get_mergeinfo(apr_hash_t
**mergeinfo
,
697 const apr_array_header_t
*paths
,
698 svn_mergeinfo_inheritance_t inherit
,
707 /* We require a revision root. */
708 if (root
->is_txn_root
)
709 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT
, NULL
, NULL
);
710 rev
= REV_ROOT_REV(root
);
712 subpool
= svn_pool_create(pool
);
714 /* Retrieve a path -> mergeinfo hash mapping. */
715 SVN_ERR(svn_fs__sqlite_open(&db
, root
->fs
->path
, subpool
));
716 err
= get_mergeinfo(db
, mergeinfo
, rev
, paths
, inherit
, pool
);
717 SVN_ERR(svn_fs__sqlite_close(db
, err
));
719 /* Convert each mergeinfo hash value into a textual representation. */
720 for (i
= 0; i
< paths
->nelts
; i
++)
722 svn_stringbuf_t
*mergeinfo_buf
;
723 apr_hash_t
*path_mergeinfo
;
724 const char *path
= APR_ARRAY_IDX(paths
, i
, const char *);
726 svn_pool_clear(subpool
);
728 path_mergeinfo
= apr_hash_get(*mergeinfo
, path
, APR_HASH_KEY_STRING
);
731 SVN_ERR(svn_mergeinfo_sort(path_mergeinfo
, subpool
));
732 SVN_ERR(svn_mergeinfo_to_stringbuf(&mergeinfo_buf
, path_mergeinfo
,
733 apr_hash_pool_get(*mergeinfo
)));
734 apr_hash_set(*mergeinfo
, path
, APR_HASH_KEY_STRING
,
735 mergeinfo_buf
->data
);
738 svn_pool_destroy(subpool
);
744 svn_fs_mergeinfo__get_mergeinfo_for_tree(apr_hash_t
**mergeinfo
,
746 const apr_array_header_t
*paths
,
747 svn_fs_mergeinfo_filter_func_t filter_func
,
748 void *filter_func_baton
,
756 /* We require a revision root. */
757 if (root
->is_txn_root
)
758 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT
, NULL
, NULL
);
759 rev
= REV_ROOT_REV(root
);
761 SVN_ERR(svn_fs__sqlite_open(&db
, root
->fs
->path
, pool
));
762 err
= get_mergeinfo(db
, mergeinfo
, rev
, paths
, svn_mergeinfo_inherited
,
766 for (i
= 0; i
< paths
->nelts
; i
++)
768 const char *path
= APR_ARRAY_IDX(paths
, i
, const char *);
769 apr_hash_t
*path_mergeinfo
= apr_hash_get(*mergeinfo
, path
,
770 APR_HASH_KEY_STRING
);
773 path_mergeinfo
= apr_hash_make(pool
);
779 err
= filter_func(filter_func_baton
, &omit
, path
, path_mergeinfo
,
785 apr_hash_set(*mergeinfo
, path
, APR_HASH_KEY_STRING
, NULL
);
790 err
= get_mergeinfo_for_children(db
, path
, rev
, path_mergeinfo
,
791 filter_func
, filter_func_baton
, pool
);
794 apr_hash_set(*mergeinfo
, path
, APR_HASH_KEY_STRING
, path_mergeinfo
);
798 return svn_fs__sqlite_close(db
, err
);