1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1999-2009 Oracle.  All rights reserved.
5 *
6 * $Id$
7 */
8
9#ifndef _DB_TCL_DB_H_
10#define	_DB_TCL_DB_H_
11
12#if defined(__cplusplus)
13extern "C" {
14#endif
15
16#define	MSG_SIZE 100		/* Message size */
17
18enum INFOTYPE {
19    I_DB, I_DBC, I_ENV, I_LOCK, I_LOGC, I_MP, I_NDBM, I_PG, I_SEQ, I_TXN};
20
21#define	MAX_ID		8	/* Maximum number of sub-id's we need */
22#define	DBTCL_PREP	64	/* Size of txn_recover preplist */
23
24#define	DBTCL_DBM	1
25#define	DBTCL_NDBM	2
26
27#define	DBTCL_GETCLOCK		0
28#define	DBTCL_GETLIMIT		1
29#define	DBTCL_GETREQ		2
30
31#define	DBTCL_MUT_ALIGN	0
32#define	DBTCL_MUT_INCR	1
33#define	DBTCL_MUT_MAX	2
34#define	DBTCL_MUT_TAS	3
35
36/*
37 * Why use a home grown package over the Tcl_Hash functions?
38 *
39 * We could have implemented the stuff below without maintaining our
40 * own list manipulation, efficiently hashing it with the available
41 * Tcl functions (Tcl_CreateHashEntry, Tcl_GetHashValue, etc).  I chose
42 * not to do so for these reasons:
43 *
44 * We still need the information below.  Using the hashing only removes
45 * us from needing the next/prev pointers.  We still need the structure
46 * itself because we need more than one value associated with a widget.
47 * We need to keep track of parent pointers for sub-widgets (like cursors)
48 * so we can correctly close.  We need to keep track of individual widget's
49 * id counters for any sub-widgets they may have.  We need to be able to
50 * associate the name/client data outside the scope of the widget.
51 *
52 * So, is it better to use the hashing rather than
53 * the linear list we have now?  I decided against it for the simple reason
54 * that to access the structure would require two calls.  The first is
55 * Tcl_FindHashEntry(table, key) and then, once we have the entry, we'd
56 * have to do Tcl_GetHashValue(entry) to get the pointer of the structure.
57 *
58 * I believe the number of simultaneous DB widgets in existence at one time
59 * is not going to be that large (more than several dozen) such that
60 * linearly searching the list is not going to impact performance in a
61 * noticeable way.  Should performance be impacted due to the size of the
62 * info list, then perhaps it is time to revisit this decision.
63 */
64typedef struct dbtcl_info {
65	LIST_ENTRY(dbtcl_info) entries;
66	Tcl_Interp *i_interp;
67	char *i_name;
68	enum INFOTYPE i_type;
69	union infop {
70		DB *dbp;
71		DBC *dbcp;
72		DB_ENV *envp;
73		DB_LOCK *lock;
74		DB_LOGC *logc;
75		DB_MPOOLFILE *mp;
76		DB_TXN *txnp;
77		void *anyp;
78	} un;
79	union data {
80		int anydata;
81		db_pgno_t pgno;
82		u_int32_t lockid;
83	} und;
84	union data2 {
85		int anydata;
86		int pagesz;
87		DB_COMPACT *c_data;
88	} und2;
89	DBT i_lockobj;
90	FILE *i_err;
91	char *i_errpfx;
92
93	/* Callbacks--Tcl_Objs containing proc names */
94	Tcl_Obj *i_compare;
95	Tcl_Obj *i_dupcompare;
96	Tcl_Obj *i_event;
97	Tcl_Obj *i_hashproc;
98	Tcl_Obj *i_isalive;
99	Tcl_Obj *i_part_callback;
100	Tcl_Obj *i_rep_send;
101	Tcl_Obj *i_second_call;
102
103	/* Environment ID for the i_rep_send callback. */
104	Tcl_Obj *i_rep_eid;
105
106	struct dbtcl_info *i_parent;
107	int	i_otherid[MAX_ID];
108} DBTCL_INFO;
109
110#define	i_anyp un.anyp
111#define	i_dbp un.dbp
112#define	i_dbcp un.dbcp
113#define	i_envp un.envp
114#define	i_lock un.lock
115#define	i_logc un.logc
116#define	i_mp un.mp
117#define	i_pagep un.anyp
118#define	i_txnp un.txnp
119
120#define	i_data und.anydata
121#define	i_pgno und.pgno
122#define	i_locker und.lockid
123#define	i_data2 und2.anydata
124#define	i_pgsz und2.pagesz
125#define	i_cdata und2.c_data
126
127#define	i_envtxnid i_otherid[0]
128#define	i_envmpid i_otherid[1]
129#define	i_envlockid i_otherid[2]
130#define	i_envlogcid i_otherid[3]
131
132#define	i_mppgid  i_otherid[0]
133
134#define	i_dbdbcid i_otherid[0]
135
136extern int __debug_on, __debug_print, __debug_stop, __debug_test;
137
138typedef struct dbtcl_global {
139	LIST_HEAD(infohead, dbtcl_info) g_infohead;
140} DBTCL_GLOBAL;
141#define	__db_infohead __dbtcl_global.g_infohead
142
143extern DBTCL_GLOBAL __dbtcl_global;
144
145/*
146 * Tcl_NewStringObj takes an "int" length argument, when the typical use is to
147 * call it with a size_t length (for example, returned by strlen).  Tcl is in
148 * the wrong, but that doesn't help us much -- cast the argument.
149 */
150#define	NewStringObj(a, b)						\
151	Tcl_NewStringObj(a, (int)b)
152
153#define	NAME_TO_DB(name)	(DB *)_NameToPtr((name))
154#define	NAME_TO_DBC(name)	(DBC *)_NameToPtr((name))
155#define	NAME_TO_ENV(name)	(DB_ENV *)_NameToPtr((name))
156#define	NAME_TO_LOCK(name)	(DB_LOCK *)_NameToPtr((name))
157#define	NAME_TO_MP(name)	(DB_MPOOLFILE *)_NameToPtr((name))
158#define	NAME_TO_TXN(name)	(DB_TXN *)_NameToPtr((name))
159#define	NAME_TO_SEQUENCE(name)	(DB_SEQUENCE *)_NameToPtr((name))
160
161/*
162 * MAKE_STAT_LIST appends a {name value} pair to a result list that MUST be
163 * called 'res' that is a Tcl_Obj * in the local function.  This macro also
164 * assumes a label "error" to go to in the event of a Tcl error.  For stat
165 * functions this will typically go before the "free" function to free the
166 * stat structure returned by DB.
167 */
168#define	MAKE_STAT_LIST(s, v) do {					\
169	result = _SetListElemInt(interp, res, (s), (long)(v));		\
170	if (result != TCL_OK)						\
171		goto error;						\
172} while (0)
173
174#define	MAKE_WSTAT_LIST(s, v) do {					\
175	result = _SetListElemWideInt(interp, res, (s), (int64_t)(v));	\
176	if (result != TCL_OK)						\
177		goto error;						\
178} while (0)
179
180/*
181 * MAKE_STAT_LSN appends a {name {LSNfile LSNoffset}} pair to a result list
182 * that MUST be called 'res' that is a Tcl_Obj * in the local
183 * function.  This macro also assumes a label "error" to go to
184 * in the even of a Tcl error.  For stat functions this will
185 * typically go before the "free" function to free the stat structure
186 * returned by DB.
187 */
188#define	MAKE_STAT_LSN(s, lsn) do {					\
189	myobjc = 2;							\
190	myobjv[0] = Tcl_NewLongObj((long)(lsn)->file);			\
191	myobjv[1] = Tcl_NewLongObj((long)(lsn)->offset);		\
192	lsnlist = Tcl_NewListObj(myobjc, myobjv);			\
193	myobjc = 2;							\
194	myobjv[0] = Tcl_NewStringObj((s), (int)strlen(s));		\
195	myobjv[1] = lsnlist;						\
196	thislist = Tcl_NewListObj(myobjc, myobjv);			\
197	result = Tcl_ListObjAppendElement(interp, res, thislist);	\
198	if (result != TCL_OK)						\
199		goto error;						\
200} while (0)
201
202/*
203 * MAKE_STAT_STRLIST appends a {name string} pair to a result list
204 * that MUST be called 'res' that is a Tcl_Obj * in the local
205 * function.  This macro also assumes a label "error" to go to
206 * in the even of a Tcl error.  For stat functions this will
207 * typically go before the "free" function to free the stat structure
208 * returned by DB.
209 */
210#define	MAKE_STAT_STRLIST(s,s1) do {					\
211	result = _SetListElem(interp, res, (s), (u_int32_t)strlen(s),	\
212	    (s1), (u_int32_t)strlen(s1));				\
213	if (result != TCL_OK)						\
214		goto error;						\
215} while (0)
216
217/*
218 * MAKE_SITE_LIST appends a {eid host port status} tuple to a result list
219 * that MUST be called 'res' that is a Tcl_Obj * in the local function.
220 * This macro also assumes a label "error" to go to in the event of a Tcl
221 * error.
222 */
223#define	MAKE_SITE_LIST(e, h, p, s) do {					\
224	myobjc = 4;							\
225	myobjv[0] = Tcl_NewIntObj(e);					\
226	myobjv[1] = Tcl_NewStringObj((h), (int)strlen(h));		\
227	myobjv[2] = Tcl_NewIntObj((int)p);				\
228	myobjv[3] = Tcl_NewStringObj((s), (int)strlen(s));		\
229	thislist = Tcl_NewListObj(myobjc, myobjv);			\
230	result = Tcl_ListObjAppendElement(interp, res, thislist);	\
231	if (result != TCL_OK)						\
232		goto error;						\
233} while (0)
234
235/*
236 * FLAG_CHECK checks that the given flag is not set yet.
237 * If it is, it sets up an error message.
238 */
239#define	FLAG_CHECK(flag) do {						\
240	if ((flag) != 0) {						\
241		Tcl_SetResult(interp,					\
242		    " Only 1 policy can be specified.\n",		\
243		    TCL_STATIC);					\
244		result = TCL_ERROR;					\
245		break;							\
246	}								\
247} while (0)
248
249/*
250 * FLAG_CHECK2 checks that the given flag is not set yet or is
251 * only set to the given allowed value.
252 * If it is, it sets up an error message.
253 */
254#define	FLAG_CHECK2(flag, val) do {					\
255	if (((flag) & ~(val)) != 0) {					\
256		Tcl_SetResult(interp,					\
257		    " Only 1 policy can be specified.\n",		\
258		    TCL_STATIC);					\
259		result = TCL_ERROR;					\
260		break;							\
261	}								\
262} while (0)
263
264/*
265 * IS_HELP checks whether the arg we bombed on is -?, which is a help option.
266 * If it is, we return TCL_OK (but leave the result set to whatever
267 * Tcl_GetIndexFromObj says, which lists all the valid options.  Otherwise
268 * return TCL_ERROR.
269 */
270#define	IS_HELP(s)						\
271    (strcmp(Tcl_GetStringFromObj(s,NULL), "-?") == 0) ? TCL_OK : TCL_ERROR
272
273#if defined(__cplusplus)
274}
275#endif
276
277#include "dbinc_auto/tcl_ext.h"
278#endif /* !_DB_TCL_DB_H_ */
279