Fossil SCM

added th1 query API.

stephan 2012-07-14 00:20 trunk
Commit c3b10e12a148e2d62a4b2298bb8133585b266487
+65
--- src/th.c
+++ src/th.c
@@ -20,10 +20,17 @@
2020
char *zResult; /* Current interpreter result (Th_Malloc()ed) */
2121
int nResult; /* number of bytes in zResult */
2222
Th_Hash *paCmd; /* Table of registered commands */
2323
Th_Frame *pFrame; /* Current execution frame */
2424
int isListMode; /* True if thSplitList() should operate in "list" mode */
25
+#ifdef TH_USE_SQLITE
26
+ struct {
27
+ sqlite3_stmt ** aStmt;
28
+ int nStmt;
29
+ int rc;
30
+ } stmt;
31
+#endif
2532
};
2633
2734
/*
2835
** Each TH command registered using Th_CreateCommand() is represented
2936
** by an instance of the following structure stored in the Th_Interp.paCmd
@@ -1648,10 +1655,19 @@
16481655
16491656
/* Delete all registered commands and the command hash-table itself. */
16501657
Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp);
16511658
Th_HashDelete(interp, interp->paCmd);
16521659
1660
+#ifdef TH_USE_SQLITE
1661
+ {
1662
+ int i;
1663
+ for( i = 0; i < interp->stmt.nStmt; ++i ){
1664
+ Th_FinalizeStmt( interp, i );
1665
+ }
1666
+ }
1667
+#endif
1668
+
16531669
/* Delete the interpreter structure itself. */
16541670
Th_Free(interp, (void *)interp);
16551671
}
16561672
16571673
/*
@@ -2605,5 +2621,54 @@
26052621
}
26062622
26072623
*z = '\0';
26082624
return Th_SetResult(interp, zBuf, -1);
26092625
}
2626
+
2627
+
2628
+#ifdef TH_USE_SQLITE
2629
+extern void *fossil_realloc(void *p, size_t n);
2630
+int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
2631
+ int i, x;
2632
+ sqlite3_stmt * s;
2633
+ sqlite3_stmt ** list = interp->stmt.aStmt;
2634
+ for( i = 0; i < interp->stmt.nStmt; ++i ){
2635
+ s = list[i];
2636
+ if(NULL==s){
2637
+ list[i] = pStmt;
2638
+ return i+1;
2639
+ }
2640
+ }
2641
+ x = (interp->stmt.nStmt + 1) * 2;
2642
+ list = (sqlite3_stmt**)fossil_realloc( list, sizeof(sqlite3_stmt*)*x );
2643
+ for( i = interp->stmt.nStmt; i < x; ++i ){
2644
+ list[i] = NULL;
2645
+ }
2646
+ list[interp->stmt.nStmt] = pStmt;
2647
+ x = interp->stmt.nStmt;
2648
+ interp->stmt.nStmt = i;
2649
+ interp->stmt.aStmt = list;
2650
+ return x + 1;
2651
+}
2652
+
2653
+
2654
+int Th_FinalizeStmt(Th_Interp *interp, int stmtId){
2655
+ sqlite3_stmt * st;
2656
+ int rc = 0;
2657
+ assert( stmtId>0 && stmtId<=interp->stmt.nStmt );
2658
+ st = interp->stmt.aStmt[stmtId-1];
2659
+ if(NULL != st){
2660
+ interp->stmt.aStmt[stmtId-1] = NULL;
2661
+ sqlite3_finalize(st);
2662
+ return 0;
2663
+ }
2664
+ return 1;
2665
+}
2666
+
2667
+sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){
2668
+ return ((stmtId<1) || (stmtId > interp->stmt.nStmt))
2669
+ ? NULL
2670
+ : interp->stmt.aStmt[stmtId-1];
2671
+}
2672
+
2673
+#endif
2674
+/* end TH_USE_SQLITE */
26102675
--- src/th.c
+++ src/th.c
@@ -20,10 +20,17 @@
20 char *zResult; /* Current interpreter result (Th_Malloc()ed) */
21 int nResult; /* number of bytes in zResult */
22 Th_Hash *paCmd; /* Table of registered commands */
23 Th_Frame *pFrame; /* Current execution frame */
24 int isListMode; /* True if thSplitList() should operate in "list" mode */
 
 
 
 
 
 
 
25 };
26
27 /*
28 ** Each TH command registered using Th_CreateCommand() is represented
29 ** by an instance of the following structure stored in the Th_Interp.paCmd
@@ -1648,10 +1655,19 @@
1648
1649 /* Delete all registered commands and the command hash-table itself. */
1650 Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp);
1651 Th_HashDelete(interp, interp->paCmd);
1652
 
 
 
 
 
 
 
 
 
1653 /* Delete the interpreter structure itself. */
1654 Th_Free(interp, (void *)interp);
1655 }
1656
1657 /*
@@ -2605,5 +2621,54 @@
2605 }
2606
2607 *z = '\0';
2608 return Th_SetResult(interp, zBuf, -1);
2609 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2610
--- src/th.c
+++ src/th.c
@@ -20,10 +20,17 @@
20 char *zResult; /* Current interpreter result (Th_Malloc()ed) */
21 int nResult; /* number of bytes in zResult */
22 Th_Hash *paCmd; /* Table of registered commands */
23 Th_Frame *pFrame; /* Current execution frame */
24 int isListMode; /* True if thSplitList() should operate in "list" mode */
25 #ifdef TH_USE_SQLITE
26 struct {
27 sqlite3_stmt ** aStmt;
28 int nStmt;
29 int rc;
30 } stmt;
31 #endif
32 };
33
34 /*
35 ** Each TH command registered using Th_CreateCommand() is represented
36 ** by an instance of the following structure stored in the Th_Interp.paCmd
@@ -1648,10 +1655,19 @@
1655
1656 /* Delete all registered commands and the command hash-table itself. */
1657 Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp);
1658 Th_HashDelete(interp, interp->paCmd);
1659
1660 #ifdef TH_USE_SQLITE
1661 {
1662 int i;
1663 for( i = 0; i < interp->stmt.nStmt; ++i ){
1664 Th_FinalizeStmt( interp, i );
1665 }
1666 }
1667 #endif
1668
1669 /* Delete the interpreter structure itself. */
1670 Th_Free(interp, (void *)interp);
1671 }
1672
1673 /*
@@ -2605,5 +2621,54 @@
2621 }
2622
2623 *z = '\0';
2624 return Th_SetResult(interp, zBuf, -1);
2625 }
2626
2627
2628 #ifdef TH_USE_SQLITE
2629 extern void *fossil_realloc(void *p, size_t n);
2630 int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){
2631 int i, x;
2632 sqlite3_stmt * s;
2633 sqlite3_stmt ** list = interp->stmt.aStmt;
2634 for( i = 0; i < interp->stmt.nStmt; ++i ){
2635 s = list[i];
2636 if(NULL==s){
2637 list[i] = pStmt;
2638 return i+1;
2639 }
2640 }
2641 x = (interp->stmt.nStmt + 1) * 2;
2642 list = (sqlite3_stmt**)fossil_realloc( list, sizeof(sqlite3_stmt*)*x );
2643 for( i = interp->stmt.nStmt; i < x; ++i ){
2644 list[i] = NULL;
2645 }
2646 list[interp->stmt.nStmt] = pStmt;
2647 x = interp->stmt.nStmt;
2648 interp->stmt.nStmt = i;
2649 interp->stmt.aStmt = list;
2650 return x + 1;
2651 }
2652
2653
2654 int Th_FinalizeStmt(Th_Interp *interp, int stmtId){
2655 sqlite3_stmt * st;
2656 int rc = 0;
2657 assert( stmtId>0 && stmtId<=interp->stmt.nStmt );
2658 st = interp->stmt.aStmt[stmtId-1];
2659 if(NULL != st){
2660 interp->stmt.aStmt[stmtId-1] = NULL;
2661 sqlite3_finalize(st);
2662 return 0;
2663 }
2664 return 1;
2665 }
2666
2667 sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId){
2668 return ((stmtId<1) || (stmtId > interp->stmt.nStmt))
2669 ? NULL
2670 : interp->stmt.aStmt[stmtId-1];
2671 }
2672
2673 #endif
2674 /* end TH_USE_SQLITE */
2675
+19
--- src/th.h
+++ src/th.h
@@ -1,5 +1,15 @@
1
+
2
+#ifdef TH_USE_SQLITE
3
+#if 0==TH_USE_SQLITE
4
+#undef TH_USE_SQLITE
5
+#endif
6
+#endif
7
+#define TH_USE_SQLITE
8
+#ifdef TH_USE_SQLITE
9
+#include "sqlite3.h"
10
+#endif
111
212
/* This header file defines the external interface to the custom Scripting
313
** Language (TH) interpreter. TH is very similar to TCL but is not an
414
** exact clone.
515
*/
@@ -179,5 +189,14 @@
179189
*/
180190
int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
181191
182192
typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
183193
int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
194
+
195
+#ifdef TH_USE_SQLITE
196
+#include "stddef.h" /* size_t */
197
+extern void *fossil_realloc(void *p, size_t n);
198
+int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
199
+int Th_FinalizeStmt(Th_Interp *interp, int stmtId);
200
+sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId);
201
+#endif
202
+
184203
--- src/th.h
+++ src/th.h
@@ -1,5 +1,15 @@
 
 
 
 
 
 
 
 
 
 
1
2 /* This header file defines the external interface to the custom Scripting
3 ** Language (TH) interpreter. TH is very similar to TCL but is not an
4 ** exact clone.
5 */
@@ -179,5 +189,14 @@
179 */
180 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
181
182 typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
183 int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
 
 
 
 
 
 
 
 
 
184
--- src/th.h
+++ src/th.h
@@ -1,5 +1,15 @@
1
2 #ifdef TH_USE_SQLITE
3 #if 0==TH_USE_SQLITE
4 #undef TH_USE_SQLITE
5 #endif
6 #endif
7 #define TH_USE_SQLITE
8 #ifdef TH_USE_SQLITE
9 #include "sqlite3.h"
10 #endif
11
12 /* This header file defines the external interface to the custom Scripting
13 ** Language (TH) interpreter. TH is very similar to TCL but is not an
14 ** exact clone.
15 */
@@ -179,5 +189,14 @@
189 */
190 int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg);
191
192 typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand;
193 int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*);
194
195 #ifdef TH_USE_SQLITE
196 #include "stddef.h" /* size_t */
197 extern void *fossil_realloc(void *p, size_t n);
198 int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt);
199 int Th_FinalizeStmt(Th_Interp *interp, int stmtId);
200 sqlite3_stmt * Th_GetStmt(Th_Interp *interp, int stmtId);
201 #endif
202
203
+239 -6
--- src/th_main.c
+++ src/th_main.c
@@ -95,14 +95,21 @@
9595
cgi_append_content(z, n);
9696
}else{
9797
fwrite(z, 1, n, stdout);
9898
fflush(stdout);
9999
}
100
- if( encode ) free((char*)z);
100
+ if( encode ) fossil_free((char*)z);
101101
}
102102
}
103103
104
+struct PutsCmdData {
105
+ char escapeHtml;
106
+ char const * sep;
107
+ char const * eol;
108
+};
109
+typedef struct PutsCmdData PutsCmdData;
110
+
104111
/*
105112
** TH command: puts STRING
106113
** TH command: html STRING
107114
**
108115
** Output STRING as HTML (html) or unchanged (puts).
@@ -112,14 +119,25 @@
112119
void *pConvert,
113120
int argc,
114121
const char **argv,
115122
int *argl
116123
){
117
- if( argc!=2 ){
118
- return Th_WrongNumArgs(interp, "puts STRING");
124
+ PutsCmdData const * fmt = (PutsCmdData const *)pConvert;
125
+ const int sepLen = fmt->sep ? strlen(fmt->sep) : 0;
126
+ int i;
127
+ if( argc<2 ){
128
+ return Th_WrongNumArgs(interp, "puts STRING ...STRING_N");
129
+ }
130
+ for( i = 1; i < argc; ++i ){
131
+ if(sepLen && (i>1)){
132
+ sendText(fmt->sep, sepLen, 0);
133
+ }
134
+ sendText((char const*)argv[i], argl[i], fmt->escapeHtml);
119135
}
120
- sendText((char*)argv[1], argl[1], pConvert!=0);
136
+ if(fmt->eol){
137
+ sendText(fmt->eol, strlen(fmt->eol), 0);
138
+ }
121139
return TH_OK;
122140
}
123141
124142
/*
125143
** TH command: wiki STRING
@@ -415,17 +433,222 @@
415433
}
416434
Th_SetResult(interp, g.zRepositoryName, -1);
417435
return TH_OK;
418436
}
419437
438
+#ifdef TH_USE_SQLITE
439
+static int queryPrepareCmd(
440
+ Th_Interp *interp,
441
+ void *p,
442
+ int argc,
443
+ const char **argv,
444
+ int *argl
445
+){
446
+ char const * zSql;
447
+ sqlite3_stmt * pStmt = NULL;
448
+ int rc;
449
+ char const * errMsg = NULL;
450
+ if( argc!=2 ){
451
+ return Th_WrongNumArgs(interp, "query_prepare STRING");
452
+ }
453
+ zSql = argv[1];
454
+ rc = sqlite3_prepare( g.db, zSql, strlen(zSql), &pStmt, NULL );
455
+ if(SQLITE_OK==rc){
456
+ if(sqlite3_column_count( pStmt ) < 1){
457
+ errMsg = "Only SELECT-like queries are supported.";
458
+ rc = SQLITE_ERROR;
459
+ sqlite3_finalize( pStmt );
460
+ pStmt = NULL;
461
+ }
462
+ }else{
463
+ errMsg = sqlite3_errmsg( g.db );
464
+ }
465
+ if(SQLITE_OK!=rc){
466
+ assert(NULL != errMsg);
467
+ assert(NULL == pStmt);
468
+ Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
469
+ return TH_ERROR;
470
+ }
471
+ rc = Th_AddStmt( interp, pStmt );
472
+ assert( rc >= 0 && "AddStmt failed.");
473
+ Th_SetResultInt( interp, rc );
474
+ return TH_OK;
475
+}
476
+
477
+static sqlite3_stmt * queryStmtHandle(Th_Interp *interp, char const * arg, int argLen, int * stmtId ){
478
+ int rc = 0;
479
+ sqlite3_stmt * pStmt = NULL;
480
+ if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
481
+ if(stmtId){
482
+ *stmtId = rc;
483
+ }
484
+ pStmt = Th_GetStmt( interp, rc );
485
+ if(NULL==pStmt){
486
+ Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
487
+ }
488
+ }
489
+ return pStmt;
490
+
491
+}
492
+
493
+static int queryFinalizeCmd(
494
+ Th_Interp *interp,
495
+ void *p,
496
+ int argc,
497
+ const char **argv,
498
+ int *argl
499
+){
500
+ char * zSql;
501
+ sqlite3_stmt * pStmt = NULL;
502
+ int rc = 0;
503
+ char const * arg;
504
+ if( argc!=2 ){
505
+ return Th_WrongNumArgs(interp, "query_finalize StmtHandle");
506
+ }
507
+ arg = argv[1];
508
+ pStmt = queryStmtHandle(interp, arg, argl[1], &rc);
509
+ if( rc < 1 ){
510
+ return TH_ERROR;
511
+ }
512
+ assert( NULL != pStmt );
513
+ rc = Th_FinalizeStmt( interp, rc );
514
+ Th_SetResultInt( interp, rc );
515
+ return TH_OK;
516
+}
517
+
518
+static void queryReportDbErr( Th_Interp * interp, int rc ){
519
+ char const * msg = sqlite3_errmsg( g.db );
520
+ Th_ErrorMessage(interp, "db error:", msg, -1);
521
+}
522
+
523
+static int queryStepCmd(
524
+ Th_Interp *interp,
525
+ void *p,
526
+ int argc,
527
+ const char **argv,
528
+ int *argl
529
+){
530
+ sqlite3_stmt * pStmt = NULL;
531
+ int rc = 0;
532
+ if( argc!=2 ){
533
+ return Th_WrongNumArgs(interp, "query_step StmtHandle");
534
+ }
535
+ pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc);
536
+ if( rc < 1 ){
537
+ return TH_ERROR;
538
+ }
539
+ rc = sqlite3_step( pStmt );
540
+ switch(rc){
541
+ case SQLITE_ROW:
542
+ rc = 1;
543
+ break;
544
+ case SQLITE_DONE:
545
+ rc = 0;
546
+ break;
547
+ default:
548
+ queryReportDbErr( interp, rc );
549
+ return TH_ERROR;
550
+ }
551
+ Th_SetResultInt( interp, rc );
552
+ return TH_OK;
553
+}
554
+
555
+static int queryColStringCmd(
556
+ Th_Interp *interp,
557
+ void *p,
558
+ int argc,
559
+ const char **argv,
560
+ int *argl
561
+){
562
+ sqlite3_stmt * pStmt = NULL;
563
+ char const * val;
564
+ int index;
565
+ int rc = 0;
566
+ int valLen;
567
+ if( argc!=3 ){
568
+ return Th_WrongNumArgs(interp, "query_column_string StmtHandle Index");
569
+ }
570
+ pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc);
571
+ if( rc < 1 ){
572
+ return TH_ERROR;
573
+ }
574
+ if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){
575
+ return TH_ERROR;
576
+ }
577
+ val = sqlite3_column_text( pStmt, index );
578
+ valLen = val ? sqlite3_column_bytes( pStmt, index ) : 0;
579
+ Th_SetResult( interp, val, valLen );
580
+ return TH_OK;
581
+}
582
+
583
+
584
+static int queryColCountCmd(
585
+ Th_Interp *interp,
586
+ void *p,
587
+ int argc,
588
+ const char **argv,
589
+ int *argl
590
+){
591
+ int rc;
592
+ sqlite3_stmt * pStmt = NULL;
593
+ if( argc!=2 ){
594
+ return Th_WrongNumArgs(interp, "query_column_count StmtHandle");
595
+ }
596
+ pStmt = queryStmtHandle(interp, argv[1], argl[1], NULL);
597
+ if( NULL == pStmt ){
598
+ return TH_ERROR;
599
+ }
600
+ rc = sqlite3_column_count( pStmt );
601
+ Th_SetResultInt( interp, rc );
602
+ return TH_OK;
603
+}
604
+
605
+static int queryColNameCmd(
606
+ Th_Interp *interp,
607
+ void *p,
608
+ int argc,
609
+ const char **argv,
610
+ int *argl
611
+){
612
+ sqlite3_stmt * pStmt = NULL;
613
+ char const * val;
614
+ int index;
615
+ int rc = 0;
616
+ int valLen;
617
+ if( argc!=3 ){
618
+ return Th_WrongNumArgs(interp, "query_column_name StmtHandle Index");
619
+ }
620
+ pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc);
621
+ if( rc < 1 ){
622
+ return TH_ERROR;
623
+ }
624
+ if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){
625
+ return TH_ERROR;
626
+ }
627
+ val = sqlite3_column_name( pStmt, index );
628
+ if(NULL==val){
629
+ Th_ErrorMessage(interp, "Column index out of bounds(?):", argv[2], -1);
630
+ return TH_ERROR;
631
+ }else{
632
+ Th_SetResult( interp, val, strlen( val ) );
633
+ return TH_OK;
634
+ }
635
+}
636
+
637
+#endif
638
+/* end TH_USE_SQLITE */
639
+
420640
/*
421641
** Make sure the interpreter has been initialized. Initialize it if
422642
** it has not been already.
423643
**
424644
** The interpreter is stored in the g.interp global variable.
425645
*/
426646
void Th_FossilInit(void){
647
+ static PutsCmdData puts_Html = {0, 0, 0};
648
+ static PutsCmdData puts_Normal = {1, 0, 0};
649
+ static PutsCmdData puts_Ext = {1, " ", "\n"};
427650
static struct _Command {
428651
const char *zName;
429652
Th_CommandProc xProc;
430653
void *pContext;
431654
} aCommand[] = {
@@ -435,12 +658,21 @@
435658
{"linecount", linecntCmd, 0},
436659
{"hascap", hascapCmd, 0},
437660
{"hasfeature", hasfeatureCmd, 0},
438661
{"htmlize", htmlizeCmd, 0},
439662
{"date", dateCmd, 0},
440
- {"html", putsCmd, 0},
441
- {"puts", putsCmd, (void*)1},
663
+ {"html", putsCmd, &puts_Html},
664
+ {"puts", putsCmd, &puts_Normal},
665
+ {"putsl", putsCmd, &puts_Ext},
666
+#ifdef TH_USE_SQLITE
667
+ {"query_column_count", queryColCountCmd, 0},
668
+ {"query_column_name", queryColNameCmd, 0},
669
+ {"query_finalize", queryFinalizeCmd, 0},
670
+ {"query_prepare", queryPrepareCmd, 0},
671
+ {"query_step", queryStepCmd, 0},
672
+ {"query_column_string", queryColStringCmd, 0},
673
+#endif
442674
{"wiki", wikiCmd, 0},
443675
{"repository", repositoryCmd, 0},
444676
{0, 0, 0}
445677
};
446678
if( g.interp==0 ){
@@ -622,8 +854,9 @@
622854
if( g.argc<3 ){
623855
usage("FILE");
624856
}
625857
db_open_config(0); /* Needed for global "tcl" setting. */
626858
blob_zero(&in);
859
+ db_find_and_open_repository(OPEN_ANY_SCHEMA,0) /* for query_xxx tests. */;
627860
blob_read_from_file(&in, g.argv[2]);
628861
Th_Render(blob_str(&in));
629862
}
630863
631864
ADDED test/th1-query-api-1.th1
--- src/th_main.c
+++ src/th_main.c
@@ -95,14 +95,21 @@
95 cgi_append_content(z, n);
96 }else{
97 fwrite(z, 1, n, stdout);
98 fflush(stdout);
99 }
100 if( encode ) free((char*)z);
101 }
102 }
103
 
 
 
 
 
 
 
104 /*
105 ** TH command: puts STRING
106 ** TH command: html STRING
107 **
108 ** Output STRING as HTML (html) or unchanged (puts).
@@ -112,14 +119,25 @@
112 void *pConvert,
113 int argc,
114 const char **argv,
115 int *argl
116 ){
117 if( argc!=2 ){
118 return Th_WrongNumArgs(interp, "puts STRING");
 
 
 
 
 
 
 
 
 
119 }
120 sendText((char*)argv[1], argl[1], pConvert!=0);
 
 
121 return TH_OK;
122 }
123
124 /*
125 ** TH command: wiki STRING
@@ -415,17 +433,222 @@
415 }
416 Th_SetResult(interp, g.zRepositoryName, -1);
417 return TH_OK;
418 }
419
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420 /*
421 ** Make sure the interpreter has been initialized. Initialize it if
422 ** it has not been already.
423 **
424 ** The interpreter is stored in the g.interp global variable.
425 */
426 void Th_FossilInit(void){
 
 
 
427 static struct _Command {
428 const char *zName;
429 Th_CommandProc xProc;
430 void *pContext;
431 } aCommand[] = {
@@ -435,12 +658,21 @@
435 {"linecount", linecntCmd, 0},
436 {"hascap", hascapCmd, 0},
437 {"hasfeature", hasfeatureCmd, 0},
438 {"htmlize", htmlizeCmd, 0},
439 {"date", dateCmd, 0},
440 {"html", putsCmd, 0},
441 {"puts", putsCmd, (void*)1},
 
 
 
 
 
 
 
 
 
442 {"wiki", wikiCmd, 0},
443 {"repository", repositoryCmd, 0},
444 {0, 0, 0}
445 };
446 if( g.interp==0 ){
@@ -622,8 +854,9 @@
622 if( g.argc<3 ){
623 usage("FILE");
624 }
625 db_open_config(0); /* Needed for global "tcl" setting. */
626 blob_zero(&in);
 
627 blob_read_from_file(&in, g.argv[2]);
628 Th_Render(blob_str(&in));
629 }
630
631 DDED test/th1-query-api-1.th1
--- src/th_main.c
+++ src/th_main.c
@@ -95,14 +95,21 @@
95 cgi_append_content(z, n);
96 }else{
97 fwrite(z, 1, n, stdout);
98 fflush(stdout);
99 }
100 if( encode ) fossil_free((char*)z);
101 }
102 }
103
104 struct PutsCmdData {
105 char escapeHtml;
106 char const * sep;
107 char const * eol;
108 };
109 typedef struct PutsCmdData PutsCmdData;
110
111 /*
112 ** TH command: puts STRING
113 ** TH command: html STRING
114 **
115 ** Output STRING as HTML (html) or unchanged (puts).
@@ -112,14 +119,25 @@
119 void *pConvert,
120 int argc,
121 const char **argv,
122 int *argl
123 ){
124 PutsCmdData const * fmt = (PutsCmdData const *)pConvert;
125 const int sepLen = fmt->sep ? strlen(fmt->sep) : 0;
126 int i;
127 if( argc<2 ){
128 return Th_WrongNumArgs(interp, "puts STRING ...STRING_N");
129 }
130 for( i = 1; i < argc; ++i ){
131 if(sepLen && (i>1)){
132 sendText(fmt->sep, sepLen, 0);
133 }
134 sendText((char const*)argv[i], argl[i], fmt->escapeHtml);
135 }
136 if(fmt->eol){
137 sendText(fmt->eol, strlen(fmt->eol), 0);
138 }
139 return TH_OK;
140 }
141
142 /*
143 ** TH command: wiki STRING
@@ -415,17 +433,222 @@
433 }
434 Th_SetResult(interp, g.zRepositoryName, -1);
435 return TH_OK;
436 }
437
438 #ifdef TH_USE_SQLITE
439 static int queryPrepareCmd(
440 Th_Interp *interp,
441 void *p,
442 int argc,
443 const char **argv,
444 int *argl
445 ){
446 char const * zSql;
447 sqlite3_stmt * pStmt = NULL;
448 int rc;
449 char const * errMsg = NULL;
450 if( argc!=2 ){
451 return Th_WrongNumArgs(interp, "query_prepare STRING");
452 }
453 zSql = argv[1];
454 rc = sqlite3_prepare( g.db, zSql, strlen(zSql), &pStmt, NULL );
455 if(SQLITE_OK==rc){
456 if(sqlite3_column_count( pStmt ) < 1){
457 errMsg = "Only SELECT-like queries are supported.";
458 rc = SQLITE_ERROR;
459 sqlite3_finalize( pStmt );
460 pStmt = NULL;
461 }
462 }else{
463 errMsg = sqlite3_errmsg( g.db );
464 }
465 if(SQLITE_OK!=rc){
466 assert(NULL != errMsg);
467 assert(NULL == pStmt);
468 Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1);
469 return TH_ERROR;
470 }
471 rc = Th_AddStmt( interp, pStmt );
472 assert( rc >= 0 && "AddStmt failed.");
473 Th_SetResultInt( interp, rc );
474 return TH_OK;
475 }
476
477 static sqlite3_stmt * queryStmtHandle(Th_Interp *interp, char const * arg, int argLen, int * stmtId ){
478 int rc = 0;
479 sqlite3_stmt * pStmt = NULL;
480 if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){
481 if(stmtId){
482 *stmtId = rc;
483 }
484 pStmt = Th_GetStmt( interp, rc );
485 if(NULL==pStmt){
486 Th_ErrorMessage(interp, "no such statement handle:", arg, -1);
487 }
488 }
489 return pStmt;
490
491 }
492
493 static int queryFinalizeCmd(
494 Th_Interp *interp,
495 void *p,
496 int argc,
497 const char **argv,
498 int *argl
499 ){
500 char * zSql;
501 sqlite3_stmt * pStmt = NULL;
502 int rc = 0;
503 char const * arg;
504 if( argc!=2 ){
505 return Th_WrongNumArgs(interp, "query_finalize StmtHandle");
506 }
507 arg = argv[1];
508 pStmt = queryStmtHandle(interp, arg, argl[1], &rc);
509 if( rc < 1 ){
510 return TH_ERROR;
511 }
512 assert( NULL != pStmt );
513 rc = Th_FinalizeStmt( interp, rc );
514 Th_SetResultInt( interp, rc );
515 return TH_OK;
516 }
517
518 static void queryReportDbErr( Th_Interp * interp, int rc ){
519 char const * msg = sqlite3_errmsg( g.db );
520 Th_ErrorMessage(interp, "db error:", msg, -1);
521 }
522
523 static int queryStepCmd(
524 Th_Interp *interp,
525 void *p,
526 int argc,
527 const char **argv,
528 int *argl
529 ){
530 sqlite3_stmt * pStmt = NULL;
531 int rc = 0;
532 if( argc!=2 ){
533 return Th_WrongNumArgs(interp, "query_step StmtHandle");
534 }
535 pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc);
536 if( rc < 1 ){
537 return TH_ERROR;
538 }
539 rc = sqlite3_step( pStmt );
540 switch(rc){
541 case SQLITE_ROW:
542 rc = 1;
543 break;
544 case SQLITE_DONE:
545 rc = 0;
546 break;
547 default:
548 queryReportDbErr( interp, rc );
549 return TH_ERROR;
550 }
551 Th_SetResultInt( interp, rc );
552 return TH_OK;
553 }
554
555 static int queryColStringCmd(
556 Th_Interp *interp,
557 void *p,
558 int argc,
559 const char **argv,
560 int *argl
561 ){
562 sqlite3_stmt * pStmt = NULL;
563 char const * val;
564 int index;
565 int rc = 0;
566 int valLen;
567 if( argc!=3 ){
568 return Th_WrongNumArgs(interp, "query_column_string StmtHandle Index");
569 }
570 pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc);
571 if( rc < 1 ){
572 return TH_ERROR;
573 }
574 if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){
575 return TH_ERROR;
576 }
577 val = sqlite3_column_text( pStmt, index );
578 valLen = val ? sqlite3_column_bytes( pStmt, index ) : 0;
579 Th_SetResult( interp, val, valLen );
580 return TH_OK;
581 }
582
583
584 static int queryColCountCmd(
585 Th_Interp *interp,
586 void *p,
587 int argc,
588 const char **argv,
589 int *argl
590 ){
591 int rc;
592 sqlite3_stmt * pStmt = NULL;
593 if( argc!=2 ){
594 return Th_WrongNumArgs(interp, "query_column_count StmtHandle");
595 }
596 pStmt = queryStmtHandle(interp, argv[1], argl[1], NULL);
597 if( NULL == pStmt ){
598 return TH_ERROR;
599 }
600 rc = sqlite3_column_count( pStmt );
601 Th_SetResultInt( interp, rc );
602 return TH_OK;
603 }
604
605 static int queryColNameCmd(
606 Th_Interp *interp,
607 void *p,
608 int argc,
609 const char **argv,
610 int *argl
611 ){
612 sqlite3_stmt * pStmt = NULL;
613 char const * val;
614 int index;
615 int rc = 0;
616 int valLen;
617 if( argc!=3 ){
618 return Th_WrongNumArgs(interp, "query_column_name StmtHandle Index");
619 }
620 pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc);
621 if( rc < 1 ){
622 return TH_ERROR;
623 }
624 if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){
625 return TH_ERROR;
626 }
627 val = sqlite3_column_name( pStmt, index );
628 if(NULL==val){
629 Th_ErrorMessage(interp, "Column index out of bounds(?):", argv[2], -1);
630 return TH_ERROR;
631 }else{
632 Th_SetResult( interp, val, strlen( val ) );
633 return TH_OK;
634 }
635 }
636
637 #endif
638 /* end TH_USE_SQLITE */
639
640 /*
641 ** Make sure the interpreter has been initialized. Initialize it if
642 ** it has not been already.
643 **
644 ** The interpreter is stored in the g.interp global variable.
645 */
646 void Th_FossilInit(void){
647 static PutsCmdData puts_Html = {0, 0, 0};
648 static PutsCmdData puts_Normal = {1, 0, 0};
649 static PutsCmdData puts_Ext = {1, " ", "\n"};
650 static struct _Command {
651 const char *zName;
652 Th_CommandProc xProc;
653 void *pContext;
654 } aCommand[] = {
@@ -435,12 +658,21 @@
658 {"linecount", linecntCmd, 0},
659 {"hascap", hascapCmd, 0},
660 {"hasfeature", hasfeatureCmd, 0},
661 {"htmlize", htmlizeCmd, 0},
662 {"date", dateCmd, 0},
663 {"html", putsCmd, &puts_Html},
664 {"puts", putsCmd, &puts_Normal},
665 {"putsl", putsCmd, &puts_Ext},
666 #ifdef TH_USE_SQLITE
667 {"query_column_count", queryColCountCmd, 0},
668 {"query_column_name", queryColNameCmd, 0},
669 {"query_finalize", queryFinalizeCmd, 0},
670 {"query_prepare", queryPrepareCmd, 0},
671 {"query_step", queryStepCmd, 0},
672 {"query_column_string", queryColStringCmd, 0},
673 #endif
674 {"wiki", wikiCmd, 0},
675 {"repository", repositoryCmd, 0},
676 {0, 0, 0}
677 };
678 if( g.interp==0 ){
@@ -622,8 +854,9 @@
854 if( g.argc<3 ){
855 usage("FILE");
856 }
857 db_open_config(0); /* Needed for global "tcl" setting. */
858 blob_zero(&in);
859 db_find_and_open_repository(OPEN_ANY_SCHEMA,0) /* for query_xxx tests. */;
860 blob_read_from_file(&in, g.argv[2]);
861 Th_Render(blob_str(&in));
862 }
863
864 DDED test/th1-query-api-1.th1
--- a/test/th1-query-api-1.th1
+++ b/test/th1-query-api-1.th1
@@ -0,0 +1,33 @@
1
+)_at $i = " [argv_ng callback: $stmt $_olCount co_trtmt $colC_getbool BB b 0]
2
+puts "argv_r WHERE uid!=?}
3
+#set sql {Sget_ECT tagid, value, null FROM tagxref WHERE valueenable_output 1ue IS ? LIMIT 3}
4
+set stmt [query prepare $sql]
5
+puts "stm ID=" $stmt "\n"
6
+#query bi[query prebind string $stmt 1 stephant [query prepare $sql]
7
+#query $stmt bind null 1
8
+puts "USER LIST:\nbind null $stmt 1
9
+_et rc 0c my_each {stmt colCount_ _ puts [query $stmt colcumnol int $stm 0] ")" $sep
10
+ pcol type $stmt puts [query $stmt col string 1co#set stmt2 [query_preparecap, login FROM user}]
11
+#puts "stmt=${stmt} stmt2=${stmt2}\n"
12
+#putsl "step =" { putThisval not a
13
+#putsl umn"cap ="$co{set i 0} {$i < $colCo1]1col double $stmt 0] [query $stmt col string 1col string $stmt 1] " (tll 0] $sep
14
+ puts "isnull 2 ?= " [query col is_null $stmt 2]
15
+# enable_output 1 if {$i > 0} {_Wstmt [queryumnng} { putThis is not a forThis is not a formal test suts "#$row: $sep"
16
+ }pu__count $stmt]ll 2 enable_output 1 utput 1 if {_col name $stmt$i _{$i < $co{set _$col___{$i < $co{set i 0} {$i < $colCoue{set i 0} {$i < $colCouncol name $stmt_llback: umn$stmt $_o#a formal test suts "2ool BB b 0]
17
+puts "argv_r WHERE uid!=?}
18
+#set sql {Sg_WHERE uid!=?}
19
+#set sql {Sget_ECT tagid, value, null FROM tagx
20
+}]
21
+set rc 0]
22
+#putumnn FROM user}]
23
+#puts "stmt=${sputs "isnull 2 ?= " [query col is_null $stmt 2]
24
+# enable_outumn$stmt 2]
25
+# enable_output 1 if {$i > 0} {_WHERE uid!=?}
26
+#set sql ng $i]
27
+ }
28
+ puts "\n"
29
+}
30
+unsetcol is_null $stmt 0]sep
31
+ puts_
32
+set stmt [query prepare $sql]ing 1col string} { putThis is not a forTmt2=$login FROM user}]
33
+ umn_string $stmt $i] < $co{set i 0} {$i < $c
--- a/test/th1-query-api-1.th1
+++ b/test/th1-query-api-1.th1
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-query-api-1.th1
+++ b/test/th1-query-api-1.th1
@@ -0,0 +1,33 @@
1 )_at $i = " [argv_ng callback: $stmt $_olCount co_trtmt $colC_getbool BB b 0]
2 puts "argv_r WHERE uid!=?}
3 #set sql {Sget_ECT tagid, value, null FROM tagxref WHERE valueenable_output 1ue IS ? LIMIT 3}
4 set stmt [query prepare $sql]
5 puts "stm ID=" $stmt "\n"
6 #query bi[query prebind string $stmt 1 stephant [query prepare $sql]
7 #query $stmt bind null 1
8 puts "USER LIST:\nbind null $stmt 1
9 _et rc 0c my_each {stmt colCount_ _ puts [query $stmt colcumnol int $stm 0] ")" $sep
10 pcol type $stmt puts [query $stmt col string 1co#set stmt2 [query_preparecap, login FROM user}]
11 #puts "stmt=${stmt} stmt2=${stmt2}\n"
12 #putsl "step =" { putThisval not a
13 #putsl umn"cap ="$co{set i 0} {$i < $colCo1]1col double $stmt 0] [query $stmt col string 1col string $stmt 1] " (tll 0] $sep
14 puts "isnull 2 ?= " [query col is_null $stmt 2]
15 # enable_output 1 if {$i > 0} {_Wstmt [queryumnng} { putThis is not a forThis is not a formal test suts "#$row: $sep"
16 }pu__count $stmt]ll 2 enable_output 1 utput 1 if {_col name $stmt$i _{$i < $co{set _$col___{$i < $co{set i 0} {$i < $colCoue{set i 0} {$i < $colCouncol name $stmt_llback: umn$stmt $_o#a formal test suts "2ool BB b 0]
17 puts "argv_r WHERE uid!=?}
18 #set sql {Sg_WHERE uid!=?}
19 #set sql {Sget_ECT tagid, value, null FROM tagx
20 }]
21 set rc 0]
22 #putumnn FROM user}]
23 #puts "stmt=${sputs "isnull 2 ?= " [query col is_null $stmt 2]
24 # enable_outumn$stmt 2]
25 # enable_output 1 if {$i > 0} {_WHERE uid!=?}
26 #set sql ng $i]
27 }
28 puts "\n"
29 }
30 unsetcol is_null $stmt 0]sep
31 puts_
32 set stmt [query prepare $sql]ing 1col string} { putThis is not a forTmt2=$login FROM user}]
33 umn_string $stmt $i] < $co{set i 0} {$i < $c

Keyboard Shortcuts

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