Bug Summary

File:db/dbtcl.c
Warning:line 637, column 9
Duplicate code detected
Note:line 641, column 9
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name dbtcl.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsdb -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsdb -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c dbtcl.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30
31/*
32 * dbtcl.c --
33 *
34 * Tcl database access routines.
35 */
36
37#include "db.h"
38
39/*
40 * The following structure maintains per-interp data.
41 */
42
43typedef struct InterpData {
44 const char *server;
45 Tcl_HashTable dbs;
46} InterpData;
47
48/*
49 * Local functions defined in this file
50 */
51
52static int DbFail(Tcl_Interp *interp, Ns_DbHandle *handle, const char *cmd)
53 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
54
55static void EnterDbHandle(InterpData *idataPtr, Tcl_Interp *interp, Ns_DbHandle *handle, Tcl_Obj *listObj)
56 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
57
58static int DbGetHandle(InterpData *idataPtr, Tcl_Interp *interp, const char *handleId,
59 Ns_DbHandle **handle, Tcl_HashEntry **hPtrPtr);
60
61static Ns_ReturnCode QuoteSqlValue(Tcl_DString *dsPtr, Tcl_Obj *valueObj, int valueType)
62 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
63
64static void
65FinishElement(Tcl_DString *elemPtr, Tcl_DString *colsPtr, bool_Bool quoted)
66 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
67
68#if !defined(NS_TCL_PRE85)
69static Ns_ReturnCode CurrentHandles( Tcl_Interp *interp, Tcl_HashTable *tablePtr, Tcl_Obj *dictObj)
70 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
71#endif
72
73static Tcl_InterpDeleteProc FreeData;
74static Tcl_ObjCmdProc
75 DbConfigPathObjCmd,
76 DbErrorCodeObjCmd,
77 DbErrorMsgObjCmd,
78 DbObjCmd,
79 GetCsvObjCmd,
80 PoolDescriptionObjCmd,
81 QuoteListObjCmd,
82 QuoteListToListObjCmd,
83 QuoteValueObjCmd;
84
85static int ErrorObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, char cmd);
86
87
88/*
89 * Local variables defined in this file.
90 */
91
92static const Tcl_ObjType *intTypePtr = NULL((void*)0);
93static const char *const datakey = "nsdb:data";
94
95static const Ns_ObjvTable valueTypes[] = {
96 {"decimal", UCHAR('n')((unsigned char)('n'))},
97 {"double", UCHAR('n')((unsigned char)('n'))},
98 {"integer", UCHAR('n')((unsigned char)('n'))},
99 {"int", UCHAR('n')((unsigned char)('n'))},
100 {"real", UCHAR('n')((unsigned char)('n'))},
101 {"smallint", UCHAR('n')((unsigned char)('n'))},
102 {"bigint", UCHAR('n')((unsigned char)('n'))},
103 {"bit", UCHAR('n')((unsigned char)('n'))},
104 {"float", UCHAR('n')((unsigned char)('n'))},
105 {"numeric", UCHAR('n')((unsigned char)('n'))},
106 {"tinyint", UCHAR('n')((unsigned char)('n'))},
107 {"text", UCHAR('q')((unsigned char)('q'))},
108 {NULL((void*)0), 0u}
109};
110
111
112/*
113 *----------------------------------------------------------------------
114 * Ns_TclDbGetHandle --
115 *
116 * Get database handle from its handle id.
117 *
118 * Results:
119 * Tcl result code.
120 *
121 * Side effects:
122 * None.
123 *
124 *----------------------------------------------------------------------
125 */
126
127int
128Ns_TclDbGetHandle(Tcl_Interp *interp, const char *handleId, Ns_DbHandle **handlePtr)
129{
130 int result;
131 InterpData *idataPtr;
132
133 idataPtr = Tcl_GetAssocData(interp, datakey, NULL((void*)0));
134 if (idataPtr == NULL((void*)0)) {
135 result = TCL_ERROR1;
136 } else {
137 result = DbGetHandle(idataPtr, interp, handleId, handlePtr, NULL((void*)0));
138 }
139 return result;
140}
141
142
143/*
144 *----------------------------------------------------------------------
145 *
146 * NsDbAddCmds --
147 *
148 * Add the nsdb commands.
149 *
150 * Results:
151 * None.
152 *
153 * Side effects:
154 * None.
155 *
156 *----------------------------------------------------------------------
157 */
158
159int
160NsDbAddCmds(Tcl_Interp *interp, const void *arg)
161{
162 const char *server = arg;
163 InterpData *idataPtr;
164
165 /*
166 * Initialize the per-interp data.
167 */
168
169 idataPtr = ns_malloc(sizeof(InterpData));
170 idataPtr->server = server;
171 Tcl_InitHashTable(&idataPtr->dbs, TCL_STRING_KEYS(0));
172 Tcl_SetAssocData(interp, datakey, FreeData, idataPtr);
173
174 intTypePtr = Tcl_GetObjType("int");
175 if (intTypePtr == NULL((void*)0)) {
176 Tcl_Panic("NsTclInitObjs: no int type");
177 }
178
179 (void)Tcl_CreateObjCommand(interp, "ns_db", DbObjCmd, idataPtr, NULL((void*)0));
180 (void)Tcl_CreateObjCommand(interp, "ns_dbconfigpath", DbConfigPathObjCmd, idataPtr, NULL((void*)0));
181 (void)Tcl_CreateObjCommand(interp, "ns_dberrorcode", DbErrorCodeObjCmd, idataPtr, NULL((void*)0));
182 (void)Tcl_CreateObjCommand(interp, "ns_dberrormsg", DbErrorMsgObjCmd, idataPtr, NULL((void*)0));
183 (void)Tcl_CreateObjCommand(interp, "ns_dbquotevalue", QuoteValueObjCmd, idataPtr, NULL((void*)0));
184 (void)Tcl_CreateObjCommand(interp, "ns_dbquotelist", QuoteListObjCmd, idataPtr, NULL((void*)0));
185 (void)Tcl_CreateObjCommand(interp, "ns_getcsv", GetCsvObjCmd, idataPtr, NULL((void*)0));
186 (void)Tcl_CreateObjCommand(interp, "ns_pooldescription", PoolDescriptionObjCmd, idataPtr, NULL((void*)0));
187 (void)Tcl_CreateObjCommand(interp, "ns_quotelisttolist", QuoteListToListObjCmd, idataPtr, NULL((void*)0));
188
189 return TCL_OK0;
190}
191
192
193/*
194 *----------------------------------------------------------------------
195 * NsDbReleaseHandles --
196 *
197 * Release any database handles still held when an interp is
198 * deallocated.
199 *
200 * Results:
201 * None.
202 *
203 * Side effects:
204 * None.
205 *
206 *----------------------------------------------------------------------
207 */
208
209int
210NsDbReleaseHandles(Tcl_Interp *interp, const void *UNUSED(arg)UNUSED_arg __attribute__((__unused__)))
211{
212 InterpData *idataPtr;
213
214 idataPtr = Tcl_GetAssocData(interp, datakey, NULL((void*)0));
215 if (idataPtr != NULL((void*)0)) {
216 Tcl_HashSearch search;
217 const Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&idataPtr->dbs, &search);
218
219 while (hPtr != NULL((void*)0)) {
220 Ns_DbHandle *handlePtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
221
222 Ns_DbPoolPutHandle(handlePtr);
223 hPtr = Tcl_NextHashEntry(&search);
224 }
225 Tcl_DeleteHashTable(&idataPtr->dbs);
226 Tcl_InitHashTable(&idataPtr->dbs, TCL_STRING_KEYS(0));
227 }
228
229 return TCL_OK0;
230}
231
232#if !defined(NS_TCL_PRE85)
233
234/*
235 *----------------------------------------------------------------------
236 *
237 * CurrentHandles --
238 *
239 * Return a Tcl dict with information about the current allocated
240 * handles in the current interp/thread.
241 *
242 * Results:
243 * NS_OK if everything went well, NS_ERROR otherwise.
244 *
245 * Side effects:
246 * None.
247 *
248 *----------------------------------------------------------------------
249 */
250
251static Ns_ReturnCode
252CurrentHandles( Tcl_Interp *interp, Tcl_HashTable *tablePtr, Tcl_Obj *dictObj)
253{
254 Tcl_HashSearch search;
255 const Tcl_HashEntry *hPtr;
256
257 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
258 NS_NONNULL_ASSERT(tablePtr != NULL)((void) (0));
259 NS_NONNULL_ASSERT(dictObj != NULL)((void) (0));
260
261 hPtr = Tcl_FirstHashEntry(tablePtr, &search);
262 while (hPtr != NULL((void*)0)) {
263 Ns_DbHandle *handlePtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData);
264 Tcl_Obj *keyv[2];
265
266 keyv[0] = Tcl_NewStringObj(handlePtr->poolname, -1);
267 keyv[1] = Tcl_NewStringObj(Tcl_GetHashKey(tablePtr, hPtr)((void *) (((tablePtr)->keyType == (1) || (tablePtr)->keyType
== (-1)) ? (hPtr)->key.oneWordValue : (hPtr)->key.string
))
, -1);
268 Tcl_DictObjPutKeyList(interp, dictObj, 2, keyv, Tcl_NewIntObj(NsDbGetActive(handlePtr)));
269 hPtr = Tcl_NextHashEntry(&search);
270 }
271
272 return NS_OK;
273}
274#endif
275
276
277/*
278 *----------------------------------------------------------------------
279 *
280 * DbObjCmd --
281 *
282 * Implements "ns_db".
283 *
284 * Results:
285 * Return TCL_OK upon success and TCL_ERROR otherwise.
286 *
287 * Side effects:
288 * Depends on the command.
289 *
290 *----------------------------------------------------------------------
291 */
292
293static int
294DbObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
295{
296 InterpData *idataPtr = clientData;
297 char tmpbuf[32] = "";
298 const char *pool = NULL((void*)0);
299 int cmd, nrows;
300 Ns_DbHandle *handlePtr = NULL((void*)0);
301 Ns_Set *rowPtr;
302 Tcl_HashEntry *hPtr;
303 int result = TCL_OK0;
304
305 enum {
306 ZERO_OR_ONE_ROW,
307 ONE_ROW,
308 BINDROW,
309 BOUNCEPOOL,
310 CANCEL,
311 CONNECTED,
312#if !defined(NS_TCL_PRE85)
313 CURRENTHANDLES,
314#endif
315 DATASOURCE,
316 DBTYPE,
317 DISCONNECT,
318 DML,
319 DRIVER,
320 EXCEPTION,
321 EXEC,
322 FLUSH,
323 GETHANDLE,
324 GETROW,
325 INTERPRETSQLFILE,
326 LOGMINDURATION,
327 PASSWORD,
328 POOLNAME,
329 POOLS,
330 RELEASEHANDLE,
331 RESETHANDLE,
332 ROWCOUNT,
333 SELECT,
334 SESSIONID,
335 SETEXCEPTION,
336 SP_EXEC,
337 SP_GETPARAMS,
338 SP_RETURNCODE,
339 SP_SETPARAM,
340 SP_START,
341 STATS,
342 USER,
343 VERBOSE
344 };
345
346 static const char *const subcmd[] = {
347 "0or1row",
348 "1row",
349 "bindrow",
350 "bouncepool",
351 "cancel",
352 "connected",
353#if !defined(NS_TCL_PRE85)
354 "currenthandles",
355#endif
356 "datasource",
357 "dbtype",
358 "disconnect",
359 "dml",
360 "driver",
361 "exception",
362 "exec",
363 "flush",
364 "gethandle",
365 "getrow",
366 "interpretsqlfile",
367 "logminduration",
368 "password",
369 "poolname",
370 "pools",
371 "releasehandle",
372 "resethandle",
373 "rowcount",
374 "select",
375 "session_id",
376 "setexception",
377 "sp_exec",
378 "sp_getparams",
379 "sp_returncode",
380 "sp_setparam",
381 "sp_start",
382 "stats",
383 "user",
384 "verbose",
385 NULL((void*)0)
386 };
387
388 if (objc < 2) {
389 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
390 return TCL_ERROR1;
391 }
392 if (Tcl_GetIndexFromObj(interp, objv[1], subcmd, "option", 0, &cmd)Tcl_GetIndexFromObjStruct(interp, objv[1], subcmd, sizeof(char
*), "option", 0, &cmd)
!= TCL_OK0) {
393 return TCL_ERROR1;
394 }
395
396 switch (cmd) {
397 case POOLS:
398 if (objc != 2) {
399 Tcl_WrongNumArgs(interp, 2, objv, NULL((void*)0));
400 result = TCL_ERROR1;
401 } else {
402 pool = Ns_DbPoolList(idataPtr->server);
403 if (pool != NULL((void*)0)) {
404 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
405
406 while (*pool != '\0') {
407 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(pool, -1));
408 pool = pool + strlen(pool) + 1;
409 }
410 Tcl_SetObjResult(interp, listObj);
411 }
412 }
413 break;
414
415 case BOUNCEPOOL:
416 if (objc != 3) {
417 Tcl_WrongNumArgs(interp, 2, objv, "pool");
418 result = TCL_ERROR1;
419
420 } else if (Ns_DbBouncePool(Tcl_GetString(objv[2])) == NS_ERROR) {
421 Ns_TclPrintfResult(interp, "could not bounce: %s", Tcl_GetString(objv[2]));
422 result = TCL_ERROR1;
423 }
424 break;
425
426 case GETHANDLE: {
427 int nhandles = 1;
428 Ns_Time *timeoutPtr = NULL((void*)0);
429 Ns_DbHandle **handlesPtrPtr;
430 Ns_ReturnCode status;
431 char *poolString = NULL((void*)0);
432 Ns_ObjvValueRange handlesRange = {1, INT_MAX2147483647};
433 Ns_ObjvSpec opts[] = {
434 {"-timeout", Ns_ObjvTime, &timeoutPtr, NULL((void*)0)},
435 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
436 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
437 };
438 Ns_ObjvSpec args[] = {
439 {"?pool", Ns_ObjvString, &poolString, NULL((void*)0)},
440 {"?nhandles", Ns_ObjvInt, &nhandles, &handlesRange},
441 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
442 };
443
444 if (Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK) {
445 return TCL_ERROR1;
446 }
447
448 /*
449 * Determine the pool and requested number of handles
450 * from the remaining args.
451 */
452
453 if (poolString == NULL((void*)0)) {
454 pool = Ns_DbPoolDefault(idataPtr->server);
455 if (pool == NULL((void*)0)) {
456 Ns_TclPrintfResult(interp, "no defaultpool configured");
457 return TCL_ERROR1;
458 }
459 } else {
460 pool = (const char *)poolString;
461 }
462
463 if (Ns_DbPoolAllowable(idataPtr->server, pool) == NS_FALSE0) {
464 Ns_TclPrintfResult(interp, "no access to pool: \"%s\"", pool);
465 return TCL_ERROR1;
466 }
467
468 /*
469 * When timeout is specified as 0 (or 0:0) then treat is as
470 * non-specified (blocking).
471 */
472 if (timeoutPtr != NULL((void*)0) && timeoutPtr->sec == 0 && timeoutPtr->usec == 0) {
473 timeoutPtr = NULL((void*)0);
474 }
475
476 /*
477 * Allocate handles and enter them into Tcl.
478 */
479
480 if (nhandles == 1) {
481 handlesPtrPtr = &handlePtr;
482 } else {
483 handlesPtrPtr = ns_malloc((size_t)nhandles * sizeof(Ns_DbHandle *));
484 }
485 status = Ns_DbPoolTimedGetMultipleHandles(handlesPtrPtr, pool,
486 nhandles, timeoutPtr);
487 if (status == NS_OK) {
488 int i;
489 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
490
491 for (i = 0; i < nhandles; ++i) {
492 EnterDbHandle(idataPtr, interp, *(handlesPtrPtr + i), listObj);
493 }
494 Tcl_SetObjResult(interp, listObj);
495 }
496 if (handlesPtrPtr != &handlePtr) {
497 ns_free(handlesPtrPtr);
498 }
499 if (status != NS_TIMEOUT && status != NS_OK) {
500 Ns_TclPrintfResult(interp,
501 "could not allocate %d handle%s from pool \"%s\"",
502 nhandles,
503 nhandles > 1 ? "s" : NS_EMPTY_STRING,
504 pool);
505 result = TCL_ERROR1;
506 }
507 break;
508 }
509
510 case CURRENTHANDLES: {
511 if (Ns_ParseObjv(NULL((void*)0), NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
512 result = TCL_ERROR1;
513
514 } else {
515 Ns_ReturnCode status;
516 Tcl_Obj *dictObj = Tcl_NewDictObj();
517
518 assert(idataPtr != NULL)((void) (0));
519
520 status = CurrentHandles(interp, &idataPtr->dbs, dictObj);
521 if (status != NS_OK) {
522 Tcl_DecrRefCount(dictObj)do { Tcl_Obj *_objPtr = (dictObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
523 result = TCL_ERROR1;
524 } else {
525 Tcl_SetObjResult(interp, dictObj);
526 }
527 }
528 break;
529 }
530
531
532 case LOGMINDURATION: {
533 Ns_Time *minDurationPtr = NULL((void*)0);
534 char *poolString = NULL((void*)0);
535 Ns_ObjvSpec args[] = {
536 {"?pool", Ns_ObjvString, &poolString, NULL((void*)0)},
537 {"?minduration", Ns_ObjvTime, &minDurationPtr, NULL((void*)0)},
538 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
539 };
540
541 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
542 result = TCL_ERROR1;
543
544 } else if (poolString == NULL((void*)0)) {
545 /*
546 * No argument, list min duration for every pool.
547 */
548 Tcl_SetObjResult(interp, Ns_DbListMinDurations(interp, idataPtr->server));
549
550 } else if (minDurationPtr == NULL((void*)0)) {
551 pool = (const char *)poolString;
552 /*
553 * In this case, minduration was not given, return the
554 * actual minduration of this pool.
555 */
556
557 if (Ns_DbGetMinDuration(interp, pool, &minDurationPtr) != TCL_OK0) {
558 result = TCL_ERROR1;
559 } else {
560 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(minDurationPtr));
561 }
562
563 } else {
564 pool = (const char *)poolString;
565 /*
566 * Set the minduration the specified value.
567 */
568 if (Ns_DbSetMinDuration(interp, pool, minDurationPtr) != TCL_OK0) {
569 result = TCL_ERROR1;
570 } else {
571 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(minDurationPtr));
572 }
573 }
574
575 break;
576 }
577
578 case EXCEPTION:
579 if (objc != 3) {
580 Tcl_WrongNumArgs(interp, 2, objv, "dbId");
581 result = TCL_ERROR1;
582
583 } else if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[2]), &handlePtr, NULL((void*)0)) != TCL_OK0) {
584 result = TCL_ERROR1;
585
586 } else {
587 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
588
589 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(handlePtr->cExceptionCode, -1));
590 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(handlePtr->dsExceptionMsg.string, -1));
591 Tcl_SetObjResult(interp, listObj);
592 }
593 break;
594
595 case STATS:
596 if (objc != 2) {
597 Tcl_WrongNumArgs(interp, 2, objv, NULL((void*)0));
598 result = TCL_ERROR1;
599 } else if (Ns_DbPoolStats(interp) != TCL_OK0) {
600 result = TCL_ERROR1;
601 }
602 break;
603
604 case POOLNAME: NS_FALL_THROUGH((void)0); /* fall through */
605 case PASSWORD: NS_FALL_THROUGH((void)0); /* fall through */
606 case USER: NS_FALL_THROUGH((void)0); /* fall through */
607 case DATASOURCE: NS_FALL_THROUGH((void)0); /* fall through */
608 case DISCONNECT: NS_FALL_THROUGH((void)0); /* fall through */
609 case DBTYPE: NS_FALL_THROUGH((void)0); /* fall through */
610 case DRIVER: NS_FALL_THROUGH((void)0); /* fall through */
611 case CANCEL: NS_FALL_THROUGH((void)0); /* fall through */
612 case BINDROW: NS_FALL_THROUGH((void)0); /* fall through */
613 case FLUSH: NS_FALL_THROUGH((void)0); /* fall through */
614 case RELEASEHANDLE: NS_FALL_THROUGH((void)0); /* fall through */
615 case RESETHANDLE: NS_FALL_THROUGH((void)0); /* fall through */
616 case CONNECTED: NS_FALL_THROUGH((void)0); /* fall through */
617 case SP_EXEC: NS_FALL_THROUGH((void)0); /* fall through */
618 case SP_GETPARAMS: NS_FALL_THROUGH((void)0); /* fall through */
619 case SP_RETURNCODE: NS_FALL_THROUGH((void)0); /* fall through */
620 case SESSIONID:
621
622 if (objc < 3) {
623 Tcl_WrongNumArgs(interp, 2, objv, "dbId");
624 return TCL_ERROR1;
625 }
626 if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[2]), &handlePtr, &hPtr) != TCL_OK0) {
627 return TCL_ERROR1;
628 }
629 Ns_DStringFreeTcl_DStringFree(&handlePtr->dsExceptionMsg);
630 handlePtr->cExceptionCode[0] = '\0';
631
632 /*
633 * The following commands require just the handle.
634 */
635
636 switch (cmd) {
637 case POOLNAME:
Duplicate code detected
638 Tcl_SetObjResult(interp, Tcl_NewStringObj(handlePtr->poolname, -1));
639 break;
640
641 case PASSWORD:
Similar code here
642 Tcl_SetObjResult(interp, Tcl_NewStringObj(handlePtr->password, -1));
643 break;
644
645 case USER:
646 Tcl_SetObjResult(interp, Tcl_NewStringObj(handlePtr->user, -1));
647 break;
648
649 case DATASOURCE:
650 Tcl_SetObjResult(interp, Tcl_NewStringObj(handlePtr->datasource, -1));
651 break;
652
653 case DISCONNECT:
654 NsDbDisconnect(handlePtr);
655 break;
656
657 case DBTYPE:
658 Tcl_SetObjResult(interp, Tcl_NewStringObj(Ns_DbDriverDbType(handlePtr), -1));
659 break;
660
661 case DRIVER:
662 Tcl_SetObjResult(interp, Tcl_NewStringObj(Ns_DbDriverName(handlePtr), -1));
663 break;
664
665 case CANCEL:
666 if (Ns_DbCancel(handlePtr) != NS_OK) {
667 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
668 }
669 break;
670
671 case BINDROW:
672 rowPtr = Ns_DbBindRow(handlePtr);
673 if (rowPtr == NULL((void*)0)) {
674 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
675
676 } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_STATIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_STATIC
) != 0), 0))
) {
677 result = TCL_ERROR1;
678 }
679 break;
680
681 case ROWCOUNT:
682 Tcl_SetObjResult(interp, Tcl_NewIntObj(Ns_DbGetRowCount(handlePtr)));
683 break;
684
685 case FLUSH:
686 if (Ns_DbFlush(handlePtr) != NS_OK) {
687 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
688 }
689 break;
690
691 case RELEASEHANDLE:
692 Tcl_DeleteHashEntry(hPtr);
693 Ns_DbPoolPutHandle(handlePtr);
694 break;
695
696 case RESETHANDLE:
697 if (Ns_DbResetHandle(handlePtr) != NS_OK) {
698 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
699 } else {
700 Tcl_SetObjResult(interp, Tcl_NewIntObj(NS_OK));
701 }
702 break;
703
704 case CONNECTED:
705 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(handlePtr->connected)Tcl_NewIntObj((handlePtr->connected)!=0));
706 break;
707
708 case SESSIONID:
709 {
710 char idstr[TCL_INTEGER_SPACE24 + 4];
711 int length;
712
713 memcpy(idstr, "sid", 3u);
714 length = ns_uint64toa(&idstr[3], (uint64_t)NsDbGetSessionId(handlePtr));
715 Tcl_SetObjResult(interp, Tcl_NewStringObj(idstr, length + 3));
716 }
717 break;
718
719 case SP_EXEC:
720 switch (Ns_DbSpExec(handlePtr)) {
721 case NS_DML:
722 Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_DML", 6));
723 break;
724 case NS_ROWS:
725 Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_ROWS", 7));
726 break;
727 default:
728 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
729 }
730 break;
731
732 case SP_GETPARAMS:
733 rowPtr = Ns_DbSpGetParams(handlePtr);
734 if (rowPtr == NULL((void*)0)) {
735 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
736
737 } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC
) != 0), 0))
) {
738 result = TCL_ERROR1;
739 }
740 break;
741
742 case SP_RETURNCODE:
743 if (Ns_DbSpReturnCode(handlePtr, tmpbuf, 32) != NS_OK) {
744 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
745 } else {
746 Tcl_SetObjResult(interp, Tcl_NewStringObj(tmpbuf, -1));
747 }
748 break;
749
750 default:
751 /* should not happen */
752 assert(cmd && 0)((void) (0));
753 }
754 break;
755
756 case DML: NS_FALL_THROUGH((void)0); /* fall through */
757 case GETROW: NS_FALL_THROUGH((void)0); /* fall through */
758 case ONE_ROW: NS_FALL_THROUGH((void)0); /* fall through */
759 case ZERO_OR_ONE_ROW: NS_FALL_THROUGH((void)0); /* fall through */
760 case EXEC: NS_FALL_THROUGH((void)0); /* fall through */
761 case SELECT: NS_FALL_THROUGH((void)0); /* fall through */
762 case SP_START: NS_FALL_THROUGH((void)0); /* fall through */
763 case INTERPRETSQLFILE:
764 {
765 const char *value;
766 int valueLength = 0;
767 Tcl_DString ds;
768
769 /*
770 * The following commands require a 3rd argument.
771 */
772
773 if (objc != 4) {
774 if (cmd == INTERPRETSQLFILE) {
775 Tcl_WrongNumArgs(interp, 2, objv, "dbId sqlfile");
776
777 } else if (cmd == GETROW) {
778 Tcl_WrongNumArgs(interp, 2, objv, "dbId row");
779
780 } else {
781 Tcl_WrongNumArgs(interp, 2, objv, "dbId sql");
782 }
783 return TCL_ERROR1;
784 }
785
786 if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[2]), &handlePtr, &hPtr) != TCL_OK0) {
787 return TCL_ERROR1;
788 }
789 Ns_DStringFreeTcl_DStringFree(&handlePtr->dsExceptionMsg);
790 handlePtr->cExceptionCode[0] = '\0';
791 value = Tcl_GetStringFromObj(objv[3], &valueLength);
792
793 /*
794 * Convert data to external UTF-8... and lets hope, the
795 * driver can handle UTF-8. In case it does not, we might
796 * have to tailor this functionality for certain drivers
797 * (would need additional configuration options).
798 */
799 Tcl_UtfToExternalDString(NULL((void*)0), value, valueLength, &ds);
800 value = ds.string;
801
802 /*if (cmd != GETROW) {
803 fprintf(stderr, "CMD %s: <%s> (%s)\n", Tcl_GetString(objv[1]), value,
804 objv[3]->typePtr ? objv[3]->typePtr->name : "none");
805 }*/
806
807 switch (cmd) {
808 case DML:
809 if (Ns_DbDML(handlePtr, value) != NS_OK) {
810 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
811 }
812 break;
813
814 case ONE_ROW:
815 rowPtr = Ns_Db1Row(handlePtr, value);
816 if (rowPtr == NULL((void*)0)) {
817 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
818
819 } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC
) != 0), 0))
) {
820 result = TCL_ERROR1;
821 }
822 break;
823
824 case ZERO_OR_ONE_ROW:
825 rowPtr = Ns_Db0or1Row(handlePtr, value, &nrows);
826 if (rowPtr == NULL((void*)0)) {
827 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
828
829 } else if (nrows == 0) {
830 Ns_SetFree(rowPtr);
831
832 } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_DYNAMIC
) != 0), 0))
) {
833 result = TCL_ERROR1;
834 }
835 break;
836
837 case EXEC:
838 switch (Ns_DbExec(handlePtr, value)) {
839 case NS_DML:
840 Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_DML", 6));
841 break;
842 case NS_ROWS:
843 Tcl_SetObjResult(interp, Tcl_NewStringObj("NS_ROWS", 7));
844 break;
845 default:
846 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
847 }
848 break;
849
850 case SELECT:
851 rowPtr = Ns_DbSelect(handlePtr, value);
852 if (rowPtr == NULL((void*)0)) {
853 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
854
855 } else if (unlikely(Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_STATIC) != TCL_OK)(__builtin_expect((Ns_TclEnterSet(interp, rowPtr, NS_TCL_SET_STATIC
) != 0), 0))
) {
856 result = TCL_ERROR1;
857 }
858 break;
859
860 case SP_START:
861 if (Ns_DbSpStart(handlePtr, value) != NS_OK) {
862 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
863 } else {
864 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
865 }
866 break;
867
868 case INTERPRETSQLFILE:
869 if (Ns_DbInterpretSqlFile(handlePtr, value) != NS_OK) {
870 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
871 }
872 break;
873
874
875 case GETROW:
876 if (Ns_TclGetSet2(interp, value, &rowPtr) != TCL_OK0) {
877 result = TCL_ERROR1;
878 } else {
879 switch (Ns_DbGetRow(handlePtr, rowPtr)) {
880 case NS_OK:
881 Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
882 break;
883 case NS_END_DATA:
884 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
885 break;
886 default:
887 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
888 }
889 }
890 break;
891
892 default:
893 /* should not happen */
894 assert(cmd && 0)((void) (0));
895 }
896
897 Tcl_DStringFree(&ds);
898 }
899 break;
900
901 case VERBOSE:
902 {
903 int verbose = 0;
904 char *idString;
905 Ns_ObjvSpec args[] = {
906 {"dbID", Ns_ObjvString, &idString, NULL((void*)0)},
907 {"?verbose", Ns_ObjvBool, &verbose, NULL((void*)0)},
908 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
909 };
910
911 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
912 result = TCL_ERROR1;
913
914 } else if (DbGetHandle(idataPtr, interp, idString, &handlePtr, NULL((void*)0)) != TCL_OK0) {
915 result = TCL_ERROR1;
916
917 } else {
918 assert(handlePtr != NULL)((void) (0));
919 Ns_LogDeprecated(objv, 2, "ns_logctl debug(sql) ...", NULL((void*)0));
920
921 if (objc == 4) {
922 handlePtr->verbose = verbose;
923 (void) Ns_LogSeveritySetEnabled(Ns_LogSqlDebug, (bool_Bool)verbose);
924 }
925
926 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(handlePtr->verbose)Tcl_NewIntObj((handlePtr->verbose)!=0));
927 }
928 }
929 break;
930
931 case SETEXCEPTION:
932 if (objc != 5) {
933 Tcl_WrongNumArgs(interp, 2, objv, "dbId code message");
934 result = TCL_ERROR1;
935
936 } else if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[2]), &handlePtr, NULL((void*)0)) != TCL_OK0) {
937 result = TCL_ERROR1;
938
939 } else {
940 const char *code;
941 int codeLen;
942
943 assert(handlePtr != NULL)((void) (0));
944
945 code = Tcl_GetStringFromObj(objv[3], &codeLen);
946 if (codeLen > 5) {
947 Ns_TclPrintfResult(interp, "code \"%s"" more than 5 characters", code);
948 result = TCL_ERROR1;
949 } else {
950 Ns_DbSetException(handlePtr, code, Tcl_GetString(objv[4]));
951 }
952 }
953 break;
954
955 case SP_SETPARAM:
956 if (objc != 7) {
957 Tcl_WrongNumArgs(interp, 2, objv, "dbId paramname type in|out value");
958 result = TCL_ERROR1;
959 } else {
960 const char *arg5 = Tcl_GetString(objv[5]);
961
962 if (!STREQ(arg5, "in")(((*(arg5)) == (*("in"))) && (strcmp((arg5),("in")) ==
0))
&& !STREQ(arg5, "out")(((*(arg5)) == (*("out"))) && (strcmp((arg5),("out"))
== 0))
) {
963 Ns_TclPrintfResult(interp, "inout parameter of setparam must "
964 "be \"in\" or \"out\"");
965 result = TCL_ERROR1;
966
967 } else if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[2]), &handlePtr, NULL((void*)0)) != TCL_OK0) {
968 result = TCL_ERROR1;
969
970 } else {
971 assert(handlePtr != NULL)((void) (0));
972
973 if (Ns_DbSpSetParam(handlePtr,
974 Tcl_GetString(objv[3]),
975 Tcl_GetString(objv[4]),
976 arg5,
977 Tcl_GetString(objv[6])) != NS_OK) {
978 result = DbFail(interp, handlePtr, Tcl_GetString(objv[1]));
979 } else {
980 Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
981 }
982 }
983 }
984 break;
985
986 default:
987 /* should not happen */
988 assert(cmd && 0)((void) (0));
989 }
990
991 return result;
992}
993
994
995/*
996 *----------------------------------------------------------------------
997 * DbErrorCodeObjCmd --
998 *
999 * Implements "ns_dberrorcode".
1000 * Returns database exception code for the database handle.
1001 *
1002 * Results:
1003 * Returns TCL_OK and database exception code is set as Tcl result
1004 * or TCL_ERROR if failure.
1005 *
1006 * Side effects:
1007 * None.
1008 *
1009 *----------------------------------------------------------------------
1010 */
1011
1012static int
1013ErrorObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, char cmd)
1014{
1015 InterpData *idataPtr = clientData;
1016 Ns_DbHandle *handle;
1017 int result = TCL_OK0;
1018
1019 if (objc != 2) {
1020 Tcl_WrongNumArgs(interp, 1, objv, "dbId");
1021 result = TCL_ERROR1;
1022
1023 } else if (DbGetHandle(idataPtr, interp, Tcl_GetString(objv[1]), &handle, NULL((void*)0)) != TCL_OK0) {
1024 result = TCL_ERROR1;
1025
1026 } else {
1027 if (cmd == 'c') {
1028 Tcl_SetObjResult(interp, Tcl_NewStringObj(handle->cExceptionCode, -1));
1029 } else {
1030 Tcl_DStringResult(interp, &handle->dsExceptionMsg);
1031 }
1032 }
1033 return result;
1034}
1035
1036static int
1037DbErrorCodeObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1038{
1039 return ErrorObjCmd(clientData, interp, objc, objv, 'c');
1040}
1041
1042static int
1043DbErrorMsgObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1044{
1045 return ErrorObjCmd(clientData, interp, objc, objv, 'm');
1046}
1047
1048
1049/*
1050 *----------------------------------------------------------------------
1051 * DbConfigPathObjCmd --
1052 *
1053 * Implements "ns_dbconfigpath". Get the database section name
1054 * from the configuration file.
1055 *
1056 * Results:
1057 * TCL_OK and the database section name is set as the Tcl result
1058 * or TCL_ERROR if failure.
1059 *
1060 * Side effects:
1061 * None.
1062 *
1063 *----------------------------------------------------------------------
1064 */
1065
1066static int
1067DbConfigPathObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1068{
1069 int result = TCL_OK0;
1070
1071 if (objc != 1) {
1072 Tcl_WrongNumArgs(interp, 0, objv, NULL((void*)0));
1073 result = TCL_ERROR1;
1074 } else {
1075 const InterpData *idataPtr = clientData;
1076 const char *section = Ns_ConfigSectionPath(NULL((void*)0), idataPtr->server, NULL((void*)0), "db", (char *)0L);
1077
1078 Tcl_SetObjResult(interp, Tcl_NewStringObj(section, -1));
1079 }
1080 return result;
1081}
1082
1083
1084/*
1085 *----------------------------------------------------------------------
1086 * PoolDescriptionObjCmd --
1087 *
1088 * Implements "ns_pooldescription". Returns the pool's
1089 * description string.
1090 *
1091 * Results:
1092 * Return TCL_OK and the pool's description string is set as the
1093 * Tcl result string or TCL_ERROR if failure.
1094 *
1095 * Side effects:
1096 * None.
1097 *
1098 *----------------------------------------------------------------------
1099 */
1100
1101static int
1102PoolDescriptionObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1103{
1104 int result = TCL_OK0;
1105
1106 if (objc != 2) {
1107 Tcl_WrongNumArgs(interp, 1, objv, "poolname");
1108 result = TCL_ERROR1;
1109 } else {
1110 Tcl_SetObjResult(interp, Tcl_NewStringObj(Ns_DbPoolDescription(Tcl_GetString(objv[1])), -1));
1111 }
1112
1113 return result;
1114}
1115
1116
1117/*
1118 *----------------------------------------------------------------------
1119 * QuoteListToListObjCmd --
1120 *
1121 * Implements "ns_quotelisttolist". Removes space, \ and '
1122 * characters in a string.
1123 *
1124 * Results:
1125 * TCL_OK and set the stripped string as the Tcl result or TCL_ERROR
1126 * if failure.
1127 *
1128 * Side effects:
1129 * None.
1130 *
1131 *----------------------------------------------------------------------
1132 */
1133
1134static int
1135QuoteListToListObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1136{
1137 int result = TCL_OK0;
1138
1139 if (objc != 2) {
1140 Tcl_WrongNumArgs(interp, 1, objv, "quotelist");
1141 result = TCL_ERROR1;
1142 } else {
1143 const char *quotelist;
1144 bool_Bool inquotes;
1145 Ns_DStringTcl_DString ds;
1146 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
1147
1148 Ns_DStringInitTcl_DStringInit(&ds);
1149 quotelist = Tcl_GetString(objv[1]);
1150 inquotes = NS_FALSE0;
1151
1152 while (*quotelist != '\0') {
1153 if (CHARTYPE(space, *quotelist)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*quotelist
))))] & (unsigned short int) _ISspace))
!= 0 && !inquotes) {
1154 if (ds.length != 0) {
1155 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length));
1156 Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0);
1157 }
1158 while (CHARTYPE(space, *quotelist)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*quotelist
))))] & (unsigned short int) _ISspace))
!= 0) {
1159 quotelist++;
1160 }
1161 } else if (*quotelist == '\\' && (*(quotelist + 1) != '\0')) {
1162 Ns_DStringNAppendTcl_DStringAppend(&ds, quotelist + 1, 1);
1163 quotelist += 2;
1164 } else if (*quotelist == '\'') {
1165 if (inquotes) {
1166 /* Finish element */
1167 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length));
1168 Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0);
1169 inquotes = NS_FALSE0;
1170 } else {
1171 /* Start element */
1172 inquotes = NS_TRUE1;
1173 }
1174 quotelist++;
1175 } else {
1176 Ns_DStringNAppendTcl_DStringAppend(&ds, quotelist, 1);
1177 quotelist++;
1178 }
1179 }
1180 if (ds.length != 0) {
1181 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(ds.string, ds.length));
1182 }
1183 Ns_DStringFreeTcl_DStringFree(&ds);
1184 Tcl_SetObjResult(interp, listObj);
1185 }
1186 return result;
1187}
1188
1189
1190/*
1191 *----------------------------------------------------------------------
1192 * QuoteSqlValue --
1193 *
1194 * Helper function for QuoteValueObjCmd() and QuoteListObjCmd()
1195 * doing the actual quoting work.
1196 *
1197 * Results:
1198 * Ns_ReturnCode to indicate success (NS_OK or NS_ERROR).
1199 *
1200 * Side effects:
1201 * Updates dsPtr by appending the value.
1202 *
1203 *----------------------------------------------------------------------
1204 */
1205static Ns_ReturnCode
1206QuoteSqlValue(Tcl_DString *dsPtr, Tcl_Obj *valueObj, int valueType)
1207{
1208 int valueLength;
1209 const char *valueString;
1210 Ns_ReturnCode result = NS_OK;
1211
1212 NS_NONNULL_ASSERT(dsPtr != NULL)((void) (0));
1213 NS_NONNULL_ASSERT(valueObj != NULL)((void) (0));
1214
1215 valueString = Tcl_GetStringFromObj(valueObj, &valueLength);
1216
1217 if (valueObj->typePtr == intTypePtr) {
1218 /*
1219 * Since we can trust the byterep, we can bypass the expensive
1220 * Tcl_UtfToExternalDString check.
1221 */
1222 if (valueType == INTCHAR('n')((int)((unsigned char)(('n'))))) {
1223 Tcl_DStringAppend(dsPtr, valueString, valueLength);
1224 } else {
1225 Tcl_DStringAppend(dsPtr, "'", 1);
1226 Tcl_DStringAppend(dsPtr, valueString, valueLength);
1227 Tcl_DStringAppend(dsPtr, "'", 1);
1228 }
1229 } else {
1230 Tcl_DString ds;
1231 /*
1232 * Protect against potential attacks, e.g. embedded nulls
1233 * appearing after conversion to the external value.
1234 */
1235 Tcl_DStringInit(&ds);
1236 Tcl_UtfToExternalDString(NULL((void*)0), valueString, valueLength, &ds);
1237
1238 if (strlen(ds.string) < (size_t)ds.length) {
1239 result = NS_ERROR;
1240
1241 } else if (valueType == INTCHAR('n')((int)((unsigned char)(('n'))))) {
1242 Tcl_DStringAppend(dsPtr, valueString, valueLength);
1243
1244 } else {
1245 Tcl_DStringAppend(dsPtr, "'", 1);
1246
1247 for (;;) {
1248 const char *p = strchr(valueString, INTCHAR('\'')((int)((unsigned char)(('\'')))));
1249 if (p == NULL((void*)0)) {
1250 Tcl_DStringAppend(dsPtr, valueString, valueLength);
1251 break;
1252 } else {
1253 int length = (int)((p - valueString) + 1);
1254
1255 Tcl_DStringAppend(dsPtr, valueString, length);
1256 Tcl_DStringAppend(dsPtr, "'", 1);
1257 valueString = p+1;
1258 valueLength -= length;
1259 }
1260 }
1261 Tcl_DStringAppend(dsPtr, "'", 1);
1262 }
1263 Tcl_DStringFree(&ds);
1264 }
1265
1266 return result;
1267}
1268
1269/*
1270 *----------------------------------------------------------------------
1271 * QuoteValueObjCmd --
1272 *
1273 * Implements "ns_dbquotevalue".
1274 * Prepare a value string for inclusion in an SQL statement:
1275 * - "" is translated into NULL.
1276 * - All values of any numeric type are left alone.
1277 * - All other values are surrounded by single quotes and any
1278 * single quotes included in the value are escaped (i.e. translated
1279 * into 2 single quotes).
1280 *
1281 * Results:
1282 * Tcl standard result codes.
1283 *
1284 * Side effects:
1285 * Modifying interp result.
1286 *
1287 *----------------------------------------------------------------------
1288 */
1289static int
1290QuoteValueObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1291{
1292 int result, valueType = INTCHAR('q')((int)((unsigned char)(('q'))));
1293 Tcl_Obj *valueObj;
1294 Ns_ObjvSpec args[] = {
1295 {"value", Ns_ObjvObj, &valueObj, NULL((void*)0)},
1296 {"?type", Ns_ObjvIndex, &valueType, (void*)valueTypes},
1297 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1298 };
1299
1300 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
1301 result = TCL_ERROR1;
1302
1303 } else if (*Tcl_GetString(valueObj) == '\0') {
1304 Tcl_SetObjResult(interp, Tcl_NewStringObj("NULL", 4));
1305 result = TCL_OK0;
1306
1307 } else {
1308 Tcl_DString ds;
1309
1310 Tcl_DStringInit(&ds);
1311 if (unlikely(QuoteSqlValue(&ds, valueObj, valueType) == NS_ERROR)(__builtin_expect((QuoteSqlValue(&ds, valueObj, valueType
) == NS_ERROR), 0))
) {
1312 Ns_TclPrintfResult(interp, "input string '%s' contains invalid characters",
1313 Tcl_GetString(valueObj));
1314 Tcl_DStringFree(&ds);
1315 result = TCL_ERROR1;
1316
1317 } else {
1318 Tcl_DStringResult(interp, &ds);
1319 result = TCL_OK0;
1320 }
1321
1322 }
1323 return result;
1324}
1325
1326
1327/*
1328 *----------------------------------------------------------------------
1329 * QuoteListObjCmd --
1330 *
1331 * Implements "ns_dbquotelist".
1332 * Prepare a value string for inclusion in an SQL statement:
1333 * - "" is translated into NULL.
1334 * - All values of any numeric type are left alone.
1335 * - All other values are surrounded by single quotes and any
1336 * single quotes included in the value are escaped (i.e. translated
1337 * into 2 single quotes).
1338 *
1339 * Results:
1340 * Tcl standard result codes.
1341 *
1342 * Side effects:
1343 * Modifying interp result.
1344 *
1345 *----------------------------------------------------------------------
1346 */
1347
1348static int
1349QuoteListObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1350{
1351 int result = TCL_OK0, valueType = INTCHAR('q')((int)((unsigned char)(('q'))));
1352 Tcl_Obj *listObj;
1353 Ns_ObjvSpec args[] = {
1354 {"list", Ns_ObjvObj, &listObj, NULL((void*)0)},
1355 {"?type", Ns_ObjvIndex, &valueType, (void*)valueTypes},
1356 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1357 };
1358
1359 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
1360 result = TCL_ERROR1;
1361
1362 } else {
1363 Tcl_DString ds;
1364 int oc;
1365 Tcl_Obj **ov;
1366
1367 Tcl_DStringInit(&ds);
1368 if (Tcl_ListObjGetElements(interp, listObj, &oc, &ov) == TCL_OK0) {
1369 int i;
1370
1371 for (i = 0; i < oc; i++) {
1372
1373 if (QuoteSqlValue(&ds, ov[i], valueType) != NS_OK) {
1374 Ns_TclPrintfResult(interp, "input string '%s' contains invalid characters",
1375 Tcl_GetString(ov[i]));
1376 result = TCL_ERROR1;
1377 break;
1378
1379 } else {
1380 if (i < oc-1) {
1381 Tcl_DStringAppend(&ds, ",", 1);
1382 }
1383 }
1384 }
1385 if (result == TCL_OK0) {
1386 Tcl_DStringResult(interp, &ds);
1387 } else {
1388 Tcl_DStringFree(&ds);
1389 }
1390
1391 } else {
1392 result = TCL_ERROR1;
1393 }
1394 }
1395 return result;
1396}
1397
1398/*
1399 *----------------------------------------------------------------------
1400 *
1401 * GetCsvObjCmd --
1402 *
1403 * Implements "ns_getcsv". The command reads a single line from a
1404 * CSV file and parses the results into a Tcl list variable.
1405 *
1406 * Results:
1407 * A standard Tcl result.
1408 *
1409 * Side effects:
1410 * One line is read for given open channel.
1411 *
1412 *----------------------------------------------------------------------
1413 */
1414static void
1415FinishElement(Tcl_DString *elemPtr, Tcl_DString *colsPtr, bool_Bool quoted)
1416{
1417 if (!quoted) {
1418 Tcl_DStringAppendElement(colsPtr, Ns_StrTrim(elemPtr->string));
1419 } else {
1420 Tcl_DStringAppendElement(colsPtr, elemPtr->string);
1421 }
1422 Tcl_DStringSetLength(elemPtr, 0);
1423}
1424
1425
1426static int
1427GetCsvObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1428{
1429 int trimUnquoted = 0, result = TCL_OK0;
1430 char *delimiter = (char *)",", *quoteString = (char *)"\"", *fileId, *varName;
1431 Tcl_Channel chan;
1432 Ns_ObjvSpec opts[] = {
1433 {"-delimiter", Ns_ObjvString, &delimiter, NULL((void*)0)},
1434 {"-trim", Ns_ObjvBool, &trimUnquoted, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
1435 {"-quotechar", Ns_ObjvString, &quoteString, NULL((void*)0)},
1436 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
1437 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1438 };
1439 Ns_ObjvSpec args[] = {
1440 {"fileId", Ns_ObjvString, &fileId, NULL((void*)0)},
1441 {"varName", Ns_ObjvString, &varName, NULL((void*)0)},
1442 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1443 };
1444
1445 if (Ns_ParseObjv(opts, args, interp, 1, objc, objv) != NS_OK) {
1446 result = TCL_ERROR1;
1447
1448 } else if (Ns_TclGetOpenChannel(interp, fileId, 0, NS_FALSE0, &chan) == TCL_ERROR1) {
1449 result = TCL_ERROR1;
1450
1451 } else {
1452 int ncols;
1453 bool_Bool inquote, quoted, emptyElement;
1454 const char *p, *value;
1455 Tcl_DString line, cols, elem;
1456 char c, quote = *quoteString;
1457
1458 Tcl_DStringInit(&line);
1459 Tcl_DStringInit(&cols);
1460 Tcl_DStringInit(&elem);
1461
1462 ncols = 0;
1463 inquote = NS_FALSE0;
1464 quoted = NS_FALSE0;
1465 emptyElement = NS_TRUE1;
1466
1467 for (;;) {
1468 if (Tcl_Gets(chan, &line) < 0) {
1469 Tcl_DStringFree(&line);
1470 Tcl_DStringFree(&cols);
1471 Tcl_DStringFree(&elem);
1472
1473 if (Tcl_Eof(chan) == 0) {
1474 Ns_TclPrintfResult(interp, "could not read from %s: %s",
1475 fileId, Tcl_PosixError(interp));
1476 return TCL_ERROR1;
1477 }
1478 Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
1479 return TCL_OK0;
1480 }
1481
1482 p = line.string;
1483 while (*p != '\0') {
1484 c = *p++;
1485 loopstart:
1486 if (inquote) {
1487 if (c == quote) {
1488 c = *p++;
1489 if (c == '\0') {
1490 /*
1491 * Line ends after quote
1492 */
1493 inquote = NS_FALSE0;
1494 break;
1495 }
1496 if (c == quote) {
1497 /*
1498 * We have a quote in the quote.
1499 */
1500 Tcl_DStringAppend(&elem, &c, 1);
1501 } else {
1502 inquote = NS_FALSE0;
1503 goto loopstart;
1504 }
1505 } else {
1506 Tcl_DStringAppend(&elem, &c, 1);
1507 }
1508 } else {
1509 if (c == quote && emptyElement) {
1510 inquote = NS_TRUE1;
1511 quoted = NS_TRUE1;
1512 emptyElement = NS_FALSE0;
1513 } else if (strchr(delimiter, INTCHAR(c)((int)((unsigned char)((c))))) != NULL((void*)0)) {
1514 FinishElement(&elem, &cols, (trimUnquoted ? quoted : 1));
1515 ncols++;
1516 quoted = NS_FALSE0;
1517 emptyElement = NS_TRUE1;
1518 } else {
1519 emptyElement = NS_FALSE0;
1520 if (!quoted) {
1521 Tcl_DStringAppend(&elem, &c, 1);
1522 }
1523 }
1524 }
1525 }
1526 if (inquote) {
1527 Tcl_DStringAppend(&elem, "\n", 1);
1528 Tcl_DStringSetLength(&line, 0);
1529 continue;
1530 }
1531 break;
1532 }
1533
1534 if (!(ncols == 0 && emptyElement)) {
1535 FinishElement(&elem, &cols, (trimUnquoted ? quoted : 1));
1536 ncols++;
1537 }
1538 value = Tcl_SetVar(interp, varName, cols.string, TCL_LEAVE_ERR_MSG)Tcl_SetVar2(interp, varName, ((void*)0), cols.string, 0x200);
1539 Tcl_DStringFree(&line);
1540 Tcl_DStringFree(&cols);
1541 Tcl_DStringFree(&elem);
1542
1543 if (value == NULL((void*)0)) {
1544 result = TCL_ERROR1;
1545 } else {
1546 Tcl_SetObjResult(interp, Tcl_NewIntObj(ncols));
1547 }
1548 }
1549 return result;
1550}
1551
1552
1553/*
1554 *----------------------------------------------------------------------
1555 * DbGetHandle --
1556 *
1557 * Get database handle from its handle id.
1558 *
1559 * Results:
1560 * Return TCL_OK if handle is found or TCL_ERROR otherwise.
1561 *
1562 * Side effects:
1563 * None.
1564 *
1565 *----------------------------------------------------------------------
1566 */
1567
1568static int
1569DbGetHandle(InterpData *idataPtr, Tcl_Interp *interp, const char *handleId, Ns_DbHandle **handle,
1570 Tcl_HashEntry **hPtrPtr)
1571{
1572 Tcl_HashEntry *hPtr;
1573 int result = TCL_OK0;
1574
1575 hPtr = Tcl_FindHashEntry(&idataPtr->dbs, handleId)(*((&idataPtr->dbs)->findProc))(&idataPtr->dbs
, (const char *)(handleId))
;
1576 if (hPtr == NULL((void*)0)) {
1577 Ns_TclPrintfResult(interp, "invalid database id: \"%s\"", handleId);
1578 result = TCL_ERROR1;
1579
1580 } else {
1581 *handle = (Ns_DbHandle *) Tcl_GetHashValue(hPtr)((hPtr)->clientData);
1582 if (hPtrPtr != NULL((void*)0)) {
1583 *hPtrPtr = hPtr;
1584 }
1585 }
1586 return result;
1587}
1588
1589
1590/*
1591 *----------------------------------------------------------------------
1592 * EnterDbHandle --
1593 *
1594 * Enter a database handle and create its handle id.
1595 *
1596 * Results:
1597 * The database handle id is returned as a Tcl result.
1598 *
1599 * Side effects:
1600 * None.
1601 *
1602 *----------------------------------------------------------------------
1603 */
1604
1605static void
1606EnterDbHandle(InterpData *idataPtr, Tcl_Interp *interp, Ns_DbHandle *handle, Tcl_Obj *listObj)
1607{
1608 Tcl_HashEntry *hPtr;
1609 int isNew, next, len;
1610 char buf[100];
1611
1612 NS_NONNULL_ASSERT(idataPtr != NULL)((void) (0));
1613 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1614 NS_NONNULL_ASSERT(handle != NULL)((void) (0));
1615 NS_NONNULL_ASSERT(listObj != NULL)((void) (0));
1616
1617 next = idataPtr->dbs.numEntries;
1618 do {
1619 len = snprintf(buf, sizeof(buf), "nsdb%x", next++)__builtin___snprintf_chk (buf, sizeof(buf), 2 - 1, __builtin_object_size
(buf, 2 > 1), "nsdb%x", next++)
;
1620 hPtr = Tcl_CreateHashEntry(&idataPtr->dbs, buf, &isNew)(*((&idataPtr->dbs)->createProc))(&idataPtr->
dbs, (const char *)(buf), &isNew)
;
1621 } while (isNew == 0);
1622
1623 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj(buf, len));
1624 Tcl_SetHashValue(hPtr, handle)((hPtr)->clientData = (ClientData) (handle));
1625}
1626
1627
1628/*
1629 *----------------------------------------------------------------------
1630 * DbFail --
1631 *
1632 * Common routine that creates database failure message.
1633 *
1634 * Results:
1635 * Return TCL_ERROR and set database failure message as Tcl result.
1636 *
1637 * Side effects:
1638 * None.
1639 *
1640 *----------------------------------------------------------------------
1641 */
1642
1643static int
1644DbFail(Tcl_Interp *interp, Ns_DbHandle *handle, const char *cmd)
1645{
1646 Tcl_DString ds;
1647 NS_NONNULL_ASSERT(handle != NULL)((void) (0));
1648
1649 Tcl_DStringInit(&ds);
1650 Ns_DStringPrintf(&ds, "Database operation \"%s\" failed", cmd);
1651 if (handle->cExceptionCode[0] != '\0') {
1652 Ns_DStringPrintf(&ds, " (exception %s", handle->cExceptionCode);
1653 if (handle->dsExceptionMsg.length > 0) {
1654 Ns_DStringPrintf(&ds, ", \"%s\"", handle->dsExceptionMsg.string);
1655 }
1656 Ns_DStringPrintf(&ds, ")");
1657 }
1658 Tcl_DStringResult(interp, &ds);
1659 NsDbSetActive("dbfail", handle, NS_FALSE0);
1660
1661 return TCL_ERROR1;
1662}
1663
1664
1665/*
1666 *----------------------------------------------------------------------
1667 * FreeData --
1668 *
1669 * Free per-interp data at interp delete time.
1670 *
1671 * Results:
1672 * None.
1673 *
1674 * Side effects:
1675 * None.
1676 *
1677 *----------------------------------------------------------------------
1678 */
1679
1680static void
1681FreeData(ClientData clientData, Tcl_Interp *UNUSED(interp)UNUSED_interp __attribute__((__unused__)))
1682{
1683 InterpData *idataPtr = clientData;
1684
1685 Tcl_DeleteHashTable(&idataPtr->dbs);
1686 ns_free(idataPtr);
1687}
1688
1689/*
1690 * Local Variables:
1691 * mode: c
1692 * c-basic-offset: 4
1693 * fill-column: 70
1694 * indent-tabs-mode: nil
1695 * End:
1696 */