SQLite

Check-in [ed406d31ff]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the "modeof=<filename>" URI parameter to os_unix.c - used to specify a file to copy permissions from when a new database is created. Also allow passing NULL as the second parameter to sqlite3rbu_vacuum().
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ed406d31ff54ee3de8db91690a966e5c561f8f94
User & Date: dan 2016-08-11 18:05:47.763
Context
2016-08-13
06:38
Fix a buffer overread in fts5. (check-in: fcfbee6c7d user: dan tags: trunk)
2016-08-12
16:21
Have wal file checkpoints exit early if the sqlite3_interrupt() API function is called. (check-in: 8a5f41c7b1 user: dan tags: interruptible-checkpoint)
2016-08-11
19:12
Merge version 3.14 plus some subsequent patches (including the page-cache performance patch) from trunk. (check-in: d9f8918c5b user: drh tags: begin-concurrent)
18:05
Add the "modeof=<filename>" URI parameter to os_unix.c - used to specify a file to copy permissions from when a new database is created. Also allow passing NULL as the second parameter to sqlite3rbu_vacuum(). (check-in: ed406d31ff user: dan tags: trunk)
09:55
Fix a 1 byte buffer overwrite in the "sqldiff --rbu" command. (check-in: ab83d7077d user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/rbu/rbuprogress.test.
356
357
358
359
360
361
362


363
364
365
366
367
368
369
      CREATE TABLE t1(a, b, c);
      CREATE INDEX t1c ON t1(c);
    }
    vtab { 
      CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
    }
  } {



    foreach {tn2 rbusql r1 r2} {
      1 {
        CREATE TABLE data0_t1(a, b, c, rbu_rowid, rbu_control);
        INSERT INTO data0_t1 VALUES(15, 15, 15, 4, 0);
        INSERT INTO data0_t1 VALUES(20, 20, 20, 5, 0);
        CREATE TABLE rbu_count(tbl, cnt);







>
>







356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
      CREATE TABLE t1(a, b, c);
      CREATE INDEX t1c ON t1(c);
    }
    vtab { 
      CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
    }
  } {

    if {$tn=="vtab"} { ifcapable !fts5 break }

    foreach {tn2 rbusql r1 r2} {
      1 {
        CREATE TABLE data0_t1(a, b, c, rbu_rowid, rbu_control);
        INSERT INTO data0_t1 VALUES(15, 15, 15, 4, 0);
        INSERT INTO data0_t1 VALUES(20, 20, 20, 5, 0);
        CREATE TABLE rbu_count(tbl, cnt);
Changes to ext/rbu/rbuvacuum2.test.
152
153
154
155
156
157
158
159
160



161






































162

      SELECT * FROM sqlite_master;
    } {
    table t1 t1 2 {CREATE TABLE t1(a, b, c)}
    view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1}
    trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
    }
  }

}










































finish_test








|
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
      SELECT * FROM sqlite_master;
    } {
    table t1 t1 2 {CREATE TABLE t1(a, b, c)}
    view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1}
    trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
    }
  }
}
  
#-------------------------------------------------------------------------
# Test that passing a NULL value as the second argument to 
# sqlite3rbu_vacuum() causes it to:
#
#   * Use <database>-vacuum as the state db, and
#   * Set the state db permissions to the same as those on the db file.
#
db close
if {$::tcl_platform(platform)=="unix"} {
  forcedelete test.db

  sqlite3 db test.db
  do_execsql_test 5.0 {
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
    INSERT INTO t1 VALUES(5, 6);
    INSERT INTO t1 VALUES(7, 8);
  }
  db close

  foreach {tn perm} {
    1 00755
    2 00666
    3 00644
    4 00444
  } {
    forcedelete test.db-vacuum

    do_test 5.$tn.1 {
      file attributes test.db -permissions $perm
      sqlite3rbu_vacuum rbu test.db
      rbu step
    } {SQLITE_OK}

    do_test 5.$tn.2 { file exists test.db-vacuum } 1
    do_test 5.$tn.3 { file attributes test.db-vacuum -permissions} $perm
    rbu close
  }
}


finish_test

Changes to ext/rbu/sqlite3rbu.c.
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345




2346
2347
2348
2349
2350
2351
2352


/*
** Open the database handle and attach the RBU database as "rbu". If an
** error occurs, leave an error code and message in the RBU handle.
*/
static void rbuOpenDatabase(sqlite3rbu *p){
  assert( p->rc==SQLITE_OK );
  assert( p->dbMain==0 && p->dbRbu==0 );
  assert( rbuIsVacuum(p) || p->zTarget!=0 );

  /* Open the RBU database */
  p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);

  if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
    sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);




  }

  /* If using separate RBU and state databases, attach the state database to
  ** the RBU db handle now.  */
  if( p->zState ){
    rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
    memcpy(p->zStateDb, "stat", 4);







<
|
|






>
>
>
>







2330
2331
2332
2333
2334
2335
2336

2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355


/*
** Open the database handle and attach the RBU database as "rbu". If an
** error occurs, leave an error code and message in the RBU handle.
*/
static void rbuOpenDatabase(sqlite3rbu *p){

  assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
  assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );

  /* Open the RBU database */
  p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);

  if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
    sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
    if( p->zState==0 ){
      const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
      p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
    }
  }

  /* If using separate RBU and state databases, attach the state database to
  ** the RBU db handle now.  */
  if( p->zState ){
    rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
    memcpy(p->zStateDb, "stat", 4);
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
  const char *zTarget, 
  const char *zRbu,
  const char *zState
){
  sqlite3rbu *p;
  size_t nTarget = zTarget ? strlen(zTarget) : 0;
  size_t nRbu = strlen(zRbu);
  size_t nState = zState ? strlen(zState) : 0;
  size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1;

  p = (sqlite3rbu*)sqlite3_malloc64(nByte);
  if( p ){
    RbuState *pState = 0;

    /* Create the custom VFS. */
    memset(p, 0, sizeof(sqlite3rbu));







<
|







3476
3477
3478
3479
3480
3481
3482

3483
3484
3485
3486
3487
3488
3489
3490
  const char *zTarget, 
  const char *zRbu,
  const char *zState
){
  sqlite3rbu *p;
  size_t nTarget = zTarget ? strlen(zTarget) : 0;
  size_t nRbu = strlen(zRbu);

  size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;

  p = (sqlite3rbu*)sqlite3_malloc64(nByte);
  if( p ){
    RbuState *pState = 0;

    /* Create the custom VFS. */
    memset(p, 0, sizeof(sqlite3rbu));
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
        memcpy(p->zTarget, zTarget, nTarget+1);
        pCsr += nTarget+1;
      }
      p->zRbu = pCsr;
      memcpy(p->zRbu, zRbu, nRbu+1);
      pCsr += nRbu+1;
      if( zState ){
        p->zState = pCsr;
        memcpy(p->zState, zState, nState+1);
      }
      rbuOpenDatabase(p);
    }

    if( p->rc==SQLITE_OK ){
      pState = rbuLoadState(p);
      assert( pState || p->rc!=SQLITE_OK );







|
<







3498
3499
3500
3501
3502
3503
3504
3505

3506
3507
3508
3509
3510
3511
3512
        memcpy(p->zTarget, zTarget, nTarget+1);
        pCsr += nTarget+1;
      }
      p->zRbu = pCsr;
      memcpy(p->zRbu, zRbu, nRbu+1);
      pCsr += nRbu+1;
      if( zState ){
        p->zState = rbuMPrintf(p, "%s", zState);

      }
      rbuOpenDatabase(p);
    }

    if( p->rc==SQLITE_OK ){
      pState = rbuLoadState(p);
      assert( pState || p->rc!=SQLITE_OK );
3606
3607
3608
3609
3610
3611
3612














3613
3614
3615
3616
3617
3618
3619
3620
3621

3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632

3633
3634
3635
3636
3637
3638
3639
    }

    rbuFreeState(pState);
  }

  return p;
}















/*
** Open and return a new RBU handle. 
*/
sqlite3rbu *sqlite3rbu_open(
  const char *zTarget, 
  const char *zRbu,
  const char *zState
){

  /* TODO: Check that zTarget and zRbu are non-NULL */
  return openRbuHandle(zTarget, zRbu, zState);
}

/*
** Open a handle to begin or resume an RBU VACUUM operation.
*/
sqlite3rbu *sqlite3rbu_vacuum(
  const char *zTarget, 
  const char *zState
){

  /* TODO: Check that both arguments are non-NULL */
  return openRbuHandle(0, zTarget, zState);
}

/*
** Return the database handle used by pRbu.
*/







>
>
>
>
>
>
>
>
>
>
>
>
>
>









>











>







3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
    }

    rbuFreeState(pState);
  }

  return p;
}

/*
** Allocate and return an RBU handle with all fields zeroed except for the
** error code, which is set to SQLITE_MISUSE.
*/
static sqlite3rbu *rbuMisuseError(void){
  sqlite3rbu *pRet;
  pRet = sqlite3_malloc64(sizeof(sqlite3rbu));
  if( pRet ){
    memset(pRet, 0, sizeof(sqlite3rbu));
    pRet->rc = SQLITE_MISUSE;
  }
  return pRet;
}

/*
** Open and return a new RBU handle. 
*/
sqlite3rbu *sqlite3rbu_open(
  const char *zTarget, 
  const char *zRbu,
  const char *zState
){
  if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
  /* TODO: Check that zTarget and zRbu are non-NULL */
  return openRbuHandle(zTarget, zRbu, zState);
}

/*
** Open a handle to begin or resume an RBU VACUUM operation.
*/
sqlite3rbu *sqlite3rbu_vacuum(
  const char *zTarget, 
  const char *zState
){
  if( zTarget==0 ){ return rbuMisuseError(); }
  /* TODO: Check that both arguments are non-NULL */
  return openRbuHandle(0, zTarget, zState);
}

/*
** Return the database handle used by pRbu.
*/
3703
3704
3705
3706
3707
3708
3709

3710
3711
3712
3713
3714
3715
3716
    rbuDeleteVfs(p);
    sqlite3_free(p->aBuf);
    sqlite3_free(p->aFrame);

    rbuEditErrmsg(p);
    rc = p->rc;
    *pzErrmsg = p->zErrmsg;

    sqlite3_free(p);
  }else{
    rc = SQLITE_NOMEM;
    *pzErrmsg = 0;
  }
  return rc;
}







>







3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
    rbuDeleteVfs(p);
    sqlite3_free(p->aBuf);
    sqlite3_free(p->aFrame);

    rbuEditErrmsg(p);
    rc = p->rc;
    *pzErrmsg = p->zErrmsg;
    sqlite3_free(p->zState);
    sqlite3_free(p);
  }else{
    rc = SQLITE_NOMEM;
    *pzErrmsg = 0;
  }
  return rc;
}
Changes to ext/rbu/sqlite3rbu.h.
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330






331
332
333
334
335
336
337
);

/*
** Open an RBU handle to perform an RBU vacuum on database file zTarget.
** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
** that it can be suspended and resumed like an RBU update.
**
** The second argument to this function, which may not be NULL, identifies 
** a database in which to store the state of the RBU vacuum operation if
** it is suspended. The first time sqlite3rbu_vacuum() is called, to start
** an RBU vacuum operation, the state database should either not exist or
** be empty (contain no tables). If an RBU vacuum is suspended by calling
** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
** returned SQLITE_DONE, the vacuum state is stored in the state database. 
** The vacuum can be resumed by calling this function to open a new RBU
** handle specifying the same target and state databases.






**
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
** the next call to sqlite3rbu_vacuum() opens a handle that starts a 
** new RBU vacuum operation.







|
|
|
|
|




>
>
>
>
>
>







315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
);

/*
** Open an RBU handle to perform an RBU vacuum on database file zTarget.
** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
** that it can be suspended and resumed like an RBU update.
**
** The second argument to this function identifies a database in which 
** to store the state of the RBU vacuum operation if it is suspended. The 
** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
** operation, the state database should either not exist or be empty
** (contain no tables). If an RBU vacuum is suspended by calling 
** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
** returned SQLITE_DONE, the vacuum state is stored in the state database. 
** The vacuum can be resumed by calling this function to open a new RBU
** handle specifying the same target and state databases.
**
** If the second argument passed to this function is NULL, then the
** name of the state database is "<database>-vacuum", where <database>
** is the name of the target database file. In this case, on UNIX, if the
** state database is not already present in the file-system, it is created
** with the same permissions as the target db is made.
**
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
** the next call to sqlite3rbu_vacuum() opens a handle that starts a 
** new RBU vacuum operation.
Changes to ext/rbu/test_rbu.c.
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  Tcl_Obj *CONST objv[]
){
  sqlite3rbu *pRbu = 0;
  const char *zCmd;
  const char *zTarget;
  const char *zStateDb = 0;

  if( objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB STATE-DB");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetString(objv[1]);
  zTarget = Tcl_GetString(objv[2]);
  zStateDb = Tcl_GetString(objv[3]);

  pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
  Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
  Tcl_SetObjResult(interp, objv[1]);
  return TCL_OK;
}








|
|




|







236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  Tcl_Obj *CONST objv[]
){
  sqlite3rbu *pRbu = 0;
  const char *zCmd;
  const char *zTarget;
  const char *zStateDb = 0;

  if( objc!=3 && objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetString(objv[1]);
  zTarget = Tcl_GetString(objv[2]);
  if( objc==4 ) zStateDb = Tcl_GetString(objv[3]);

  pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
  Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
  Tcl_SetObjResult(interp, objv[1]);
  return TCL_OK;
}

Changes to src/os_unix.c.
5524
5525
5526
5527
5528
5529
5530





















5531
5532
5533
5534
5535
5536
5537
      }
    }
    unixLeaveMutex();
  }
#endif    /* if !OS_VXWORKS */
  return pUnused;
}






















/*
** This function is called by unixOpen() to determine the unix permissions
** to create new files with. If no error occurs, then SQLITE_OK is returned
** and a value suitable for passing as the third argument to open(2) is
** written to *pMode. If an IO error occurs, an SQLite error code is 
** returned and the value of *pMode is not modified.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
      }
    }
    unixLeaveMutex();
  }
#endif    /* if !OS_VXWORKS */
  return pUnused;
}

/*
** Find the mode, uid and gid of file zFile. 
*/
static int getFileMode(
  const char *zFile,              /* File name */
  mode_t *pMode,                  /* OUT: Permissions of zFile */
  uid_t *pUid,                    /* OUT: uid of zFile. */
  gid_t *pGid                     /* OUT: gid of zFile. */
){
  struct stat sStat;              /* Output of stat() on database file */
  int rc = SQLITE_OK;
  if( 0==osStat(zFile, &sStat) ){
    *pMode = sStat.st_mode & 0777;
    *pUid = sStat.st_uid;
    *pGid = sStat.st_gid;
  }else{
    rc = SQLITE_IOERR_FSTAT;
  }
  return rc;
}

/*
** This function is called by unixOpen() to determine the unix permissions
** to create new files with. If no error occurs, then SQLITE_OK is returned
** and a value suitable for passing as the third argument to open(2) is
** written to *pMode. If an IO error occurs, an SQLite error code is 
** returned and the value of *pMode is not modified.
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
  int rc = SQLITE_OK;             /* Return Code */
  *pMode = 0;
  *pUid = 0;
  *pGid = 0;
  if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
    char zDb[MAX_PATHNAME+1];     /* Database file path */
    int nDb;                      /* Number of valid bytes in zDb */
    struct stat sStat;            /* Output of stat() on database file */

    /* zPath is a path to a WAL or journal file. The following block derives
    ** the path to the associated database file from zPath. This block handles
    ** the following naming conventions:
    **
    **   "<path to db>-journal"
    **   "<path to db>-wal"







<







5581
5582
5583
5584
5585
5586
5587

5588
5589
5590
5591
5592
5593
5594
  int rc = SQLITE_OK;             /* Return Code */
  *pMode = 0;
  *pUid = 0;
  *pGid = 0;
  if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
    char zDb[MAX_PATHNAME+1];     /* Database file path */
    int nDb;                      /* Number of valid bytes in zDb */


    /* zPath is a path to a WAL or journal file. The following block derives
    ** the path to the associated database file from zPath. This block handles
    ** the following naming conventions:
    **
    **   "<path to db>-journal"
    **   "<path to db>-wal"
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606









5607
5608
5609
5610
5611
5612
5613
      if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
#endif
      nDb--;
    }
    memcpy(zDb, zPath, nDb);
    zDb[nDb] = '\0';

    if( 0==osStat(zDb, &sStat) ){
      *pMode = sStat.st_mode & 0777;
      *pUid = sStat.st_uid;
      *pGid = sStat.st_gid;
    }else{
      rc = SQLITE_IOERR_FSTAT;
    }
  }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
    *pMode = 0600;









  }
  return rc;
}

/*
** Open the file zPath.
** 







|
<
<
<
<
<
<


>
>
>
>
>
>
>
>
>







5611
5612
5613
5614
5615
5616
5617
5618






5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
      if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
#endif
      nDb--;
    }
    memcpy(zDb, zPath, nDb);
    zDb[nDb] = '\0';

    rc = getFileMode(zDb, pMode, pUid, pGid);






  }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
    *pMode = 0600;
  }else if( flags & SQLITE_OPEN_URI ){
    /* If this is a main database file and the file was opened using a URI
    ** filename, check for the "modeof" parameter. If present, interpret
    ** its value as a filename and try to copy the mode, uid and gid from
    ** that file.  */
    const char *z = sqlite3_uri_parameter(zPath, "modeof");
    if( z ){
      rc = getFileMode(z, pMode, pUid, pGid);
    }
  }
  return rc;
}

/*
** Open the file zPath.
**