| | @@ -17,12 +17,17 @@ |
| 17 | 17 | ** |
| 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 | | - |
| 23 | 22 | #include "th_main.h" |
| 23 | +#ifndef INTERFACE |
| 24 | +#include "blob.h" |
| 25 | +#endif |
| 26 | +#ifdef TH_USE_SQLITE |
| 27 | +#include "sqlite3.h" |
| 28 | +#endif |
| 24 | 29 | |
| 25 | 30 | /*#include "th_main.h"*/ |
| 26 | 31 | /* |
| 27 | 32 | ** Global variable counting the number of outstanding calls to malloc() |
| 28 | 33 | ** made by the th1 implementation. This is used to catch memory leaks |
| | @@ -537,16 +542,18 @@ |
| 537 | 542 | Th_SetResult(interp, g.zRepositoryName, -1); |
| 538 | 543 | return TH_OK; |
| 539 | 544 | } |
| 540 | 545 | |
| 541 | 546 | |
| 542 | | -extern const char *find_option(const char *zLong, const char *zShort, int hasArg); |
| 543 | | - |
| 547 | +#ifdef TH_USE_ARGV |
| 548 | +extern const char *find_option(const char *zLong, |
| 549 | + const char *zShort, |
| 550 | + int hasArg) /* from main.c */; |
| 544 | 551 | /* |
| 545 | 552 | ** TH Syntax: |
| 546 | 553 | ** |
| 547 | | -** argv_len |
| 554 | +** argv len |
| 548 | 555 | ** |
| 549 | 556 | ** Returns the number of command-line arguments. |
| 550 | 557 | */ |
| 551 | 558 | static int argvArgcCmd( |
| 552 | 559 | Th_Interp *interp, |
| | @@ -557,12 +564,12 @@ |
| 557 | 564 | ){ |
| 558 | 565 | Th_SetResultInt( interp, g.argc ); |
| 559 | 566 | return TH_OK; |
| 560 | 567 | } |
| 561 | 568 | |
| 562 | | -#define TH_USE_ARGV |
| 563 | | -#ifdef TH_USE_ARGV |
| 569 | + |
| 570 | + |
| 564 | 571 | /* |
| 565 | 572 | ** TH Syntax: |
| 566 | 573 | ** |
| 567 | 574 | ** argv_getat Index |
| 568 | 575 | ** |
| | @@ -833,13 +840,121 @@ |
| 833 | 840 | |
| 834 | 841 | #endif |
| 835 | 842 | /* end TH_USE_ARGV */ |
| 836 | 843 | |
| 837 | 844 | #ifdef TH_USE_SQLITE |
| 838 | | -#ifndef INTERFACE |
| 839 | | -#include "blob.h" |
| 840 | | -#endif |
| 845 | + |
| 846 | +/* |
| 847 | +** Adds the given prepared statement to the interpreter. Returns the |
| 848 | +** statements opaque identifier (a positive value). Ownerships of |
| 849 | +** pStmt is transfered to interp and it must be cleaned up by the |
| 850 | +** client by calling Th_FinalizeStmt(), passing it the value returned |
| 851 | +** by this function. |
| 852 | +** |
| 853 | +** If interp is destroyed before all statements are finalized, |
| 854 | +** it will finalize them but may emit a warning message. |
| 855 | +*/ |
| 856 | +int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt); |
| 857 | + |
| 858 | +/* |
| 859 | +** Expects stmtId to be a statement identifier returned by |
| 860 | +** Th_AddStmt(). On success, finalizes the statement and returns 0. |
| 861 | +** On error (statement not found) non-0 is returned. After this |
| 862 | +** call, some subsequent call to Th_AddStmt() may return the |
| 863 | +** same statement ID. |
| 864 | +*/ |
| 865 | +int Th_FinalizeStmt(Th_Interp *interp, int stmtId); |
| 866 | + |
| 867 | +/* |
| 868 | +** Fetches the statement with the given ID, as returned by |
| 869 | +** Th_AddStmt(). Returns NULL if stmtId does not refer (or no longer |
| 870 | +** refers) to a statement added via Th_AddStmt(). |
| 871 | +*/ |
| 872 | +sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId); |
| 873 | + |
| 874 | + |
| 875 | +struct Th_Sqlite { |
| 876 | + sqlite3_stmt ** aStmt; |
| 877 | + int nStmt; |
| 878 | +}; |
| 879 | +#define Th_Sqlite_KEY "Th_Sqlite" |
| 880 | +typedef struct Th_Sqlite Th_Sqlite; |
| 881 | + |
| 882 | +static Th_Sqlite * Th_sqlite_manager( Th_Interp * interp ){ |
| 883 | + void * p = Th_Data_Get( interp, Th_Sqlite_KEY ); |
| 884 | + return p ? (Th_Sqlite*)p : NULL; |
| 885 | +} |
| 886 | + |
| 887 | +int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){ |
| 888 | + Th_Sqlite * sq = Th_sqlite_manager(interp); |
| 889 | + int i, x; |
| 890 | + sqlite3_stmt * s; |
| 891 | + sqlite3_stmt ** list = sq->aStmt; |
| 892 | + for( i = 0; i < sq->nStmt; ++i ){ |
| 893 | + s = list[i]; |
| 894 | + if(NULL==s){ |
| 895 | + list[i] = pStmt; |
| 896 | + return i+1; |
| 897 | + } |
| 898 | + } |
| 899 | + x = (sq->nStmt + 1) * 2; |
| 900 | + list = (sqlite3_stmt**)fossil_realloc( list, sizeof(sqlite3_stmt*)*x ); |
| 901 | + for( i = sq->nStmt; i < x; ++i ){ |
| 902 | + list[i] = NULL; |
| 903 | + } |
| 904 | + list[sq->nStmt] = pStmt; |
| 905 | + x = sq->nStmt; |
| 906 | + sq->nStmt = i; |
| 907 | + sq->aStmt = list; |
| 908 | + return x + 1; |
| 909 | +} |
| 910 | + |
| 911 | + |
| 912 | +int Th_FinalizeStmt(Th_Interp *interp, int stmtId){ |
| 913 | + Th_Sqlite * sq = Th_sqlite_manager(interp); |
| 914 | + sqlite3_stmt * st; |
| 915 | + int rc = 0; |
| 916 | + assert( stmtId>0 && stmtId<=sq->nStmt ); |
| 917 | + st = sq->aStmt[stmtId-1]; |
| 918 | + if(NULL != st){ |
| 919 | + sq->aStmt[stmtId-1] = NULL; |
| 920 | + sqlite3_finalize(st); |
| 921 | + return 0; |
| 922 | + }else{ |
| 923 | + return 1; |
| 924 | + } |
| 925 | +} |
| 926 | + |
| 927 | +sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){ |
| 928 | + Th_Sqlite * sq = Th_sqlite_manager(interp); |
| 929 | + return ((stmtId<1) || (stmtId > sq->nStmt)) |
| 930 | + ? NULL |
| 931 | + : sq->aStmt[stmtId-1]; |
| 932 | +} |
| 933 | + |
| 934 | + |
| 935 | +static void finalizerSqlite( Th_Interp * interp, void * p ){ |
| 936 | + Th_Sqlite * sq = Th_sqlite_manager( interp ); |
| 937 | + int i; |
| 938 | + sqlite3_stmt * st = NULL; |
| 939 | + if(!sq) { |
| 940 | + fossil_warning("Got a finalizer call for a NULL Th_Sqlite."); |
| 941 | + return; |
| 942 | + } |
| 943 | + for( i = 0; i < sq->nStmt; ++i ){ |
| 944 | + st = sq->aStmt[i]; |
| 945 | + if(NULL != st){ |
| 946 | + fossil_warning("Auto-finalizing unfinalized query_prepare " |
| 947 | + "statement id #%d: %s", |
| 948 | + i+1, sqlite3_sql(st)); |
| 949 | + Th_FinalizeStmt( interp, i+1 ); |
| 950 | + } |
| 951 | + } |
| 952 | + Th_Free(interp, sq->aStmt); |
| 953 | + Th_Free(interp, sq); |
| 954 | +} |
| 955 | + |
| 841 | 956 | |
| 842 | 957 | /* |
| 843 | 958 | ** TH Syntax: |
| 844 | 959 | ** |
| 845 | 960 | ** query_prepare SQL |
| | @@ -1544,20 +1659,22 @@ |
| 1544 | 1659 | void *ctx, |
| 1545 | 1660 | int argc, |
| 1546 | 1661 | const char **argv, |
| 1547 | 1662 | int *argl |
| 1548 | 1663 | ){ |
| 1664 | + Th_Sqlite * sq = Th_sqlite_manager(interp); |
| 1549 | 1665 | static Th_SubCommand aSub[] = { |
| 1550 | 1666 | {"bind", queryBindTopLevelCmd}, |
| 1551 | 1667 | {"col", queryColTopLevelCmd}, |
| 1552 | 1668 | {"step", queryStepCmd}, |
| 1553 | 1669 | {"finalize", queryFinalizeCmd}, |
| 1554 | 1670 | {"prepare", queryPrepareCmd}, |
| 1555 | 1671 | {"strftime", queryStrftimeCmd}, |
| 1556 | 1672 | {0, 0} |
| 1557 | 1673 | }; |
| 1558 | | - Th_CallSubCommand2( interp, ctx, argc, argv, argl, aSub ); |
| 1674 | + assert( NULL != sq ); |
| 1675 | + Th_CallSubCommand2( interp, sq, argc, argv, argl, aSub ); |
| 1559 | 1676 | } |
| 1560 | 1677 | |
| 1561 | 1678 | |
| 1562 | 1679 | int th_register_sqlite(Th_Interp *interp){ |
| 1563 | 1680 | enum { BufLen = 100 }; |
| | @@ -1573,31 +1690,28 @@ |
| 1573 | 1690 | SET(SQLITE_NULL); |
| 1574 | 1691 | SET(SQLITE_OK); |
| 1575 | 1692 | SET(SQLITE_ROW); |
| 1576 | 1693 | SET(SQLITE_TEXT); |
| 1577 | 1694 | #undef SET |
| 1695 | + int rc = TH_OK; |
| 1578 | 1696 | static Th_Command_Reg aCommand[] = { |
| 1579 | 1697 | {"query", queryTopLevelCmd, 0}, |
| 1580 | | -#if 0 |
| 1581 | | - {"query_bind_int", queryBindIntCmd, 0}, |
| 1582 | | - {"query_bind_double", queryBindDoubleCmd,0}, |
| 1583 | | - {"query_bind_null", queryBindNullCmd, 0}, |
| 1584 | | - {"query_bind_string", queryBindStringCmd,0}, |
| 1585 | | - {"query_col_count", queryColCountCmd, 0}, |
| 1586 | | - {"query_col_double", queryColDoubleCmd, 0}, |
| 1587 | | - {"query_col_int", queryColIntCmd, 0}, |
| 1588 | | - {"query_col_is_null", queryColIsNullCmd, 0}, |
| 1589 | | - {"query_col_name", queryColNameCmd, 0}, |
| 1590 | | - {"query_col_string", queryColStringCmd, 0}, |
| 1591 | | - {"query_col_type", queryColTypeCmd, 0}, |
| 1592 | | - {"query_finalize", queryFinalizeCmd, 0}, |
| 1593 | | - {"query_prepare", queryPrepareCmd, 0}, |
| 1594 | | - {"query_step", queryStepCmd, 0}, |
| 1595 | | -#endif |
| 1596 | 1698 | {0, 0, 0} |
| 1597 | 1699 | }; |
| 1598 | | - Th_register_commands( interp, aCommand ); |
| 1700 | + rc = Th_register_commands( interp, aCommand ); |
| 1701 | + if(TH_OK==rc){ |
| 1702 | + Th_Sqlite * sq = Th_Malloc(interp, sizeof(Th_Sqlite)); |
| 1703 | + if(!sq){ |
| 1704 | + rc = TH_ERROR; |
| 1705 | + }else{ |
| 1706 | + assert( NULL == sq->aStmt ); |
| 1707 | + assert( 0 == sq->nStmt ); |
| 1708 | + Th_Data_Set( interp, Th_Sqlite_KEY, sq, finalizerSqlite ); |
| 1709 | + assert( sq == Th_sqlite_manager(interp) ); |
| 1710 | + } |
| 1711 | + } |
| 1712 | + return rc; |
| 1599 | 1713 | } |
| 1600 | 1714 | |
| 1601 | 1715 | #endif |
| 1602 | 1716 | /* end TH_USE_SQLITE */ |
| 1603 | 1717 | |
| | @@ -1820,12 +1934,12 @@ |
| 1820 | 1934 | i++; |
| 1821 | 1935 | } |
| 1822 | 1936 | } |
| 1823 | 1937 | if( rc==TH_ERROR ){ |
| 1824 | 1938 | sendText(g.interp, "<hr><p class=\"thmainError\">ERROR: ", -1, 0); |
| 1825 | | - zResult = (char*)Th_GetResult(g.interp, &n); |
| 1826 | | - sendText(g.interp, (char*)zResult, n, 1); |
| 1939 | + zResult = Th_GetResult(g.interp, &n); |
| 1940 | + sendText(g.interp, zResult, n, 1); |
| 1827 | 1941 | sendText(g.interp, "</p>", -1, 0); |
| 1828 | 1942 | }else{ |
| 1829 | 1943 | sendText(g.interp, z, i, 0); |
| 1830 | 1944 | } |
| 1831 | 1945 | return rc; |
| | @@ -1849,10 +1963,11 @@ |
| 1849 | 1963 | assert(0 && "usage() does not return"); |
| 1850 | 1964 | } |
| 1851 | 1965 | blob_zero(&in); |
| 1852 | 1966 | db_open_config(0); /* Needed for global "tcl" setting. */ |
| 1853 | 1967 | #ifdef TH_USE_SQLITE |
| 1854 | | - db_find_and_open_repository(OPEN_ANY_SCHEMA,0) /* for query_xxx API. */; |
| 1968 | + db_find_and_open_repository(OPEN_ANY_SCHEMA,0) |
| 1969 | + /* required for th1 query API. */; |
| 1855 | 1970 | #endif |
| 1856 | 1971 | blob_read_from_file(&in, g.argv[2]); |
| 1857 | 1972 | Th_Render(blob_str(&in), Th_Render_Flags_DEFAULT); |
| 1858 | 1973 | } |
| 1859 | 1974 | |