1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2004-2009 Oracle.  All rights reserved.
5 *
6 * $Id$
7 */
8
9#include "db_config.h"
10#ifdef HAVE_64BIT_TYPES
11
12#include "db_int.h"
13#ifdef HAVE_SYSTEM_INCLUDE_FILES
14#include <tcl.h>
15#endif
16#include "dbinc/tcl_db.h"
17#include "dbinc_auto/sequence_ext.h"
18
19/*
20 * Prototypes for procedures defined later in this file:
21 */
22static int	tcl_SeqClose __P((Tcl_Interp *,
23    int, Tcl_Obj * CONST*, DB_SEQUENCE *, DBTCL_INFO *));
24static int	tcl_SeqGet __P((Tcl_Interp *,
25    int, Tcl_Obj * CONST*, DB_SEQUENCE *));
26static int	tcl_SeqRemove __P((Tcl_Interp *,
27    int, Tcl_Obj * CONST*, DB_SEQUENCE *, DBTCL_INFO *));
28static int	tcl_SeqStat __P((Tcl_Interp *,
29    int, Tcl_Obj * CONST*, DB_SEQUENCE *));
30static int	tcl_SeqGetFlags __P((Tcl_Interp *,
31    int, Tcl_Obj * CONST*, DB_SEQUENCE *));
32
33/*
34 *
35 * PUBLIC: int seq_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
36 *
37 * seq_Cmd --
38 *	Implements the "seq" widget.
39 */
40int
41seq_Cmd(clientData, interp, objc, objv)
42	ClientData clientData;		/* SEQ handle */
43	Tcl_Interp *interp;		/* Interpreter */
44	int objc;			/* How many arguments? */
45	Tcl_Obj *CONST objv[];		/* The argument objects */
46{
47	static const char *seqcmds[] = {
48		"close",
49		"get",
50		"get_cachesize",
51		"get_db",
52		"get_flags",
53		"get_key",
54		"get_range",
55		"remove",
56		"stat",
57		NULL
58	};
59	enum seqcmds {
60		SEQCLOSE,
61		SEQGET,
62		SEQGETCACHESIZE,
63		SEQGETDB,
64		SEQGETFLAGS,
65		SEQGETKEY,
66		SEQGETRANGE,
67		SEQREMOVE,
68		SEQSTAT
69	};
70	DB *dbp;
71	DBT key;
72	DBTCL_INFO *dbip, *ip;
73	DB_SEQUENCE *seq;
74	Tcl_Obj *myobjv[2], *res;
75	db_seq_t min, max;
76	int cmdindex, ncache, result, ret;
77
78	Tcl_ResetResult(interp);
79	seq = (DB_SEQUENCE *)clientData;
80	result = TCL_OK;
81	dbip = NULL;
82	if (objc <= 1) {
83		Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
84		return (TCL_ERROR);
85	}
86	if (seq == NULL) {
87		Tcl_SetResult(interp, "NULL sequence pointer", TCL_STATIC);
88		return (TCL_ERROR);
89	}
90
91	ip = _PtrToInfo((void *)seq);
92	if (ip == NULL) {
93		Tcl_SetResult(interp, "NULL info pointer", TCL_STATIC);
94		return (TCL_ERROR);
95	}
96
97	/*
98	 * Get the command name index from the object based on the dbcmds
99	 * defined above.
100	 */
101	if (Tcl_GetIndexFromObj(interp,
102	    objv[1], seqcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
103		return (IS_HELP(objv[1]));
104
105	res = NULL;
106	switch ((enum seqcmds)cmdindex) {
107	case SEQGETRANGE:
108		ret = seq->get_range(seq, &min, &max);
109		if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
110		    "sequence get_range")) == TCL_OK) {
111			myobjv[0] = Tcl_NewWideIntObj(min);
112			myobjv[1] = Tcl_NewWideIntObj(max);
113			res = Tcl_NewListObj(2, myobjv);
114		}
115		break;
116	case SEQCLOSE:
117		result = tcl_SeqClose(interp, objc, objv, seq, ip);
118		break;
119	case SEQREMOVE:
120		result = tcl_SeqRemove(interp, objc, objv, seq, ip);
121		break;
122	case SEQGET:
123		result = tcl_SeqGet(interp, objc, objv, seq);
124		break;
125	case SEQSTAT:
126		result = tcl_SeqStat(interp, objc, objv, seq);
127		break;
128	case SEQGETCACHESIZE:
129		if (objc != 2) {
130			Tcl_WrongNumArgs(interp, 1, objv, NULL);
131			return (TCL_ERROR);
132		}
133		ret = seq->get_cachesize(seq, &ncache);
134		if ((result = _ReturnSetup(interp, ret,
135		    DB_RETOK_STD(ret), "sequence get_cachesize")) == TCL_OK)
136			res = Tcl_NewIntObj(ncache);
137		break;
138	case SEQGETDB:
139		if (objc != 2) {
140			Tcl_WrongNumArgs(interp, 1, objv, NULL);
141			return (TCL_ERROR);
142		}
143		ret = seq->get_db(seq, &dbp);
144		if (ret == 0 && (dbip = _PtrToInfo((void *)dbp)) == NULL) {
145			Tcl_SetResult(interp,
146			     "NULL db info pointer", TCL_STATIC);
147			return (TCL_ERROR);
148		}
149
150		if ((result = _ReturnSetup(interp, ret,
151		    DB_RETOK_STD(ret), "sequence get_db")) == TCL_OK)
152			res = NewStringObj(dbip->i_name, strlen(dbip->i_name));
153		break;
154	case SEQGETKEY:
155		if (objc != 2) {
156			Tcl_WrongNumArgs(interp, 1, objv, NULL);
157			return (TCL_ERROR);
158		}
159		ret = seq->get_key(seq, &key);
160		if ((result = _ReturnSetup(interp, ret,
161		    DB_RETOK_STD(ret), "sequence get_key")) == TCL_OK)
162			res = Tcl_NewByteArrayObj(
163			    (u_char *)key.data, (int)key.size);
164		break;
165	case SEQGETFLAGS:
166		result = tcl_SeqGetFlags(interp, objc, objv, seq);
167		break;
168	}
169
170	/*
171	 * Only set result if we have a res.  Otherwise, lower functions have
172	 * already done so.
173	 */
174	if (result == TCL_OK && res)
175		Tcl_SetObjResult(interp, res);
176	return (result);
177}
178
179/*
180 * tcl_db_stat --
181 */
182static int
183tcl_SeqStat(interp, objc, objv, seq)
184	Tcl_Interp *interp;		/* Interpreter */
185	int objc;			/* How many arguments? */
186	Tcl_Obj *CONST objv[];		/* The argument objects */
187	DB_SEQUENCE *seq;		/* Database pointer */
188{
189	DB_SEQUENCE_STAT *sp;
190	u_int32_t flag;
191	Tcl_Obj *res, *flaglist, *myobjv[2];
192	int result, ret;
193	char *arg;
194
195	result = TCL_OK;
196	flag = 0;
197
198	if (objc > 3) {
199		Tcl_WrongNumArgs(interp, 2, objv, "?-clear?");
200		return (TCL_ERROR);
201	}
202
203	if (objc == 3) {
204		arg = Tcl_GetStringFromObj(objv[2], NULL);
205		if (strcmp(arg, "-clear") == 0)
206			flag = DB_STAT_CLEAR;
207		else {
208			Tcl_SetResult(interp,
209			    "db stat: unknown arg", TCL_STATIC);
210			return (TCL_ERROR);
211		}
212	}
213
214	_debug_check();
215	ret = seq->stat(seq, &sp, flag);
216	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db stat");
217	if (result == TCL_ERROR)
218		return (result);
219
220	res = Tcl_NewObj();
221	MAKE_WSTAT_LIST("Wait", sp->st_wait);
222	MAKE_WSTAT_LIST("No wait", sp->st_nowait);
223	MAKE_WSTAT_LIST("Current", sp->st_current);
224	MAKE_WSTAT_LIST("Cached", sp->st_value);
225	MAKE_WSTAT_LIST("Max Cached", sp->st_last_value);
226	MAKE_WSTAT_LIST("Min", sp->st_min);
227	MAKE_WSTAT_LIST("Max", sp->st_max);
228	MAKE_STAT_LIST("Cache size", sp->st_cache_size);
229	/*
230	 * Construct a {name {flag1 flag2 ... flagN}} list for the
231	 * seq flags.
232	 */
233	myobjv[0] = NewStringObj("Flags", strlen("Flags"));
234	myobjv[1] =
235	    _GetFlagsList(interp, sp->st_flags, __db_get_seq_flags_fn());
236	flaglist = Tcl_NewListObj(2, myobjv);
237	if (flaglist == NULL) {
238		result = TCL_ERROR;
239		goto error;
240	}
241	if ((result =
242	    Tcl_ListObjAppendElement(interp, res, flaglist)) != TCL_OK)
243		goto error;
244
245	Tcl_SetObjResult(interp, res);
246
247error:	__os_ufree(seq->seq_dbp->env, sp);
248	return (result);
249}
250
251/*
252 * tcl_db_close --
253 */
254static int
255tcl_SeqClose(interp, objc, objv, seq, ip)
256	Tcl_Interp *interp;		/* Interpreter */
257	int objc;			/* How many arguments? */
258	Tcl_Obj *CONST objv[];		/* The argument objects */
259	DB_SEQUENCE *seq;		/* Database pointer */
260	DBTCL_INFO *ip;			/* Info pointer */
261{
262	int result, ret;
263
264	result = TCL_OK;
265	if (objc > 2) {
266		Tcl_WrongNumArgs(interp, 2, objv, "");
267		return (TCL_ERROR);
268	}
269
270	_DeleteInfo(ip);
271	_debug_check();
272
273	ret = seq->close(seq, 0);
274	result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "sequence close");
275	return (result);
276}
277
278/*
279 * tcl_SeqGet --
280 */
281static int
282tcl_SeqGet(interp, objc, objv, seq)
283	Tcl_Interp *interp;		/* Interpreter */
284	int objc;			/* How many arguments? */
285	Tcl_Obj *CONST objv[];		/* The argument objects */
286	DB_SEQUENCE *seq;		/* Sequence pointer */
287{
288	static const char *seqgetopts[] = {
289		"-nosync",
290		"-txn",
291		NULL
292	};
293	enum seqgetopts {
294		SEQGET_NOSYNC,
295		SEQGET_TXN
296	};
297	DB_TXN *txn;
298	Tcl_Obj *res;
299	db_seq_t value;
300	u_int32_t aflag, delta;
301	int i, end, optindex, result, ret;
302	char *arg, msg[MSG_SIZE];
303
304	result = TCL_OK;
305	txn = NULL;
306	aflag = 0;
307
308	if (objc < 3) {
309		Tcl_WrongNumArgs(interp, 2, objv, "?-args? delta");
310		return (TCL_ERROR);
311	}
312
313	/*
314	 * Get the command name index from the object based on the options
315	 * defined above.
316	 */
317	i = 2;
318	end = objc;
319	while (i < end) {
320		if (Tcl_GetIndexFromObj(interp, objv[i], seqgetopts, "option",
321		    TCL_EXACT, &optindex) != TCL_OK) {
322			arg = Tcl_GetStringFromObj(objv[i], NULL);
323			if (arg[0] == '-') {
324				result = IS_HELP(objv[i]);
325				goto out;
326			} else
327				Tcl_ResetResult(interp);
328			break;
329		}
330		i++;
331		switch ((enum seqgetopts)optindex) {
332		case SEQGET_NOSYNC:
333			aflag |= DB_TXN_NOSYNC;
334			break;
335		case SEQGET_TXN:
336			if (i >= end) {
337				Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
338				result = TCL_ERROR;
339				break;
340			}
341			arg = Tcl_GetStringFromObj(objv[i++], NULL);
342			txn = NAME_TO_TXN(arg);
343			if (txn == NULL) {
344				snprintf(msg, MSG_SIZE,
345				    "Get: Invalid txn: %s\n", arg);
346				Tcl_SetResult(interp, msg, TCL_VOLATILE);
347				result = TCL_ERROR;
348			}
349			break;
350		} /* switch */
351		if (result != TCL_OK)
352			break;
353	}
354	if (result != TCL_OK)
355		goto out;
356
357	if (i != objc - 1) {
358		Tcl_SetResult(interp,
359		    "Wrong number of key/data given\n", TCL_STATIC);
360		result = TCL_ERROR;
361		goto out;
362	}
363
364	if ((result = _GetUInt32(interp, objv[objc - 1], &delta)) != TCL_OK)
365		goto out;
366
367	ret = seq->get(seq, txn, (int32_t)delta, &value, aflag);
368	result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret), "sequence get");
369	if (ret == 0) {
370		res = Tcl_NewWideIntObj((Tcl_WideInt)value);
371		Tcl_SetObjResult(interp, res);
372	}
373out:
374	return (result);
375}
376/*
377 */
378static int
379tcl_SeqRemove(interp, objc, objv, seq, ip)
380	Tcl_Interp *interp;		/* Interpreter */
381	int objc;			/* How many arguments? */
382	Tcl_Obj *CONST objv[];		/* The argument objects */
383	DB_SEQUENCE *seq;		/* Sequence pointer */
384	DBTCL_INFO *ip;			/* Info pointer */
385{
386	static const char *seqgetopts[] = {
387		"-nosync",
388		"-txn",
389		NULL
390	};
391	enum seqgetopts {
392		SEQGET_NOSYNC,
393		SEQGET_TXN
394	};
395	DB_TXN *txn;
396	u_int32_t aflag;
397	int i, end, optindex, result, ret;
398	char *arg, msg[MSG_SIZE];
399
400	result = TCL_OK;
401	txn = NULL;
402	aflag = 0;
403
404	_DeleteInfo(ip);
405
406	if (objc < 2) {
407		Tcl_WrongNumArgs(interp, 2, objv, "?-args?");
408		return (TCL_ERROR);
409	}
410
411	/*
412	 * Get the command name index from the object based on the options
413	 * defined above.
414	 */
415	i = 2;
416	end = objc;
417	while (i < end) {
418		if (Tcl_GetIndexFromObj(interp, objv[i], seqgetopts, "option",
419		    TCL_EXACT, &optindex) != TCL_OK) {
420			arg = Tcl_GetStringFromObj(objv[i], NULL);
421			if (arg[0] == '-') {
422				result = IS_HELP(objv[i]);
423				goto out;
424			} else
425				Tcl_ResetResult(interp);
426			break;
427		}
428		i++;
429		switch ((enum seqgetopts)optindex) {
430		case SEQGET_NOSYNC:
431			aflag |= DB_TXN_NOSYNC;
432			break;
433		case SEQGET_TXN:
434			if (i >= end) {
435				Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
436				result = TCL_ERROR;
437				break;
438			}
439			arg = Tcl_GetStringFromObj(objv[i++], NULL);
440			txn = NAME_TO_TXN(arg);
441			if (txn == NULL) {
442				snprintf(msg, MSG_SIZE,
443				    "Remove: Invalid txn: %s\n", arg);
444				Tcl_SetResult(interp, msg, TCL_VOLATILE);
445				result = TCL_ERROR;
446			}
447			break;
448		} /* switch */
449		if (result != TCL_OK)
450			break;
451	}
452	if (result != TCL_OK)
453		goto out;
454
455	ret = seq->remove(seq, txn, aflag);
456	result = _ReturnSetup(interp,
457	    ret, DB_RETOK_DBGET(ret), "sequence remove");
458out:
459	return (result);
460}
461
462/*
463 * tcl_SeqGetFlags --
464 */
465static int
466tcl_SeqGetFlags(interp, objc, objv, seq)
467	Tcl_Interp *interp;		/* Interpreter */
468	int objc;			/* How many arguments? */
469	Tcl_Obj *CONST objv[];		/* The argument objects */
470	DB_SEQUENCE *seq;		/* Sequence pointer */
471{
472	int i, ret, result;
473	u_int32_t flags;
474	char buf[512];
475	Tcl_Obj *res;
476
477	static const struct {
478		u_int32_t flag;
479		char *arg;
480	} seq_flags[] = {
481		{ DB_SEQ_INC, "-inc" },
482		{ DB_SEQ_DEC, "-dec" },
483		{ DB_SEQ_WRAP, "-wrap" },
484		{ 0, NULL }
485	};
486
487	if (objc != 2) {
488		Tcl_WrongNumArgs(interp, 1, objv, NULL);
489		return (TCL_ERROR);
490	}
491
492	ret = seq->get_flags(seq, &flags);
493	if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
494	    "db get_flags")) == TCL_OK) {
495		buf[0] = '\0';
496
497		for (i = 0; seq_flags[i].flag != 0; i++)
498			if (LF_ISSET(seq_flags[i].flag)) {
499				if (strlen(buf) > 0)
500					(void)strncat(buf, " ", sizeof(buf));
501				(void)strncat(
502				    buf, seq_flags[i].arg, sizeof(buf));
503			}
504
505		res = NewStringObj(buf, strlen(buf));
506		Tcl_SetObjResult(interp, res);
507	}
508
509	return (result);
510}
511#endif /* HAVE_64BIT_TYPES */
512