1 /* 2 ** 2017 April 07 3 ** 4 ** The author disclaims copyright to this source code. In place of 5 ** a legal notice, here is a blessing: 6 ** 7 ** May you do good and not evil. 8 ** May you find forgiveness for yourself and forgive others. 9 ** May you share freely, never taking more than you give. 10 ** 11 ************************************************************************* 12 */ 13 14 #if defined(SQLITE_TEST) 15 16 #include "sqlite3expert.h" 17 #include <assert.h> 18 #include <string.h> 19 20 #if defined(INCLUDE_SQLITE_TCL_H) 21 # include "sqlite_tcl.h" 22 #else 23 # include "tcl.h" 24 # ifndef SQLITE_TCLAPI 25 # define SQLITE_TCLAPI 26 # endif 27 #endif 28 29 #ifndef SQLITE_OMIT_VIRTUALTABLE 30 31 /* 32 ** Extract an sqlite3* db handle from the object passed as the second 33 ** argument. If successful, set *pDb to point to the db handle and return 34 ** TCL_OK. Otherwise, return TCL_ERROR. 35 */ 36 static int dbHandleFromObj(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){ 37 Tcl_CmdInfo info; 38 if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){ 39 Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0); 40 return TCL_ERROR; 41 } 42 43 *pDb = *(sqlite3 **)info.objClientData; 44 return TCL_OK; 45 } 46 47 48 /* 49 ** Tclcmd: $expert sql SQL 50 ** $expert analyze 51 ** $expert count 52 ** $expert report STMT EREPORT 53 ** $expert destroy 54 */ 55 static int SQLITE_TCLAPI testExpertCmd( 56 void *clientData, 57 Tcl_Interp *interp, 58 int objc, 59 Tcl_Obj *CONST objv[] 60 ){ 61 sqlite3expert *pExpert = (sqlite3expert*)clientData; 62 struct Subcmd { 63 const char *zSub; 64 int nArg; 65 const char *zMsg; 66 } aSub[] = { 67 { "sql", 1, "TABLE", }, /* 0 */ 68 { "analyze", 0, "", }, /* 1 */ 69 { "count", 0, "", }, /* 2 */ 70 { "report", 2, "STMT EREPORT", }, /* 3 */ 71 { "destroy", 0, "", }, /* 4 */ 72 { 0 } 73 }; 74 int iSub; 75 int rc = TCL_OK; 76 char *zErr = 0; 77 78 if( objc<2 ){ 79 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); 80 return TCL_ERROR; 81 } 82 rc = Tcl_GetIndexFromObjStruct(interp, 83 objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub 84 ); 85 if( rc!=TCL_OK ) return rc; 86 if( objc!=2+aSub[iSub].nArg ){ 87 Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg); 88 return TCL_ERROR; 89 } 90 91 switch( iSub ){ 92 case 0: { /* sql */ 93 char *zArg = Tcl_GetString(objv[2]); 94 rc = sqlite3_expert_sql(pExpert, zArg, &zErr); 95 break; 96 } 97 98 case 1: { /* analyze */ 99 rc = sqlite3_expert_analyze(pExpert, &zErr); 100 break; 101 } 102 103 case 2: { /* count */ 104 int n = sqlite3_expert_count(pExpert); 105 Tcl_SetObjResult(interp, Tcl_NewIntObj(n)); 106 break; 107 } 108 109 case 3: { /* report */ 110 const char *aEnum[] = { 111 "sql", "indexes", "plan", "candidates", 0 112 }; 113 int iEnum; 114 int iStmt; 115 const char *zReport; 116 117 if( Tcl_GetIntFromObj(interp, objv[2], &iStmt) 118 || Tcl_GetIndexFromObj(interp, objv[3], aEnum, "report", 0, &iEnum) 119 ){ 120 return TCL_ERROR; 121 } 122 123 assert( EXPERT_REPORT_SQL==1 ); 124 assert( EXPERT_REPORT_INDEXES==2 ); 125 assert( EXPERT_REPORT_PLAN==3 ); 126 assert( EXPERT_REPORT_CANDIDATES==4 ); 127 zReport = sqlite3_expert_report(pExpert, iStmt, 1+iEnum); 128 Tcl_SetObjResult(interp, Tcl_NewStringObj(zReport, -1)); 129 break; 130 } 131 132 default: /* destroy */ 133 assert( iSub==4 ); 134 Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); 135 break; 136 } 137 138 if( rc!=TCL_OK ){ 139 if( zErr ){ 140 Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); 141 }else{ 142 extern const char *sqlite3ErrName(int); 143 Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); 144 } 145 } 146 sqlite3_free(zErr); 147 return rc; 148 } 149 150 static void SQLITE_TCLAPI testExpertDel(void *clientData){ 151 sqlite3expert *pExpert = (sqlite3expert*)clientData; 152 sqlite3_expert_destroy(pExpert); 153 } 154 155 /* 156 ** sqlite3_expert_new DB 157 */ 158 static int SQLITE_TCLAPI test_sqlite3_expert_new( 159 void * clientData, 160 Tcl_Interp *interp, 161 int objc, 162 Tcl_Obj *CONST objv[] 163 ){ 164 static int iCmd = 0; 165 sqlite3 *db; 166 char *zCmd = 0; 167 char *zErr = 0; 168 sqlite3expert *pExpert; 169 int rc = TCL_OK; 170 171 if( objc!=2 ){ 172 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 173 return TCL_ERROR; 174 } 175 if( dbHandleFromObj(interp, objv[1], &db) ){ 176 return TCL_ERROR; 177 } 178 179 zCmd = sqlite3_mprintf("sqlite3expert%d", ++iCmd); 180 if( zCmd==0 ){ 181 Tcl_AppendResult(interp, "out of memory", (char*)0); 182 return TCL_ERROR; 183 } 184 185 pExpert = sqlite3_expert_new(db, &zErr); 186 if( pExpert==0 ){ 187 Tcl_AppendResult(interp, zErr, (char*)0); 188 rc = TCL_ERROR; 189 }else{ 190 void *p = (void*)pExpert; 191 Tcl_CreateObjCommand(interp, zCmd, testExpertCmd, p, testExpertDel); 192 Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1)); 193 } 194 195 sqlite3_free(zCmd); 196 sqlite3_free(zErr); 197 return rc; 198 } 199 200 #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ 201 202 int TestExpert_Init(Tcl_Interp *interp){ 203 #ifndef SQLITE_OMIT_VIRTUALTABLE 204 struct Cmd { 205 const char *zCmd; 206 Tcl_ObjCmdProc *xProc; 207 } aCmd[] = { 208 { "sqlite3_expert_new", test_sqlite3_expert_new }, 209 }; 210 int i; 211 212 for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){ 213 struct Cmd *p = &aCmd[i]; 214 Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0); 215 } 216 #endif 217 return TCL_OK; 218 } 219 220 #endif 221