Bug Link:

Patch Link:

source code patch:

reproduction script:


‘svn move’ causes ‘REPOSITORY.root may not be null’ error.

Moving a directory which contains a moved file triggers an error.

To be specific, assume that there is a directory A and a file f in A.

First, we rename the file ‘A/f’  by ‘svn move’, and then rename the directory ‘A’, then it introduces the error message.

FYI, if we inverse two operations, then we can avoid the error.

How it is diagnosed:

reproduced by running  reproduction script:

*Result: Two error messages are printed.

svn: constraint failed

svn: REPOSITORY.root may not be NULL

Error message creation point: "REPOSITORY.root may not be NULL"

SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(...){

  ... ...

  for(i=0; i<nCol; i++){


    onError = pTab->aCol[i].notNull;


    switch( onError ){

      case OE_Abort:


      case OE_Rollback:

      case OE_Fail: {

        char *zMsg;

        j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull,

                                  SQLITE_CONSTRAINT, onError, regData+i);

/* pTab->zName == “REPOSITORY” and pTab->aCol[i].zName == “root” */

        zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL",

                              pTab->zName, pTab->aCol[i].zName);

        sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);




call stack:

#0  sqlite3GenerateConstraintChecks (...) at /.../sqlite3.c:83459

/* i guess sqlite3 tried to insert some data but it failed */

#1  sqlite3Insert (..., onError=99) at /.../sqlite3.c:83240


#8  svn_sqlite__prepare (..., text=0x2aaaaadcaaf0 "insert into repository (root, uuid) values (?1, ?2); ") at subversion/libsvn_subr/sqlite.c:162

#9  svn_sqlite__get_statement (...) at subversion/libsvn_subr/sqlite.c:142

/* repos_root_url and repos_uuid equals 0x0. It looks strange. */

#10 create_repos_id (repos_root_url=0x0, repos_uuid=0x0, ...) at subversion/libsvn_wc/wc_db.c:602

#11 get_info_for_copy (local_abspath=0x6cddc0 "/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/A/f", ...) at subversion/libsvn_wc/wc_db.c:2854

/* need to move “A/f” to “B/f” */

#12 svn_wc__db_op_copy (src_abspath=0x6cddc0 "/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/A/f", dst_abspath=0x6cddf8 "/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/B/f", ...) at subversion/libsvn_wc/wc_db.c:2932

#23 svn_cl__move (...) at subversion/svn/move-cmd.c:92

#24 main (argc=4, argv=0x7fffffffe7e8) at subversion/svn/main.c:2312

When it tries to move the file ‘f’ in the directory ‘A’ to the directory ‘B’, ‘contraint failed’ message is printed. In ‘get_info_for_copy’ function, call ‘create_repos_id with the arguement ‘repos_root_url’ and ‘ropos_uuid’.

static svn_error_t *



  const char *repos_relpath, *repos_root_url, *repos_uuid;

  svn_revnum_t revision;

/* local_abspath “/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/A/f */

/* svn_wc__db_read_info seems to get the information of repository from DB */

  SVN_ERR(svn_wc__db_read_info(status, &revision, &repos_relpath,

                               &repos_root_url, &repos_uuid,

                               db, local_abspath, ...));

  if (*status == svn_wc__db_status_excluded)




  else if (*status != svn_wc__db_status_added)


      *copyfrom_relpath = repos_relpath;

      *copyfrom_rev = revision;

/* pass the information from ‘svn_wc__db_read_info’ */

/* In the abnormal case, copyfrom_id = 0, repos_root_url = 0x0, and repos_uuid = 0x0 */

/* these variables are not expected to be 0 */


                              repos_root_url, repos_uuid, ...));




#0  get_info_for_copy (local_abspath=0x6cddc0 "/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/A/f", ...) at subversion/libsvn_wc/wc_db.c:2809

#1  svn_wc__db_op_copy (src_abspath=0x6cddc0 "/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/A/f", dst_abspath=0x6cddf8 "/home/yyzhou/mmlee/LOG-project/svn/3673/alpha/B/f", ...) at subversion/libsvn_wc/wc_db.c:2932


#12 svn_cl__move (...) at subversion/svn/move-cmd.c:92

#13 main (argc=4, argv=0x7fffffffe7e8) at subversion/svn/main.c:2312

In ‘create_repos_id’ function, it returns the existing REPOS_ID value for a given REPOS_ROOT__URL/REPOS_UUID pair. If one does not exist, create a new one.

static svn_error_t *

create_repos_id(apr_int64_t *repos_id,

                const char *repos_root_url,

                const char *repos_uuid,



  svn_sqlite__stmt_t *get_stmt;

  svn_sqlite__stmt_t *insert_stmt;

  svn_boolean_t have_row;

  SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));

  SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));

  SVN_ERR(svn_sqlite__step(&have_row, get_stmt));



  SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,


  SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));

  return svn_error_return(svn_sqlite__insert(repos_id, insert_stmt));


In  ‘svn_error_return(svn_sqlite__insert(repos_id, insert_stmt))’ :

svn_error_t *

svn_sqlite__insert(apr_int64_t *row_id, svn_sqlite__stmt_t *stmt)


  svn_boolean_t got_row;

  SVN_ERR(svn_sqlite__step(&got_row, stmt));

  if (row_id)

    *row_id = sqlite3_last_insert_rowid(stmt->db->db3);

  return svn_error_return(svn_sqlite__reset(stmt));


Calls svn_sqlite__step :

Error message creation point: "contraint failed"

svn_error_t *

svn_sqlite__step(svn_boolean_t *got_row, svn_sqlite__stmt_t *stmt)


  int sqlite_result = sqlite3_step(stmt->s3stmt);

  if (sqlite_result != SQLITE_DONE && sqlite_result != SQLITE_ROW)


      svn_error_t *err1, *err2;

/* sqlite_result is abnormal. it returns ‘contraint failed’ error message */

      err1 = svn_error_create(SQLITE_ERROR_CODE(sqlite_result), NULL,


      err2 = svn_sqlite__reset(stmt);

      return svn_error_compose_create(err1, err2);


  *got_row = (sqlite_result == SQLITE_ROW);

  stmt->needs_reset = TRUE;

  return SVN_NO_ERROR;


Root Cause:


‘svn move’ leads to the change of sqlite3 database. The moved file information is not updated so that database access failed and ‘constraint failed’ message comes out.


The condition that repos_root_url == 0x0 or repos_uuid == 0x0 looks strange.

It should avoid this situation.

The patch is:

--- subversion/trunk/subversion/libsvn_wc/wc_db.c        2010/07/07 14:49:41        961396

+++ subversion/trunk/subversion/libsvn_wc/wc_db.c        2010/07/07 14:54:18        961397

@@ -2851,6 +2851,10 @@ get_info_for_copy(apr_int64_t *copyfrom_


       *copyfrom_relpath = repos_relpath;

       *copyfrom_rev = revision;

/* repos_root_url == 0x0 / repos_uuid == 0x0 is exceptional case. If so, try to get the appropriate value by calling svn_wc__db_scan_base_repos function */

+      if (!repos_root_url || !repos_uuid)

+        SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root_url, &repos_uuid,

+                                           db, local_abspath,

+                                           scratch_pool, scratch_pool));


                               repos_root_url, repos_uuid,

                               pdh->wcroot->sdb, scratch_pool));

Failure symptom category

wrong result

Is there any log message?


How can we automatically insert the log message?

5. log at failed safety check

In wc_db.c:3929

  if (repos_relpath || repos_root_url || repos_uuid)


      const char *base_relpath;

      /* ### unwrap this. we can optimize away the parse_local_abspath.  */

      SVN_ERR(svn_wc__db_scan_base_repos(&base_relpath, repos_root_url,

                                         repos_uuid, db, current_abspath,

                                         result_pool, scratch_pool));

      if (repos_relpath)

        *repos_relpath = svn_dirent_join(base_relpath, build_relpath,



We can learn this condition..