| | @@ -18,10 +18,11 @@ |
| 18 | 18 | ** This file contains an interface between the TH scripting language |
| 19 | 19 | ** (an independent project) and fossil. |
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "th_main.h" |
| 23 | +#include "sqlite3.h" |
| 23 | 24 | |
| 24 | 25 | /* |
| 25 | 26 | ** Global variable counting the number of outstanding calls to malloc() |
| 26 | 27 | ** made by the th1 implementation. This is used to catch memory leaks |
| 27 | 28 | ** in the interpreter. Obviously, it also means th1 is not threadsafe. |
| | @@ -575,10 +576,86 @@ |
| 575 | 576 | encode16(aRand, zOut, n); |
| 576 | 577 | Th_SetResult(interp, (const char *)zOut, -1); |
| 577 | 578 | return TH_OK; |
| 578 | 579 | } |
| 579 | 580 | |
| 581 | +/* |
| 582 | +** TH1 command: query SQL CODE |
| 583 | +** |
| 584 | +** Run the SQL query given by the SQL argument. For each row in the result |
| 585 | +** set, run CODE. |
| 586 | +** |
| 587 | +** In SQL, parameters such as $var are filled in using the value of variable |
| 588 | +** "var". Result values are stored in variables with the column name prior |
| 589 | +** to each invocation of CODE. |
| 590 | +*/ |
| 591 | +static int queryCmd( |
| 592 | + Th_Interp *interp, |
| 593 | + void *p, |
| 594 | + int argc, |
| 595 | + const char **argv, |
| 596 | + int *argl |
| 597 | +){ |
| 598 | + sqlite3_stmt *pStmt; |
| 599 | + int rc; |
| 600 | + const char *zSql; |
| 601 | + int nSql; |
| 602 | + const char *zTail; |
| 603 | + int n, i; |
| 604 | + int res = TH_OK; |
| 605 | + int nVar; |
| 606 | + |
| 607 | + if( argc!=3 ){ |
| 608 | + return Th_WrongNumArgs(interp, "query SQL CODE"); |
| 609 | + } |
| 610 | + if( g.db==0 ){ |
| 611 | + Th_ErrorMessage(interp, "database is not open", 0, 0); |
| 612 | + return TH_ERROR; |
| 613 | + } |
| 614 | + zSql = argv[1]; |
| 615 | + nSql = argl[1]; |
| 616 | + while( res==TH_OK && nSql>0 ){ |
| 617 | + rc = sqlite3_prepare_v2(g.db, argv[1], argl[1], &pStmt, &zTail); |
| 618 | + if( rc!=0 ){ |
| 619 | + Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1); |
| 620 | + return TH_ERROR; |
| 621 | + } |
| 622 | + n = (int)(zTail - zSql); |
| 623 | + zSql += n; |
| 624 | + nSql -= n; |
| 625 | + if( pStmt==0 ) continue; |
| 626 | + nVar = sqlite3_bind_parameter_count(pStmt); |
| 627 | + for(i=1; i<=nVar; i++){ |
| 628 | + const char *zVar = sqlite3_bind_parameter_name(pStmt, i); |
| 629 | + int szVar = zVar ? th_strlen(zVar) : 0; |
| 630 | + if( szVar>1 && zVar[0]=='$' |
| 631 | + && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){ |
| 632 | + int nVal; |
| 633 | + const char *zVal = Th_GetResult(interp, &nVal); |
| 634 | + sqlite3_bind_text(pStmt, i, zVal, nVal, SQLITE_TRANSIENT); |
| 635 | + } |
| 636 | + } |
| 637 | + while( res==TH_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 638 | + int nCol = sqlite3_column_count(pStmt); |
| 639 | + for(i=0; i<nCol; i++){ |
| 640 | + const char *zCol = sqlite3_column_name(pStmt, i); |
| 641 | + int szCol = th_strlen(zCol); |
| 642 | + const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 643 | + int szVal = sqlite3_column_bytes(pStmt, i); |
| 644 | + Th_SetVar(interp, zCol, szCol, zVal, szVal); |
| 645 | + } |
| 646 | + res = Th_Eval(interp, 0, argv[2], argl[2]); |
| 647 | + if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 648 | + } |
| 649 | + rc = sqlite3_finalize(pStmt); |
| 650 | + if( rc!=SQLITE_OK ){ |
| 651 | + Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1); |
| 652 | + return TH_ERROR; |
| 653 | + } |
| 654 | + } |
| 655 | + return res; |
| 656 | +} |
| 580 | 657 | |
| 581 | 658 | /* |
| 582 | 659 | ** Make sure the interpreter has been initialized. Initialize it if |
| 583 | 660 | ** it has not been already. |
| 584 | 661 | ** |
| | @@ -601,10 +678,11 @@ |
| 601 | 678 | {"hasfeature", hasfeatureCmd, 0}, |
| 602 | 679 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 603 | 680 | {"htmlize", htmlizeCmd, 0}, |
| 604 | 681 | {"linecount", linecntCmd, 0}, |
| 605 | 682 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 683 | + {"query", queryCmd, 0}, |
| 606 | 684 | {"randhex", randhexCmd, 0}, |
| 607 | 685 | {"repository", repositoryCmd, 0}, |
| 608 | 686 | {"stime", stimeCmd, 0}, |
| 609 | 687 | {"utime", utimeCmd, 0}, |
| 610 | 688 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 611 | 689 | |