1
0

test7.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. /*
  2. ** 2006 January 09
  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. ** Code for testing the client/server version of the SQLite library.
  13. ** Derived from test4.c.
  14. */
  15. #include "sqliteInt.h"
  16. #include "tcl.h"
  17. /*
  18. ** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
  19. ** the SQLITE_SERVER option.
  20. */
  21. #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
  22. SQLITE_OS_UNIX && SQLITE_THREADSAFE
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <pthread.h>
  26. #include <sched.h>
  27. #include <ctype.h>
  28. /*
  29. ** Interfaces defined in server.c
  30. */
  31. int sqlite3_client_open(const char*, sqlite3**);
  32. int sqlite3_client_prepare(sqlite3*,const char*,int,
  33. sqlite3_stmt**,const char**);
  34. int sqlite3_client_step(sqlite3_stmt*);
  35. int sqlite3_client_reset(sqlite3_stmt*);
  36. int sqlite3_client_finalize(sqlite3_stmt*);
  37. int sqlite3_client_close(sqlite3*);
  38. int sqlite3_server_start(void);
  39. int sqlite3_server_stop(void);
  40. void sqlite3_server_start2(int *pnDecr);
  41. /*
  42. ** Each thread is controlled by an instance of the following
  43. ** structure.
  44. */
  45. typedef struct Thread Thread;
  46. struct Thread {
  47. /* The first group of fields are writable by the supervisor thread
  48. ** and read-only to the client threads
  49. */
  50. char *zFilename; /* Name of database file */
  51. void (*xOp)(Thread*); /* next operation to do */
  52. char *zArg; /* argument usable by xOp */
  53. volatile int opnum; /* Operation number */
  54. volatile int busy; /* True if this thread is in use */
  55. /* The next group of fields are writable by the client threads
  56. ** but read-only to the superviser thread.
  57. */
  58. volatile int completed; /* Number of operations completed */
  59. sqlite3 *db; /* Open database */
  60. sqlite3_stmt *pStmt; /* Pending operation */
  61. char *zErr; /* operation error */
  62. char *zStaticErr; /* Static error message */
  63. int rc; /* operation return code */
  64. int argc; /* number of columns in result */
  65. const char *argv[100]; /* result columns */
  66. const char *colv[100]; /* result column names */
  67. /* Initialized to 1 by the supervisor thread when the client is
  68. ** created, and then deemed read-only to the supervisor thread.
  69. ** Is set to 0 by the server thread belonging to this client
  70. ** just before it exits.
  71. */
  72. int nServer; /* Number of server threads running */
  73. };
  74. /*
  75. ** There can be as many as 26 threads running at once. Each is named
  76. ** by a capital letter: A, B, C, ..., Y, Z.
  77. */
  78. #define N_THREAD 26
  79. static Thread threadset[N_THREAD];
  80. /*
  81. ** The main loop for a thread. Threads use busy waiting.
  82. */
  83. static void *client_main(void *pArg){
  84. Thread *p = (Thread*)pArg;
  85. if( p->db ){
  86. sqlite3_client_close(p->db);
  87. }
  88. sqlite3_client_open(p->zFilename, &p->db);
  89. if( SQLITE_OK!=sqlite3_errcode(p->db) ){
  90. p->zErr = strdup(sqlite3_errmsg(p->db));
  91. sqlite3_client_close(p->db);
  92. p->db = 0;
  93. }
  94. p->pStmt = 0;
  95. p->completed = 1;
  96. while( p->opnum<=p->completed ) sched_yield();
  97. while( p->xOp ){
  98. if( p->zErr && p->zErr!=p->zStaticErr ){
  99. sqlite3_free(p->zErr);
  100. p->zErr = 0;
  101. }
  102. (*p->xOp)(p);
  103. p->completed++;
  104. while( p->opnum<=p->completed ) sched_yield();
  105. }
  106. if( p->pStmt ){
  107. sqlite3_client_finalize(p->pStmt);
  108. p->pStmt = 0;
  109. }
  110. if( p->db ){
  111. sqlite3_client_close(p->db);
  112. p->db = 0;
  113. }
  114. if( p->zErr && p->zErr!=p->zStaticErr ){
  115. sqlite3_free(p->zErr);
  116. p->zErr = 0;
  117. }
  118. p->completed++;
  119. #ifndef SQLITE_OMIT_DEPRECATED
  120. sqlite3_thread_cleanup();
  121. #endif
  122. return 0;
  123. }
  124. /*
  125. ** Get a thread ID which is an upper case letter. Return the index.
  126. ** If the argument is not a valid thread ID put an error message in
  127. ** the interpreter and return -1.
  128. */
  129. static int parse_client_id(Tcl_Interp *interp, const char *zArg){
  130. if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
  131. Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
  132. return -1;
  133. }
  134. return zArg[0] - 'A';
  135. }
  136. /*
  137. ** Usage: client_create NAME FILENAME
  138. **
  139. ** NAME should be an upper case letter. Start the thread running with
  140. ** an open connection to the given database.
  141. */
  142. static int tcl_client_create(
  143. void *NotUsed,
  144. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  145. int argc, /* Number of arguments */
  146. const char **argv /* Text of each argument */
  147. ){
  148. int i;
  149. pthread_t x;
  150. int rc;
  151. if( argc!=3 ){
  152. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  153. " ID FILENAME", 0);
  154. return TCL_ERROR;
  155. }
  156. i = parse_client_id(interp, argv[1]);
  157. if( i<0 ) return TCL_ERROR;
  158. if( threadset[i].busy ){
  159. Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
  160. return TCL_ERROR;
  161. }
  162. threadset[i].busy = 1;
  163. sqlite3_free(threadset[i].zFilename);
  164. threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);
  165. threadset[i].opnum = 1;
  166. threadset[i].completed = 0;
  167. rc = pthread_create(&x, 0, client_main, &threadset[i]);
  168. if( rc ){
  169. Tcl_AppendResult(interp, "failed to create the thread", 0);
  170. sqlite3_free(threadset[i].zFilename);
  171. threadset[i].busy = 0;
  172. return TCL_ERROR;
  173. }
  174. pthread_detach(x);
  175. if( threadset[i].nServer==0 ){
  176. threadset[i].nServer = 1;
  177. sqlite3_server_start2(&threadset[i].nServer);
  178. }
  179. return TCL_OK;
  180. }
  181. /*
  182. ** Wait for a thread to reach its idle state.
  183. */
  184. static void client_wait(Thread *p){
  185. while( p->opnum>p->completed ) sched_yield();
  186. }
  187. /*
  188. ** Usage: client_wait ID
  189. **
  190. ** Wait on thread ID to reach its idle state.
  191. */
  192. static int tcl_client_wait(
  193. void *NotUsed,
  194. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  195. int argc, /* Number of arguments */
  196. const char **argv /* Text of each argument */
  197. ){
  198. int i;
  199. if( argc!=2 ){
  200. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  201. " ID", 0);
  202. return TCL_ERROR;
  203. }
  204. i = parse_client_id(interp, argv[1]);
  205. if( i<0 ) return TCL_ERROR;
  206. if( !threadset[i].busy ){
  207. Tcl_AppendResult(interp, "no such thread", 0);
  208. return TCL_ERROR;
  209. }
  210. client_wait(&threadset[i]);
  211. return TCL_OK;
  212. }
  213. /*
  214. ** Stop a thread.
  215. */
  216. static void stop_thread(Thread *p){
  217. client_wait(p);
  218. p->xOp = 0;
  219. p->opnum++;
  220. client_wait(p);
  221. sqlite3_free(p->zArg);
  222. p->zArg = 0;
  223. sqlite3_free(p->zFilename);
  224. p->zFilename = 0;
  225. p->busy = 0;
  226. }
  227. /*
  228. ** Usage: client_halt ID
  229. **
  230. ** Cause a client thread to shut itself down. Wait for the shutdown to be
  231. ** completed. If ID is "*" then stop all client threads.
  232. */
  233. static int tcl_client_halt(
  234. void *NotUsed,
  235. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  236. int argc, /* Number of arguments */
  237. const char **argv /* Text of each argument */
  238. ){
  239. int i;
  240. if( argc!=2 ){
  241. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  242. " ID", 0);
  243. return TCL_ERROR;
  244. }
  245. if( argv[1][0]=='*' && argv[1][1]==0 ){
  246. for(i=0; i<N_THREAD; i++){
  247. if( threadset[i].busy ){
  248. stop_thread(&threadset[i]);
  249. }
  250. }
  251. }else{
  252. i = parse_client_id(interp, argv[1]);
  253. if( i<0 ) return TCL_ERROR;
  254. if( !threadset[i].busy ){
  255. Tcl_AppendResult(interp, "no such thread", 0);
  256. return TCL_ERROR;
  257. }
  258. stop_thread(&threadset[i]);
  259. }
  260. /* If no client threads are still running, also stop the server */
  261. for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
  262. if( i>=N_THREAD ){
  263. sqlite3_server_stop();
  264. while( 1 ){
  265. for(i=0; i<N_THREAD && threadset[i].nServer==0; i++);
  266. if( i==N_THREAD ) break;
  267. sched_yield();
  268. }
  269. }
  270. return TCL_OK;
  271. }
  272. /*
  273. ** Usage: client_argc ID
  274. **
  275. ** Wait on the most recent client_step to complete, then return the
  276. ** number of columns in the result set.
  277. */
  278. static int tcl_client_argc(
  279. void *NotUsed,
  280. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  281. int argc, /* Number of arguments */
  282. const char **argv /* Text of each argument */
  283. ){
  284. int i;
  285. char zBuf[100];
  286. if( argc!=2 ){
  287. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  288. " ID", 0);
  289. return TCL_ERROR;
  290. }
  291. i = parse_client_id(interp, argv[1]);
  292. if( i<0 ) return TCL_ERROR;
  293. if( !threadset[i].busy ){
  294. Tcl_AppendResult(interp, "no such thread", 0);
  295. return TCL_ERROR;
  296. }
  297. client_wait(&threadset[i]);
  298. sprintf(zBuf, "%d", threadset[i].argc);
  299. Tcl_AppendResult(interp, zBuf, 0);
  300. return TCL_OK;
  301. }
  302. /*
  303. ** Usage: client_argv ID N
  304. **
  305. ** Wait on the most recent client_step to complete, then return the
  306. ** value of the N-th columns in the result set.
  307. */
  308. static int tcl_client_argv(
  309. void *NotUsed,
  310. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  311. int argc, /* Number of arguments */
  312. const char **argv /* Text of each argument */
  313. ){
  314. int i;
  315. int n;
  316. if( argc!=3 ){
  317. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  318. " ID N", 0);
  319. return TCL_ERROR;
  320. }
  321. i = parse_client_id(interp, argv[1]);
  322. if( i<0 ) return TCL_ERROR;
  323. if( !threadset[i].busy ){
  324. Tcl_AppendResult(interp, "no such thread", 0);
  325. return TCL_ERROR;
  326. }
  327. if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
  328. client_wait(&threadset[i]);
  329. if( n<0 || n>=threadset[i].argc ){
  330. Tcl_AppendResult(interp, "column number out of range", 0);
  331. return TCL_ERROR;
  332. }
  333. Tcl_AppendResult(interp, threadset[i].argv[n], 0);
  334. return TCL_OK;
  335. }
  336. /*
  337. ** Usage: client_colname ID N
  338. **
  339. ** Wait on the most recent client_step to complete, then return the
  340. ** name of the N-th columns in the result set.
  341. */
  342. static int tcl_client_colname(
  343. void *NotUsed,
  344. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  345. int argc, /* Number of arguments */
  346. const char **argv /* Text of each argument */
  347. ){
  348. int i;
  349. int n;
  350. if( argc!=3 ){
  351. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  352. " ID N", 0);
  353. return TCL_ERROR;
  354. }
  355. i = parse_client_id(interp, argv[1]);
  356. if( i<0 ) return TCL_ERROR;
  357. if( !threadset[i].busy ){
  358. Tcl_AppendResult(interp, "no such thread", 0);
  359. return TCL_ERROR;
  360. }
  361. if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
  362. client_wait(&threadset[i]);
  363. if( n<0 || n>=threadset[i].argc ){
  364. Tcl_AppendResult(interp, "column number out of range", 0);
  365. return TCL_ERROR;
  366. }
  367. Tcl_AppendResult(interp, threadset[i].colv[n], 0);
  368. return TCL_OK;
  369. }
  370. extern const char *sqlite3ErrName(int);
  371. /*
  372. ** Usage: client_result ID
  373. **
  374. ** Wait on the most recent operation to complete, then return the
  375. ** result code from that operation.
  376. */
  377. static int tcl_client_result(
  378. void *NotUsed,
  379. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  380. int argc, /* Number of arguments */
  381. const char **argv /* Text of each argument */
  382. ){
  383. int i;
  384. const char *zName;
  385. if( argc!=2 ){
  386. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  387. " ID", 0);
  388. return TCL_ERROR;
  389. }
  390. i = parse_client_id(interp, argv[1]);
  391. if( i<0 ) return TCL_ERROR;
  392. if( !threadset[i].busy ){
  393. Tcl_AppendResult(interp, "no such thread", 0);
  394. return TCL_ERROR;
  395. }
  396. client_wait(&threadset[i]);
  397. zName = sqlite3ErrName(threadset[i].rc);
  398. Tcl_AppendResult(interp, zName, 0);
  399. return TCL_OK;
  400. }
  401. /*
  402. ** Usage: client_error ID
  403. **
  404. ** Wait on the most recent operation to complete, then return the
  405. ** error string.
  406. */
  407. static int tcl_client_error(
  408. void *NotUsed,
  409. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  410. int argc, /* Number of arguments */
  411. const char **argv /* Text of each argument */
  412. ){
  413. int i;
  414. if( argc!=2 ){
  415. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  416. " ID", 0);
  417. return TCL_ERROR;
  418. }
  419. i = parse_client_id(interp, argv[1]);
  420. if( i<0 ) return TCL_ERROR;
  421. if( !threadset[i].busy ){
  422. Tcl_AppendResult(interp, "no such thread", 0);
  423. return TCL_ERROR;
  424. }
  425. client_wait(&threadset[i]);
  426. Tcl_AppendResult(interp, threadset[i].zErr, 0);
  427. return TCL_OK;
  428. }
  429. /*
  430. ** This procedure runs in the thread to compile an SQL statement.
  431. */
  432. static void do_compile(Thread *p){
  433. if( p->db==0 ){
  434. p->zErr = p->zStaticErr = "no database is open";
  435. p->rc = SQLITE_ERROR;
  436. return;
  437. }
  438. if( p->pStmt ){
  439. sqlite3_client_finalize(p->pStmt);
  440. p->pStmt = 0;
  441. }
  442. p->rc = sqlite3_client_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
  443. }
  444. /*
  445. ** Usage: client_compile ID SQL
  446. **
  447. ** Compile a new virtual machine.
  448. */
  449. static int tcl_client_compile(
  450. void *NotUsed,
  451. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  452. int argc, /* Number of arguments */
  453. const char **argv /* Text of each argument */
  454. ){
  455. int i;
  456. if( argc!=3 ){
  457. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  458. " ID SQL", 0);
  459. return TCL_ERROR;
  460. }
  461. i = parse_client_id(interp, argv[1]);
  462. if( i<0 ) return TCL_ERROR;
  463. if( !threadset[i].busy ){
  464. Tcl_AppendResult(interp, "no such thread", 0);
  465. return TCL_ERROR;
  466. }
  467. client_wait(&threadset[i]);
  468. threadset[i].xOp = do_compile;
  469. sqlite3_free(threadset[i].zArg);
  470. threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
  471. threadset[i].opnum++;
  472. return TCL_OK;
  473. }
  474. /*
  475. ** This procedure runs in the thread to step the virtual machine.
  476. */
  477. static void do_step(Thread *p){
  478. int i;
  479. if( p->pStmt==0 ){
  480. p->zErr = p->zStaticErr = "no virtual machine available";
  481. p->rc = SQLITE_ERROR;
  482. return;
  483. }
  484. p->rc = sqlite3_client_step(p->pStmt);
  485. if( p->rc==SQLITE_ROW ){
  486. p->argc = sqlite3_column_count(p->pStmt);
  487. for(i=0; i<sqlite3_data_count(p->pStmt); i++){
  488. p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
  489. }
  490. for(i=0; i<p->argc; i++){
  491. p->colv[i] = sqlite3_column_name(p->pStmt, i);
  492. }
  493. }
  494. }
  495. /*
  496. ** Usage: client_step ID
  497. **
  498. ** Advance the virtual machine by one step
  499. */
  500. static int tcl_client_step(
  501. void *NotUsed,
  502. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  503. int argc, /* Number of arguments */
  504. const char **argv /* Text of each argument */
  505. ){
  506. int i;
  507. if( argc!=2 ){
  508. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  509. " IDL", 0);
  510. return TCL_ERROR;
  511. }
  512. i = parse_client_id(interp, argv[1]);
  513. if( i<0 ) return TCL_ERROR;
  514. if( !threadset[i].busy ){
  515. Tcl_AppendResult(interp, "no such thread", 0);
  516. return TCL_ERROR;
  517. }
  518. client_wait(&threadset[i]);
  519. threadset[i].xOp = do_step;
  520. threadset[i].opnum++;
  521. return TCL_OK;
  522. }
  523. /*
  524. ** This procedure runs in the thread to finalize a virtual machine.
  525. */
  526. static void do_finalize(Thread *p){
  527. if( p->pStmt==0 ){
  528. p->zErr = p->zStaticErr = "no virtual machine available";
  529. p->rc = SQLITE_ERROR;
  530. return;
  531. }
  532. p->rc = sqlite3_client_finalize(p->pStmt);
  533. p->pStmt = 0;
  534. }
  535. /*
  536. ** Usage: client_finalize ID
  537. **
  538. ** Finalize the virtual machine.
  539. */
  540. static int tcl_client_finalize(
  541. void *NotUsed,
  542. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  543. int argc, /* Number of arguments */
  544. const char **argv /* Text of each argument */
  545. ){
  546. int i;
  547. if( argc!=2 ){
  548. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  549. " IDL", 0);
  550. return TCL_ERROR;
  551. }
  552. i = parse_client_id(interp, argv[1]);
  553. if( i<0 ) return TCL_ERROR;
  554. if( !threadset[i].busy ){
  555. Tcl_AppendResult(interp, "no such thread", 0);
  556. return TCL_ERROR;
  557. }
  558. client_wait(&threadset[i]);
  559. threadset[i].xOp = do_finalize;
  560. sqlite3_free(threadset[i].zArg);
  561. threadset[i].zArg = 0;
  562. threadset[i].opnum++;
  563. return TCL_OK;
  564. }
  565. /*
  566. ** This procedure runs in the thread to reset a virtual machine.
  567. */
  568. static void do_reset(Thread *p){
  569. if( p->pStmt==0 ){
  570. p->zErr = p->zStaticErr = "no virtual machine available";
  571. p->rc = SQLITE_ERROR;
  572. return;
  573. }
  574. p->rc = sqlite3_client_reset(p->pStmt);
  575. p->pStmt = 0;
  576. }
  577. /*
  578. ** Usage: client_reset ID
  579. **
  580. ** Finalize the virtual machine.
  581. */
  582. static int tcl_client_reset(
  583. void *NotUsed,
  584. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  585. int argc, /* Number of arguments */
  586. const char **argv /* Text of each argument */
  587. ){
  588. int i;
  589. if( argc!=2 ){
  590. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  591. " IDL", 0);
  592. return TCL_ERROR;
  593. }
  594. i = parse_client_id(interp, argv[1]);
  595. if( i<0 ) return TCL_ERROR;
  596. if( !threadset[i].busy ){
  597. Tcl_AppendResult(interp, "no such thread", 0);
  598. return TCL_ERROR;
  599. }
  600. client_wait(&threadset[i]);
  601. threadset[i].xOp = do_reset;
  602. sqlite3_free(threadset[i].zArg);
  603. threadset[i].zArg = 0;
  604. threadset[i].opnum++;
  605. return TCL_OK;
  606. }
  607. /*
  608. ** Usage: client_swap ID ID
  609. **
  610. ** Interchange the sqlite* pointer between two threads.
  611. */
  612. static int tcl_client_swap(
  613. void *NotUsed,
  614. Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
  615. int argc, /* Number of arguments */
  616. const char **argv /* Text of each argument */
  617. ){
  618. int i, j;
  619. sqlite3 *temp;
  620. if( argc!=3 ){
  621. Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  622. " ID1 ID2", 0);
  623. return TCL_ERROR;
  624. }
  625. i = parse_client_id(interp, argv[1]);
  626. if( i<0 ) return TCL_ERROR;
  627. if( !threadset[i].busy ){
  628. Tcl_AppendResult(interp, "no such thread", 0);
  629. return TCL_ERROR;
  630. }
  631. client_wait(&threadset[i]);
  632. j = parse_client_id(interp, argv[2]);
  633. if( j<0 ) return TCL_ERROR;
  634. if( !threadset[j].busy ){
  635. Tcl_AppendResult(interp, "no such thread", 0);
  636. return TCL_ERROR;
  637. }
  638. client_wait(&threadset[j]);
  639. temp = threadset[i].db;
  640. threadset[i].db = threadset[j].db;
  641. threadset[j].db = temp;
  642. return TCL_OK;
  643. }
  644. /*
  645. ** Register commands with the TCL interpreter.
  646. */
  647. int Sqlitetest7_Init(Tcl_Interp *interp){
  648. static struct {
  649. char *zName;
  650. Tcl_CmdProc *xProc;
  651. } aCmd[] = {
  652. { "client_create", (Tcl_CmdProc*)tcl_client_create },
  653. { "client_wait", (Tcl_CmdProc*)tcl_client_wait },
  654. { "client_halt", (Tcl_CmdProc*)tcl_client_halt },
  655. { "client_argc", (Tcl_CmdProc*)tcl_client_argc },
  656. { "client_argv", (Tcl_CmdProc*)tcl_client_argv },
  657. { "client_colname", (Tcl_CmdProc*)tcl_client_colname },
  658. { "client_result", (Tcl_CmdProc*)tcl_client_result },
  659. { "client_error", (Tcl_CmdProc*)tcl_client_error },
  660. { "client_compile", (Tcl_CmdProc*)tcl_client_compile },
  661. { "client_step", (Tcl_CmdProc*)tcl_client_step },
  662. { "client_reset", (Tcl_CmdProc*)tcl_client_reset },
  663. { "client_finalize", (Tcl_CmdProc*)tcl_client_finalize },
  664. { "client_swap", (Tcl_CmdProc*)tcl_client_swap },
  665. };
  666. int i;
  667. for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
  668. Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  669. }
  670. return TCL_OK;
  671. }
  672. #else
  673. int Sqlitetest7_Init(Tcl_Interp *interp){ return TCL_OK; }
  674. #endif /* SQLITE_OS_UNIX */