Use an application-provided SQLite function (SVN_PROPDEL) to purge a particular dav_cache property from all the nodes at or under a specified path. * subversion/libsvn_subr/sqlite.c (appfunc_svn_propdel): New app-definited SQLite function implementation. (svn_sqlite__open): Register the new appfunc_svn_propdel() function with the database context. * subversion/include/private/svn_wc_private.h, * subversion/libsvn_wc/props.c (svn_wc__node_remove_davcache_prop_recursive): New function. * subversion/libsvn_wc/wc-queries.sql (STMT_DELETE_NODE_DAVCACHE_PROP): New query for clearing one named dav_cache property. * subversion/libsvn_wc/wc_db.h, * subversion/libsvn_wc/wc_db.c (svn_wc__db_remove_prop_from_davcache_recursively): New function. * subversion/libsvn_client/ra.c (struct invalidate_wcprop_walk_baton, invalidate_wcprop_for_node): Remove as unused. (invalidate_wc_props): Use svn_wc__node_remove_davcache_prop_recursive() rather than crawling nodes. --This line, and those below, will be ignored-- Index: subversion/libsvn_subr/sqlite.c =================================================================== --- subversion/libsvn_subr/sqlite.c (revision 1082680) +++ subversion/libsvn_subr/sqlite.c (working copy) @@ -869,12 +869,72 @@ return APR_SUCCESS; } +/* An SQLite application-defined scalar function, for use with + sqlite3_create_function(). + + Allows SQL queries to use: + + svn_propdel(PROPERTY_NAME, PROPERTY_BLOB) + + Userdata is a SCRATCH_POOL, which this function clears after use. + Return a BLOB containing the skel-marshaled version of + PROPERTY_BLOB with its PROPERTY_NAME property removed. */ +static void +appfunc_svn_propdel(sqlite3_context *context, + int argc, + sqlite3_value **argv) +{ + svn_error_t *err; + int value_len; + const char *value, *propname; + apr_hash_t *props; + svn_skel_t *props_skel; + svn_stringbuf_t *props_buf; + apr_pool_t *scratch_pool = (apr_pool_t *)sqlite3_user_data(context); + + /* Validate function parameters. */ + SVN_ERR_ASSERT_NO_RETURN(argc == 2); + SVN_ERR_ASSERT_NO_RETURN(sqlite3_value_type(argv[0]) == SQLITE_TEXT); + SVN_ERR_ASSERT_NO_RETURN((sqlite3_value_type(argv[1]) == SQLITE_BLOB) || + (sqlite3_value_type(argv[1]) == SQLITE_NULL)); + + /* No propeties? Nothing to do. */ + if (sqlite3_value_type(argv[1]) == SQLITE_NULL) + { + sqlite3_result_null(context); + return; + } + + /* Parse the properties skel from argv[1] ... */ + propname = (const char *)sqlite3_value_text(argv[0]); + value_len = sqlite3_value_bytes(argv[1]); + value = (const char *)sqlite3_value_blob(argv[1]); + props_skel = svn_skel__parse(value, value_len, scratch_pool); + err = svn_skel__parse_proplist(&props, props_skel, scratch_pool); + SVN_ERR_ASSERT_NO_RETURN(err == SVN_NO_ERROR); + + /* ... remove the specified property from the resulting hash ... */ + apr_hash_set(props, propname, APR_HASH_KEY_STRING, NULL); + + /* ... unparse the resulting property hash ... */ + err = svn_skel__unparse_proplist(&props_skel, props, scratch_pool); + SVN_ERR_ASSERT_NO_RETURN(err == SVN_NO_ERROR); + props_buf = svn_skel__unparse(props_skel, scratch_pool); + + /* ... and return the result. */ + sqlite3_result_blob(context, props_buf->data, props_buf->len, + SQLITE_TRANSIENT); + svn_pool_clear(scratch_pool); +} + svn_error_t * svn_sqlite__open(svn_sqlite__db_t **db, const char *path, svn_sqlite__mode_t mode, const char * const statements[], int latest_schema, const char * const *upgrade_sql, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + apr_pool_t *custom_func_scratch_pool = svn_pool_create(result_pool); + SVN_ERR(svn_atomic__init_once(&sqlite_init_state, init_sqlite, NULL, scratch_pool)); @@ -925,6 +985,11 @@ if (upgrade_sql != NULL) SVN_ERR(check_format(*db, latest_schema, upgrade_sql, scratch_pool)); + /* Register custom SVNPROPDEL SQLite function. */ + SQLITE_ERR(sqlite3_create_function((*db)->db3, "svn_propdel", 2, SQLITE_UTF8, + (void *)custom_func_scratch_pool, + &appfunc_svn_propdel, NULL, NULL), *db); + /* Store the provided statements. */ if (statements) { Index: subversion/include/private/svn_wc_private.h =================================================================== --- subversion/include/private/svn_wc_private.h (revision 1082680) +++ subversion/include/private/svn_wc_private.h (working copy) @@ -777,6 +777,13 @@ apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__node_remove_davcache_prop_recursive(svn_wc_context_t *wc_ctx, + const char *local_abspath, + const char *propname, + apr_pool_t *scratch_pool); + + /** * For use by entries.c and entries-dump.c to read old-format working copies. */ Index: subversion/libsvn_wc/props.c =================================================================== --- subversion/libsvn_wc/props.c (revision 1082680) +++ subversion/libsvn_wc/props.c (working copy) @@ -2729,3 +2729,15 @@ } return FALSE; } + +svn_error_t * +svn_wc__node_remove_davcache_prop_recursive(svn_wc_context_t *wc_ctx, + const char *local_abspath, + const char *propname, + apr_pool_t *scratch_pool) +{ + return svn_wc__db_remove_prop_from_davcache_recursively(wc_ctx->db, + local_abspath, + propname, + scratch_pool); +} Index: subversion/libsvn_wc/wc-queries.sql =================================================================== --- subversion/libsvn_wc/wc-queries.sql (revision 1082680) +++ subversion/libsvn_wc/wc-queries.sql (working copy) @@ -219,6 +219,10 @@ UPDATE actual_node SET properties = ?3 WHERE wc_id = ?1 AND local_relpath = ?2 +-- STMT_DELETE_NODE_DAVCACHE_PROP +UPDATE nodes SET dav_cache = SVN_PROPDEL(?4, dav_cache) +WHERE wc_id = ?1 AND (local_relpath = ?2 OR local_relpath LIKE ?3 ESCAPE '#'); + -- STMT_INSERT_ACTUAL_PROPS INSERT INTO actual_node (wc_id, local_relpath, parent_relpath, properties) VALUES (?1, ?2, ?3, ?4) Index: subversion/libsvn_wc/wc_db.c =================================================================== --- subversion/libsvn_wc/wc_db.c (revision 1082680) +++ subversion/libsvn_wc/wc_db.c (working copy) @@ -9865,3 +9865,29 @@ revision_status_txn, &rsb, scratch_pool)); } + + +svn_error_t * +svn_wc__db_remove_prop_from_davcache_recursively(svn_wc__db_t *db, + const char *local_abspath, + const char *propname, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + svn_sqlite__stmt_t *stmt; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, + db, local_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_NODE_DAVCACHE_PROP)); + SVN_ERR(svn_sqlite__bindf(stmt, "isss", + wcroot->wc_id, + local_relpath, + construct_like_arg(local_relpath, scratch_pool), + propname)); + return svn_error_return(svn_sqlite__step_done(stmt)); +} Index: subversion/libsvn_wc/wc_db.h =================================================================== --- subversion/libsvn_wc/wc_db.h (revision 1082680) +++ subversion/libsvn_wc/wc_db.h (working copy) @@ -2649,6 +2649,12 @@ apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__db_remove_prop_from_davcache_recursively(svn_wc__db_t *db, + const char *local_abspath, + const char *propname, + apr_pool_t *scratch_pool); + /* @} */ Index: subversion/libsvn_client/ra.c =================================================================== --- subversion/libsvn_client/ra.c (revision 1082680) +++ subversion/libsvn_client/ra.c (working copy) @@ -254,12 +254,9 @@ local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool); - return svn_error_return( - svn_wc__node_walk_children(cb->ctx->wc_ctx, local_abspath, FALSE, - invalidate_wcprop_for_node, &wb, - svn_depth_infinity, - cb->ctx->cancel_func, cb->ctx->cancel_baton, - pool)); + return svn_error_return(svn_wc__node_remove_davcache_prop_recursive( + cb->ctx->wc_ctx, local_abspath, + prop_name, pool)); }