Fossil SCM

Initial implementation of the "bundle import" command. Also added the "db_debug()" function for use in debugging.

drh 2014-11-26 01:40 UTC DBP-workflow
Commit 8abe20a137fa953f8c761dabd7e96f47ba65aa2a
2 files changed +138 -8 +43
+138 -8
--- src/bundle.c
+++ src/bundle.c
@@ -279,10 +279,143 @@
279279
" WHERE name IN ('project-code');"
280280
);
281281
db_end_transaction(0);
282282
}
283283
284
+
285
+/*
286
+** There is a TEMP table bix(blobid,delta) containing a set of purgeitems
287
+** that need to be transferred to the BLOB table. This routine does
288
+** all items that have srcid=iSrc. The pBasis blob holds the content
289
+** of the source document if iSrc>0.
290
+*/
291
+static void bundle_import_elements(int iSrc, Blob *pBasis, int isPriv){
292
+ Stmt q;
293
+ static Bag busy;
294
+ assert( pBasis!=0 || iSrc==0 );
295
+ if( iSrc>0 ){
296
+ if( bag_find(&busy, iSrc) ){
297
+ fossil_fatal("delta loop while uncompressing bundle artifacts");
298
+ }
299
+ bag_insert(&busy, iSrc);
300
+ }
301
+ db_prepare(&q,
302
+ "SELECT uuid, data, bblob.delta, bix.blobid"
303
+ " FROM bix, bblob"
304
+ " WHERE bix.delta=%d"
305
+ " AND bix.blobid=bblob.blobid;",
306
+ iSrc
307
+ );
308
+ while( db_step(&q)==SQLITE_ROW ){
309
+ Blob h1, h2, c1, c2;
310
+ int rid;
311
+ blob_zero(&h1);
312
+ db_column_blob(&q, 0, &h1);
313
+ blob_zero(&c1);
314
+ db_column_blob(&q, 1, &c1);
315
+ blob_uncompress(&c1, &c1);
316
+ blob_zero(&c2);
317
+ if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)==40 ){
318
+ Blob basis;
319
+ rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
320
+ db_column_text(&q,2));
321
+ content_get(rid, &basis);
322
+ blob_delta_apply(&basis, &c1, &c2);
323
+ blob_reset(&basis);
324
+ blob_reset(&c1);
325
+ }else if( pBasis ){
326
+ blob_delta_apply(pBasis, &c1, &c2);
327
+ blob_reset(&c1);
328
+ }else{
329
+ c2 = c1;
330
+ }
331
+ sha1sum_blob(&c2, &h2);
332
+ if( blob_compare(&h1, &h2)!=0 ){
333
+ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
334
+ blob_str(&h1), blob_str(&h2));
335
+ }
336
+ blob_reset(&h2);
337
+ rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv);
338
+ if( rid==0 ){
339
+ fossil_fatal("%s", g.zErrMsg);
340
+ }else{
341
+ if( !isPriv ) content_make_public(rid);
342
+ content_get(rid, &c1);
343
+ manifest_crosslink(rid, &c1, MC_NO_ERRORS);
344
+ }
345
+ bundle_import_elements(db_column_int(&q,3), &c2, isPriv);
346
+ blob_reset(&c2);
347
+ }
348
+ db_finalize(&q);
349
+ if( iSrc>0 ) bag_remove(&busy, iSrc);
350
+}
351
+
352
+
353
+/* fossil bundle import BUNDLE ?OPTIONS?
354
+**
355
+** Attempt to import the changes contained in BUNDLE. Make the change
356
+** private so that they do not sync.
357
+**
358
+** OPTIONS:
359
+** --force Import even if the project-code does not match
360
+** --publish Imported changes are not private
361
+*/
362
+static void bundle_import_cmd(void){
363
+ int forceFlag = find_option("force","f",0)!=0;
364
+ int isPriv = find_option("publish",0,0)==0;
365
+ char *zMissingDeltas;
366
+ Stmt q;
367
+ verify_all_options();
368
+ bundle_attach_file(g.argv[3], "b1", 1);
369
+
370
+ /* Only import a bundle that was generated from a repo with the same
371
+ ** project code, unless the --force flag is true */
372
+ if( !forceFlag ){
373
+ if( !db_exists("SELECT 1 FROM config, bconfig"
374
+ " WHERE config.name='project-code'"
375
+ " AND bconfig.bcname='project-code'"
376
+ " AND config.value=bconfig.bcvalue;")
377
+ ){
378
+ fossil_fatal("project-code in the bundle does not match the "
379
+ "repository project code. (override with --force).");
380
+ }
381
+ }
382
+
383
+ /* If the bundle contains deltas with a basis that is external to the
384
+ ** bundle and those external basis files are missing from the local
385
+ ** repo, then the delta encodings cannot be decoded and the bundle cannot
386
+ ** be extracted. */
387
+ zMissingDeltas = db_text(0,
388
+ "SELECT group_concat(substr(delta,1,10),' ')"
389
+ " FROM bblob"
390
+ " WHERE typeof(delta)='text' AND length(delta)=40"
391
+ " AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)");
392
+ if( zMissingDeltas && zMissingDeltas[0] ){
393
+ fossil_fatal("delta basis artifacts not found in repository: %s",
394
+ zMissingDeltas);
395
+ }
396
+
397
+ db_begin_transaction();
398
+ db_multi_exec(
399
+ "CREATE TEMP TABLE bix("
400
+ " blobid INTEGER PRIMARY KEY,"
401
+ " delta INTEGER"
402
+ ");"
403
+ "CREATE INDEX bixdelta ON bix(delta);"
404
+ "INSERT INTO bix(blobid,delta)"
405
+ " SELECT blobid,"
406
+ " CASE WHEN typeof(delta)=='integer'"
407
+ " THEN delta ELSE 0 END"
408
+ " FROM bblob"
409
+ " WHERE NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.uuid);"
410
+ );
411
+ manifest_crosslink_begin();
412
+ bundle_import_elements(0, 0, isPriv);
413
+ manifest_crosslink_end(0);
414
+ db_end_transaction(0);
415
+}
416
+
284417
/*
285418
** COMMAND: bundle
286419
**
287420
** Usage: %fossil bundle SUBCOMMAND ARGS...
288421
**
@@ -295,11 +428,11 @@
295428
** --branch BRANCH Package all check-ins on BRANCH.
296429
** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2.
297430
** --m COMMENT Add the comment to the bundle.
298431
** --explain Just explain what would have happened.
299432
**
300
-** fossil bundle import BUNDLE ?--publish? ?--explain?
433
+** fossil bundle import BUNDLE ?--publish?
301434
**
302435
** Import the bundle in file BUNDLE into the repository. The --publish
303436
** option makes the import public. The --explain option makes no changes
304437
** to the repository but rather explains what would have happened.
305438
**
@@ -310,17 +443,14 @@
310443
** fossil bundle append BUNDLE FILE...
311444
**
312445
** Add files named on the command line to BUNDLE. This subcommand has
313446
** little practical use and is mostly intended for testing.
314447
**
315
-** fossil bundle extract BUNDLE ?UUID? ?FILE?
448
+** fossil bundle cat BUNDLE UUID ?FILE?
316449
**
317
-** Extract artifacts from the bundle. With no arguments, all artifacts
318
-** are extracted into files named by the UUID. If a specific UUID is
319
-** specified, then only that one artifact is extracted. If a FILE
320
-** argument is also given, then the artifact is extracted into that
321
-** particular file.
450
+** Extract an artifact from the bundle. Write it into FILE, or onto
451
+** standard output if FILE is omitted.
322452
**
323453
** SUMMARY:
324454
** fossil bundle export BUNDLEFILE ?OPTIONS?
325455
** --branch BRANCH
326456
** --from TAG1 --to TAG2
@@ -339,11 +469,11 @@
339469
db_find_and_open_repository(0,0);
340470
n = (int)strlen(zSubcmd);
341471
if( strncmp(zSubcmd, "export", n)==0 ){
342472
bundle_export_cmd();
343473
}else if( strncmp(zSubcmd, "import", n)==0 ){
344
- fossil_print("Not yet implemented...\n");
474
+ bundle_import_cmd();
345475
}else if( strncmp(zSubcmd, "ls", n)==0 ){
346476
bundle_ls_cmd();
347477
}else if( strncmp(zSubcmd, "append", n)==0 ){
348478
bundle_append_cmd();
349479
}else if( strncmp(zSubcmd, "extract", n)==0 ){
350480
--- src/bundle.c
+++ src/bundle.c
@@ -279,10 +279,143 @@
279 " WHERE name IN ('project-code');"
280 );
281 db_end_transaction(0);
282 }
283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284 /*
285 ** COMMAND: bundle
286 **
287 ** Usage: %fossil bundle SUBCOMMAND ARGS...
288 **
@@ -295,11 +428,11 @@
295 ** --branch BRANCH Package all check-ins on BRANCH.
296 ** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2.
297 ** --m COMMENT Add the comment to the bundle.
298 ** --explain Just explain what would have happened.
299 **
300 ** fossil bundle import BUNDLE ?--publish? ?--explain?
301 **
302 ** Import the bundle in file BUNDLE into the repository. The --publish
303 ** option makes the import public. The --explain option makes no changes
304 ** to the repository but rather explains what would have happened.
305 **
@@ -310,17 +443,14 @@
310 ** fossil bundle append BUNDLE FILE...
311 **
312 ** Add files named on the command line to BUNDLE. This subcommand has
313 ** little practical use and is mostly intended for testing.
314 **
315 ** fossil bundle extract BUNDLE ?UUID? ?FILE?
316 **
317 ** Extract artifacts from the bundle. With no arguments, all artifacts
318 ** are extracted into files named by the UUID. If a specific UUID is
319 ** specified, then only that one artifact is extracted. If a FILE
320 ** argument is also given, then the artifact is extracted into that
321 ** particular file.
322 **
323 ** SUMMARY:
324 ** fossil bundle export BUNDLEFILE ?OPTIONS?
325 ** --branch BRANCH
326 ** --from TAG1 --to TAG2
@@ -339,11 +469,11 @@
339 db_find_and_open_repository(0,0);
340 n = (int)strlen(zSubcmd);
341 if( strncmp(zSubcmd, "export", n)==0 ){
342 bundle_export_cmd();
343 }else if( strncmp(zSubcmd, "import", n)==0 ){
344 fossil_print("Not yet implemented...\n");
345 }else if( strncmp(zSubcmd, "ls", n)==0 ){
346 bundle_ls_cmd();
347 }else if( strncmp(zSubcmd, "append", n)==0 ){
348 bundle_append_cmd();
349 }else if( strncmp(zSubcmd, "extract", n)==0 ){
350
--- src/bundle.c
+++ src/bundle.c
@@ -279,10 +279,143 @@
279 " WHERE name IN ('project-code');"
280 );
281 db_end_transaction(0);
282 }
283
284
285 /*
286 ** There is a TEMP table bix(blobid,delta) containing a set of purgeitems
287 ** that need to be transferred to the BLOB table. This routine does
288 ** all items that have srcid=iSrc. The pBasis blob holds the content
289 ** of the source document if iSrc>0.
290 */
291 static void bundle_import_elements(int iSrc, Blob *pBasis, int isPriv){
292 Stmt q;
293 static Bag busy;
294 assert( pBasis!=0 || iSrc==0 );
295 if( iSrc>0 ){
296 if( bag_find(&busy, iSrc) ){
297 fossil_fatal("delta loop while uncompressing bundle artifacts");
298 }
299 bag_insert(&busy, iSrc);
300 }
301 db_prepare(&q,
302 "SELECT uuid, data, bblob.delta, bix.blobid"
303 " FROM bix, bblob"
304 " WHERE bix.delta=%d"
305 " AND bix.blobid=bblob.blobid;",
306 iSrc
307 );
308 while( db_step(&q)==SQLITE_ROW ){
309 Blob h1, h2, c1, c2;
310 int rid;
311 blob_zero(&h1);
312 db_column_blob(&q, 0, &h1);
313 blob_zero(&c1);
314 db_column_blob(&q, 1, &c1);
315 blob_uncompress(&c1, &c1);
316 blob_zero(&c2);
317 if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)==40 ){
318 Blob basis;
319 rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
320 db_column_text(&q,2));
321 content_get(rid, &basis);
322 blob_delta_apply(&basis, &c1, &c2);
323 blob_reset(&basis);
324 blob_reset(&c1);
325 }else if( pBasis ){
326 blob_delta_apply(pBasis, &c1, &c2);
327 blob_reset(&c1);
328 }else{
329 c2 = c1;
330 }
331 sha1sum_blob(&c2, &h2);
332 if( blob_compare(&h1, &h2)!=0 ){
333 fossil_fatal("SHA1 hash mismatch - wanted %s, got %s",
334 blob_str(&h1), blob_str(&h2));
335 }
336 blob_reset(&h2);
337 rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv);
338 if( rid==0 ){
339 fossil_fatal("%s", g.zErrMsg);
340 }else{
341 if( !isPriv ) content_make_public(rid);
342 content_get(rid, &c1);
343 manifest_crosslink(rid, &c1, MC_NO_ERRORS);
344 }
345 bundle_import_elements(db_column_int(&q,3), &c2, isPriv);
346 blob_reset(&c2);
347 }
348 db_finalize(&q);
349 if( iSrc>0 ) bag_remove(&busy, iSrc);
350 }
351
352
353 /* fossil bundle import BUNDLE ?OPTIONS?
354 **
355 ** Attempt to import the changes contained in BUNDLE. Make the change
356 ** private so that they do not sync.
357 **
358 ** OPTIONS:
359 ** --force Import even if the project-code does not match
360 ** --publish Imported changes are not private
361 */
362 static void bundle_import_cmd(void){
363 int forceFlag = find_option("force","f",0)!=0;
364 int isPriv = find_option("publish",0,0)==0;
365 char *zMissingDeltas;
366 Stmt q;
367 verify_all_options();
368 bundle_attach_file(g.argv[3], "b1", 1);
369
370 /* Only import a bundle that was generated from a repo with the same
371 ** project code, unless the --force flag is true */
372 if( !forceFlag ){
373 if( !db_exists("SELECT 1 FROM config, bconfig"
374 " WHERE config.name='project-code'"
375 " AND bconfig.bcname='project-code'"
376 " AND config.value=bconfig.bcvalue;")
377 ){
378 fossil_fatal("project-code in the bundle does not match the "
379 "repository project code. (override with --force).");
380 }
381 }
382
383 /* If the bundle contains deltas with a basis that is external to the
384 ** bundle and those external basis files are missing from the local
385 ** repo, then the delta encodings cannot be decoded and the bundle cannot
386 ** be extracted. */
387 zMissingDeltas = db_text(0,
388 "SELECT group_concat(substr(delta,1,10),' ')"
389 " FROM bblob"
390 " WHERE typeof(delta)='text' AND length(delta)=40"
391 " AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)");
392 if( zMissingDeltas && zMissingDeltas[0] ){
393 fossil_fatal("delta basis artifacts not found in repository: %s",
394 zMissingDeltas);
395 }
396
397 db_begin_transaction();
398 db_multi_exec(
399 "CREATE TEMP TABLE bix("
400 " blobid INTEGER PRIMARY KEY,"
401 " delta INTEGER"
402 ");"
403 "CREATE INDEX bixdelta ON bix(delta);"
404 "INSERT INTO bix(blobid,delta)"
405 " SELECT blobid,"
406 " CASE WHEN typeof(delta)=='integer'"
407 " THEN delta ELSE 0 END"
408 " FROM bblob"
409 " WHERE NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.uuid);"
410 );
411 manifest_crosslink_begin();
412 bundle_import_elements(0, 0, isPriv);
413 manifest_crosslink_end(0);
414 db_end_transaction(0);
415 }
416
417 /*
418 ** COMMAND: bundle
419 **
420 ** Usage: %fossil bundle SUBCOMMAND ARGS...
421 **
@@ -295,11 +428,11 @@
428 ** --branch BRANCH Package all check-ins on BRANCH.
429 ** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2.
430 ** --m COMMENT Add the comment to the bundle.
431 ** --explain Just explain what would have happened.
432 **
433 ** fossil bundle import BUNDLE ?--publish?
434 **
435 ** Import the bundle in file BUNDLE into the repository. The --publish
436 ** option makes the import public. The --explain option makes no changes
437 ** to the repository but rather explains what would have happened.
438 **
@@ -310,17 +443,14 @@
443 ** fossil bundle append BUNDLE FILE...
444 **
445 ** Add files named on the command line to BUNDLE. This subcommand has
446 ** little practical use and is mostly intended for testing.
447 **
448 ** fossil bundle cat BUNDLE UUID ?FILE?
449 **
450 ** Extract an artifact from the bundle. Write it into FILE, or onto
451 ** standard output if FILE is omitted.
 
 
 
452 **
453 ** SUMMARY:
454 ** fossil bundle export BUNDLEFILE ?OPTIONS?
455 ** --branch BRANCH
456 ** --from TAG1 --to TAG2
@@ -339,11 +469,11 @@
469 db_find_and_open_repository(0,0);
470 n = (int)strlen(zSubcmd);
471 if( strncmp(zSubcmd, "export", n)==0 ){
472 bundle_export_cmd();
473 }else if( strncmp(zSubcmd, "import", n)==0 ){
474 bundle_import_cmd();
475 }else if( strncmp(zSubcmd, "ls", n)==0 ){
476 bundle_ls_cmd();
477 }else if( strncmp(zSubcmd, "append", n)==0 ){
478 bundle_append_cmd();
479 }else if( strncmp(zSubcmd, "extract", n)==0 ){
480
+43
--- src/db.c
+++ src/db.c
@@ -424,10 +424,13 @@
424424
425425
/*
426426
** Extract text, integer, or blob values from the N-th column of the
427427
** current row.
428428
*/
429
+int db_column_type(Stmt *pStmt, int N){
430
+ return sqlite3_column_type(pStmt->pStmt, N);
431
+}
429432
int db_column_bytes(Stmt *pStmt, int N){
430433
return sqlite3_column_bytes(pStmt->pStmt, N);
431434
}
432435
int db_column_int(Stmt *pStmt, int N){
433436
return sqlite3_column_int(pStmt->pStmt, N);
@@ -486,10 +489,50 @@
486489
while( (rc = db_step(pStmt))==SQLITE_ROW ){}
487490
rc = db_reset(pStmt);
488491
db_check_result(rc);
489492
return rc;
490493
}
494
+
495
+/*
496
+** Print the output of one or more SQL queries on standard output.
497
+** This routine is used for debugging purposes only.
498
+*/
499
+int db_debug(const char *zSql, ...){
500
+ Blob sql;
501
+ int rc = SQLITE_OK;
502
+ va_list ap;
503
+ const char *z, *zEnd;
504
+ sqlite3_stmt *pStmt;
505
+ blob_init(&sql, 0, 0);
506
+ va_start(ap, zSql);
507
+ blob_vappendf(&sql, zSql, ap);
508
+ va_end(ap);
509
+ z = blob_str(&sql);
510
+ while( rc==SQLITE_OK && z[0] ){
511
+ pStmt = 0;
512
+ rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
513
+ if( rc!=SQLITE_OK ) break;
514
+ if( pStmt ){
515
+ int nRow = 0;
516
+ db.nPrepare++;
517
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
518
+ int i, n;
519
+ if( nRow++ > 0 ) fossil_print("\n");
520
+ n = sqlite3_column_count(pStmt);
521
+ for(i=0; i<n; i++){
522
+ fossil_print("%s = %s\n", sqlite3_column_name(pStmt, i),
523
+ sqlite3_column_text(pStmt,i));
524
+ }
525
+ }
526
+ rc = sqlite3_finalize(pStmt);
527
+ if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
528
+ }
529
+ z = zEnd;
530
+ }
531
+ blob_reset(&sql);
532
+ return rc;
533
+}
491534
492535
/*
493536
** Execute multiple SQL statements.
494537
*/
495538
int db_multi_exec(const char *zSql, ...){
496539
--- src/db.c
+++ src/db.c
@@ -424,10 +424,13 @@
424
425 /*
426 ** Extract text, integer, or blob values from the N-th column of the
427 ** current row.
428 */
 
 
 
429 int db_column_bytes(Stmt *pStmt, int N){
430 return sqlite3_column_bytes(pStmt->pStmt, N);
431 }
432 int db_column_int(Stmt *pStmt, int N){
433 return sqlite3_column_int(pStmt->pStmt, N);
@@ -486,10 +489,50 @@
486 while( (rc = db_step(pStmt))==SQLITE_ROW ){}
487 rc = db_reset(pStmt);
488 db_check_result(rc);
489 return rc;
490 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
492 /*
493 ** Execute multiple SQL statements.
494 */
495 int db_multi_exec(const char *zSql, ...){
496
--- src/db.c
+++ src/db.c
@@ -424,10 +424,13 @@
424
425 /*
426 ** Extract text, integer, or blob values from the N-th column of the
427 ** current row.
428 */
429 int db_column_type(Stmt *pStmt, int N){
430 return sqlite3_column_type(pStmt->pStmt, N);
431 }
432 int db_column_bytes(Stmt *pStmt, int N){
433 return sqlite3_column_bytes(pStmt->pStmt, N);
434 }
435 int db_column_int(Stmt *pStmt, int N){
436 return sqlite3_column_int(pStmt->pStmt, N);
@@ -486,10 +489,50 @@
489 while( (rc = db_step(pStmt))==SQLITE_ROW ){}
490 rc = db_reset(pStmt);
491 db_check_result(rc);
492 return rc;
493 }
494
495 /*
496 ** Print the output of one or more SQL queries on standard output.
497 ** This routine is used for debugging purposes only.
498 */
499 int db_debug(const char *zSql, ...){
500 Blob sql;
501 int rc = SQLITE_OK;
502 va_list ap;
503 const char *z, *zEnd;
504 sqlite3_stmt *pStmt;
505 blob_init(&sql, 0, 0);
506 va_start(ap, zSql);
507 blob_vappendf(&sql, zSql, ap);
508 va_end(ap);
509 z = blob_str(&sql);
510 while( rc==SQLITE_OK && z[0] ){
511 pStmt = 0;
512 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
513 if( rc!=SQLITE_OK ) break;
514 if( pStmt ){
515 int nRow = 0;
516 db.nPrepare++;
517 while( sqlite3_step(pStmt)==SQLITE_ROW ){
518 int i, n;
519 if( nRow++ > 0 ) fossil_print("\n");
520 n = sqlite3_column_count(pStmt);
521 for(i=0; i<n; i++){
522 fossil_print("%s = %s\n", sqlite3_column_name(pStmt, i),
523 sqlite3_column_text(pStmt,i));
524 }
525 }
526 rc = sqlite3_finalize(pStmt);
527 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
528 }
529 z = zEnd;
530 }
531 blob_reset(&sql);
532 return rc;
533 }
534
535 /*
536 ** Execute multiple SQL statements.
537 */
538 int db_multi_exec(const char *zSql, ...){
539

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button