Fossil SCM
Initial implementation of the "bundle import" command. Also added the "db_debug()" function for use in debugging.
Commit
8abe20a137fa953f8c761dabd7e96f47ba65aa2a
Parent
a2f04d81735d49b…
2 files changed
+138
-8
+43
+138
-8
| --- src/bundle.c | ||
| +++ src/bundle.c | ||
| @@ -279,10 +279,143 @@ | ||
| 279 | 279 | " WHERE name IN ('project-code');" |
| 280 | 280 | ); |
| 281 | 281 | db_end_transaction(0); |
| 282 | 282 | } |
| 283 | 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 | + | |
| 284 | 417 | /* |
| 285 | 418 | ** COMMAND: bundle |
| 286 | 419 | ** |
| 287 | 420 | ** Usage: %fossil bundle SUBCOMMAND ARGS... |
| 288 | 421 | ** |
| @@ -295,11 +428,11 @@ | ||
| 295 | 428 | ** --branch BRANCH Package all check-ins on BRANCH. |
| 296 | 429 | ** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2. |
| 297 | 430 | ** --m COMMENT Add the comment to the bundle. |
| 298 | 431 | ** --explain Just explain what would have happened. |
| 299 | 432 | ** |
| 300 | -** fossil bundle import BUNDLE ?--publish? ?--explain? | |
| 433 | +** fossil bundle import BUNDLE ?--publish? | |
| 301 | 434 | ** |
| 302 | 435 | ** Import the bundle in file BUNDLE into the repository. The --publish |
| 303 | 436 | ** option makes the import public. The --explain option makes no changes |
| 304 | 437 | ** to the repository but rather explains what would have happened. |
| 305 | 438 | ** |
| @@ -310,17 +443,14 @@ | ||
| 310 | 443 | ** fossil bundle append BUNDLE FILE... |
| 311 | 444 | ** |
| 312 | 445 | ** Add files named on the command line to BUNDLE. This subcommand has |
| 313 | 446 | ** little practical use and is mostly intended for testing. |
| 314 | 447 | ** |
| 315 | -** fossil bundle extract BUNDLE ?UUID? ?FILE? | |
| 448 | +** fossil bundle cat BUNDLE UUID ?FILE? | |
| 316 | 449 | ** |
| 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. | |
| 322 | 452 | ** |
| 323 | 453 | ** SUMMARY: |
| 324 | 454 | ** fossil bundle export BUNDLEFILE ?OPTIONS? |
| 325 | 455 | ** --branch BRANCH |
| 326 | 456 | ** --from TAG1 --to TAG2 |
| @@ -339,11 +469,11 @@ | ||
| 339 | 469 | db_find_and_open_repository(0,0); |
| 340 | 470 | n = (int)strlen(zSubcmd); |
| 341 | 471 | if( strncmp(zSubcmd, "export", n)==0 ){ |
| 342 | 472 | bundle_export_cmd(); |
| 343 | 473 | }else if( strncmp(zSubcmd, "import", n)==0 ){ |
| 344 | - fossil_print("Not yet implemented...\n"); | |
| 474 | + bundle_import_cmd(); | |
| 345 | 475 | }else if( strncmp(zSubcmd, "ls", n)==0 ){ |
| 346 | 476 | bundle_ls_cmd(); |
| 347 | 477 | }else if( strncmp(zSubcmd, "append", n)==0 ){ |
| 348 | 478 | bundle_append_cmd(); |
| 349 | 479 | }else if( strncmp(zSubcmd, "extract", n)==0 ){ |
| 350 | 480 |
| --- 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 |
M
src/db.c
+43
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -424,10 +424,13 @@ | ||
| 424 | 424 | |
| 425 | 425 | /* |
| 426 | 426 | ** Extract text, integer, or blob values from the N-th column of the |
| 427 | 427 | ** current row. |
| 428 | 428 | */ |
| 429 | +int db_column_type(Stmt *pStmt, int N){ | |
| 430 | + return sqlite3_column_type(pStmt->pStmt, N); | |
| 431 | +} | |
| 429 | 432 | int db_column_bytes(Stmt *pStmt, int N){ |
| 430 | 433 | return sqlite3_column_bytes(pStmt->pStmt, N); |
| 431 | 434 | } |
| 432 | 435 | int db_column_int(Stmt *pStmt, int N){ |
| 433 | 436 | return sqlite3_column_int(pStmt->pStmt, N); |
| @@ -486,10 +489,50 @@ | ||
| 486 | 489 | while( (rc = db_step(pStmt))==SQLITE_ROW ){} |
| 487 | 490 | rc = db_reset(pStmt); |
| 488 | 491 | db_check_result(rc); |
| 489 | 492 | return rc; |
| 490 | 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 | +} | |
| 491 | 534 | |
| 492 | 535 | /* |
| 493 | 536 | ** Execute multiple SQL statements. |
| 494 | 537 | */ |
| 495 | 538 | int db_multi_exec(const char *zSql, ...){ |
| 496 | 539 |
| --- 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 |