]> git.ozlabs.org Git - ccan/blob - ccan/ntdb/doc/TDB_porting.txt
coroutine: Remove on-stack buffers from testcases
[ccan] / ccan / ntdb / doc / TDB_porting.txt
1 Interface differences between TDB and NTDB.
2
3 - ntdb shares 'struct TDB_DATA' with tdb, but TDB defines the TDB_DATA
4   typedef, whereas ntdb defines NTDB_DATA (ie. both are compatible).
5   If you include both ntdb.h and tdb.h, #include tdb.h first,
6   otherwise you'll get a compile error when tdb.h re-defined struct
7   TDB_DATA.
8
9   Example:
10         #include <tdb.h>
11         #include <ntdb.h>
12
13 - ntdb functions return NTDB_SUCCESS (ie 0) on success, and a negative
14   error on failure, whereas tdb functions returned 0 on success, and
15   -1 on failure.  tdb then used tdb_error() to determine the error;
16   this API is nasty if we ever want to support threads, so is not supported.
17
18   Example:
19         #include <tdb.h>
20         #include <ntdb.h>
21
22         void tdb_example(struct tdb_context *tdb, TDB_DATA key, TDB_DATA d)
23         {
24                 if (tdb_store(tdb, key, d) == -1) {
25                         printf("store failed: %s\n", tdb_errorstr(tdb));
26                 }
27         }
28
29         void ntdb_example(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA d)
30         {
31                 enum NTDB_ERROR e;
32
33                 e = ntdb_store(ntdb, key, d);
34                 if (e) {
35                         printf("store failed: %s\n", ntdb_errorstr(e));
36                 }
37         }
38
39 - ntdb's ntdb_fetch() returns an error, tdb's returned the data directly
40   (or tdb_null, and you were supposed to check tdb_error() to find out why).
41
42   Example:
43         #include <tdb.h>
44         #include <ntdb.h>
45
46         void tdb_example(struct tdb_context *tdb, TDB_DATA key)
47         {
48                 TDB_DATA data;
49
50                 data = tdb_fetch(tdb, key);
51                 if (!data.dptr) {
52                         printf("fetch failed: %s\n", tdb_errorstr(tdb));
53                 }
54         }
55
56         void ntdb_example(struct ntdb_context *ntdb, NTDB_DATA key)
57         {
58                 NTDB_DATA data;
59                 enum NTDB_ERROR e;
60
61                 e = ntdb_fetch(ntdb, key, &data);
62                 if (e) {
63                         printf("fetch failed: %s\n", ntdb_errorstr(e));
64                 }
65         }
66
67 - ntdb's ntdb_nextkey() frees the old key's dptr, in tdb you needed to do
68   this manually.
69
70   Example:
71         #include <tdb.h>
72         #include <ntdb.h>
73
74         void tdb_example(struct tdb_context *tdb)
75         {
76                 TDB_DATA key, next, data;
77
78                 for (key = tdb_firstkey(tdb); key.dptr; key = next) {
79                         printf("Got key!\n");
80                         next = tdb_nextkey(tdb, key);
81                         free(key.dptr);
82                 }
83         }
84
85
86         void ntdb_example(struct ntdb_context *ntdb)
87         {
88                 NTDB_DATA k, data;
89                 enum NTDB_ERROR e;
90
91                 for (e = ntdb_firstkey(ntdb,&k); !e; e = ntdb_nextkey(ntdb,&k))
92                         printf("Got key!\n");
93         }
94
95 - Unlike tdb_open/tdb_open_ex, ntdb_open does not allow NULL names,
96   even for NTDB_INTERNAL dbs, and thus ntdb_name() never returns NULL.
97
98   Example:
99         #include <tdb.h>
100         #include <ntdb.h>
101
102         struct tdb_context *tdb_example(void)
103         {
104                 return tdb_open(NULL, 0, TDB_INTERNAL, O_RDWR, 0);
105         }
106
107         struct ntdb_context *ntdb_example(void)
108         {
109                 return ntdb_open("example", NTDB_INTERNAL, O_RDWR, 0);
110         }
111
112 - ntdb uses a linked list of attribute structures to implement logging and
113   alternate hashes.  tdb used tdb_open_ex, which was not extensible.
114
115   Example:
116         #include <tdb.h>
117         #include <ntdb.h>
118
119         /* Custom hash function */
120         static unsigned int my_tdb_hash_func(TDB_DATA *key)
121         {
122                 return key->dsize;
123         }
124
125         struct tdb_context *tdb_example(void)
126         {
127                 return tdb_open_ex("example.tdb", 0, TDB_DEFAULT,
128                                    O_CREAT|O_RDWR, 0600, NULL, my_hash_func);
129         }
130
131         /* Custom hash function */
132         static unsigned int my_ntdb_hash_func(const void *key, size_t len,
133                                               uint32_t seed, void *data)
134         {
135                 return len;
136         }
137
138         struct ntdb_context *ntdb_example(void)
139         {
140                 union ntdb_attribute hash;
141
142                 hash.base.attr = NTDB_ATTRIBUTE_HASH;
143                 hash.base.next = NULL;
144                 hash.hash.fn = my_ntdb_hash_func;
145                 return ntdb_open("example.ntdb", NTDB_DEFAULT,
146                                    O_CREAT|O_RDWR, 0600, &hash);
147         }
148
149 - tdb's tdb_open/tdb_open_ex took an explicit hash size, defaulting to
150   131.  ntdb's uses an attribute for this, defaulting to 8192.
151
152   Example:
153         #include <tdb.h>
154         #include <ntdb.h>
155
156         struct tdb_context *tdb_example(void)
157         {
158                 return tdb_open("example.tdb", 10007, TDB_DEFAULT,
159                                 O_CREAT|O_RDWR, 0600);
160         }
161
162         struct ntdb_context *ntdb_example(void)
163         {
164                 union ntdb_attribute hashsize;
165
166                 hashsize.base.attr = NTDB_ATTRIBUTE_HASHSIZE;
167                 hashsize.base.next = NULL;
168                 hashsize.hashsize.size = 16384;
169                 return ntdb_open("example.ntdb", NTDB_DEFAULT,
170                                    O_CREAT|O_RDWR, 0600, &hashsize);
171         }
172
173 - ntdb's log function is simpler than tdb's log function.  The string
174   is already formatted, is not terminated by a '\n', and it takes an
175   enum ntdb_log_level not a tdb_debug_level, and which has only three
176   values: NTDB_LOG_ERROR, NTDB_LOG_USE_ERROR and NTDB_LOG_WARNING.
177
178         #include <tdb.h>
179         #include <ntdb.h>
180
181         static void tdb_log(struct tdb_context *tdb,
182                             enum tdb_debug_level level, const char *fmt, ...)
183         {
184                 va_list ap;
185                 const char *name;
186
187                 switch (level) {
188                 case TDB_DEBUG_FATAL:
189                         fprintf(stderr, "FATAL: ");
190                         break;
191                 case TDB_DEBUG_ERROR:
192                         fprintf(stderr, "ERROR: ");
193                         break;
194                 case TDB_DEBUG_WARNING:
195                         fprintf(stderr, "WARNING: ");
196                         break;
197                 case TDB_DEBUG_TRACE:
198                         /* Don't print out tracing. */
199                         return;
200                 }
201
202                 name = tdb_name(tdb);
203                 if (!name) {
204                         name = "unnamed";
205                 }
206
207                 fprintf(stderr, "tdb(%s):", name);
208
209                 va_start(ap, fmt);
210                 vfprintf(stderr, fmt, ap);
211                 va_end(ap);
212         }
213
214         struct tdb_context *tdb_example(void)
215         {
216                 struct tdb_logging_context lctx;
217
218                 lctx.log_fn = tdb_log;
219                 return tdb_open_ex("example.tdb", 0, TDB_DEFAULT,
220                                    O_CREAT|O_RDWR, 0600, &lctx, NULL);
221         }
222
223         static void ntdb_log(struct ntdb_context *ntdb,
224                              enum ntdb_log_level level,
225                              enum NTDB_ERROR ecode,
226                              const char *message,
227                              void *data)
228         {
229                 switch (level) {
230                 case NTDB_LOG_ERROR:
231                         fprintf(stderr, "ERROR: ");
232                         break;
233                 case NTDB_LOG_USE_ERROR:
234                         /* We made a mistake, so abort. */
235                         abort();
236                         break;
237                 case NTDB_LOG_WARNING:
238                         fprintf(stderr, "WARNING: ");
239                         break;
240                 }
241
242                 fprintf(stderr, "ntdb(%s):%s:%s\n",
243                         ntdb_name(ntdb), ntdb_errorstr(ecode), message);
244         }
245
246         struct ntdb_context *ntdb_example(void)
247         {
248                 union ntdb_attribute log;
249
250                 log.base.attr = NTDB_ATTRIBUTE_LOG;
251                 log.base.next = NULL;
252                 log.log.fn = ntdb_log;
253                 return ntdb_open("example.ntdb", NTDB_DEFAULT,
254                                  O_CREAT|O_RDWR, 0600, &log);
255         }
256
257 - ntdb provides ntdb_deq() for comparing two NTDB_DATA, and ntdb_mkdata() for
258   creating an NTDB_DATA.
259
260         #include <tdb.h>
261         #include <ntdb.h>
262
263         void tdb_example(struct tdb_context *tdb)
264         {
265                 TDB_DATA data, key;
266
267                 key.dsize = strlen("hello");
268                 key.dptr = "hello";
269                 data = tdb_fetch(tdb, key);
270                 if (data.dsize == key.dsize
271                     && !memcmp(data.dptr, key.dptr, key.dsize))
272                         printf("key is same as data\n");
273                 }
274                 free(data.dptr);
275         }
276
277         void ntdb_example(struct ntdb_context *ntdb)
278         {
279                 NTDB_DATA data, key;
280
281                 key = ntdb_mkdata("hello", strlen("hello"));
282                 if (ntdb_fetch(ntdb, key, &data) == NTDB_SUCCESS) {
283                         if (ntdb_deq(key, data)) {
284                                 printf("key is same as data\n");
285                         }
286                         free(data.dptr);
287                 }
288         }
289
290 - ntdb's ntdb_parse_record() takes a type-checked callback data
291   pointer, not a void * (though a void * pointer still works).  The
292   callback function is allowed to do read operations on the database,
293   or write operations if you first call ntdb_lockall().  TDB's
294   tdb_parse_record() did not allow any database access within the
295   callback, could crash if you tried.
296
297   Example:
298         #include <tdb.h>
299         #include <ntdb.h>
300
301         static int tdb_parser(TDB_DATA key, TDB_DATA data, void *private_data)
302         {
303                 TDB_DATA *expect = private_data;
304
305                 return data.dsize == expect->dsize
306                         && !memcmp(data.dptr, expect->dptr, data.dsize);
307         }
308
309         void tdb_example(struct tdb_context *tdb, TDB_DATA key, NTDB_DATA d)
310         {
311                 switch (tdb_parse_record(tdb, key, tdb_parser, &d)) {
312                 case -1:
313                         printf("parse failed: %s\n", tdb_errorstr(tdb));
314                         break;
315                 case 0:
316                         printf("data was different!\n");
317                         break;
318                 case 1:
319                         printf("data was same!\n");
320                         break;
321                 }
322         }
323
324         static int ntdb_parser(TDB_DATA key, TDB_DATA data, TDB_DATA *expect)
325         {
326                 return ntdb_deq(data, *expect);
327         }
328
329         void ntdb_example(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA d)
330         {
331                 enum NTDB_ERROR e;
332
333                 e = tdb_parse_record(tdb, key, tdb_parser, &d);
334                 switch (e) {
335                 case 0:
336                         printf("data was different!\n");
337                         break;
338                 case 1:
339                         printf("data was same!\n");
340                         break;
341                 default:
342                         printf("parse failed: %s\n", ntdb_errorstr(e));
343                         break;
344                 }
345         }
346
347 - ntdb does locking on read-only databases (ie. O_RDONLY passed to ntdb_open).
348   tdb did not: use the NTDB_NOLOCK flag if you want to suppress locking.
349
350   Example:
351         #include <tdb.h>
352         #include <ntdb.h>
353
354         struct tdb_context *tdb_example(void)
355         {
356                 return tdb_open("example.tdb", 0, TDB_DEFAULT, O_RDONLY, 0);
357         }
358
359         struct ntdb_context *ntdb_example(void)
360         {
361                 return ntdb_open("example.ntdb", NTDB_NOLOCK, O_RDONLY, NULL);
362         }
363
364 - Failure inside a transaction (such as a lock function failing) does
365   not implicitly cancel the transaction; you still need to call
366   ntdb_transaction_cancel().
367
368         #include <tdb.h>
369         #include <ntdb.h>
370
371         void tdb_example(struct tdb_context *tdb, TDB_DATA key, TDB_DATA d)
372         {
373                 if (tdb_transaction_start(tdb) == -1) {
374                         printf("transaction failed: %s\n", tdb_errorstr(tdb));
375                         return;
376                 }
377
378                 if (tdb_store(tdb, key, d) == -1) {
379                         printf("store failed: %s\n", tdb_errorstr(tdb));
380                         return;
381                 }
382                 if (tdb_transaction_commit(tdb) == -1) {
383                         printf("commit failed: %s\n", tdb_errorstr(tdb));
384                 }
385         }
386
387         void ntdb_example(struct ntdb_context *ntdb, NTDB_DATA key, NTDB_DATA d)
388         {
389                 enum NTDB_ERROR e;
390
391                 e = ntdb_transaction_start(ntdb);
392                 if (e) {
393                         printf("transaction failed: %s\n", ntdb_errorstr(e));
394                         return;
395                 }
396
397                 e = ntdb_store(ntdb, key, d);
398                 if (e) {
399                         printf("store failed: %s\n", ntdb_errorstr(e));
400                         ntdb_transaction_cancel(ntdb);
401                 }
402
403                 e = ntdb_transaction_commit(ntdb);
404                 if (e) {
405                         printf("commit failed: %s\n", ntdb_errorstr(e));
406                 }
407         }
408
409 - There is no NTDB_CLEAR_IF_FIRST flag; it has severe scalability and
410   API problems.  If necessary, you can emulate this by using the open
411   hook and placing a 1-byte lock at offset 4.  If your program forks
412   and exits, you will need to place this lock again in the child before
413   the parent exits.
414
415   Example:
416
417         #include <tdb.h>
418         #include <ntdb.h>
419
420         struct tdb_context *tdb_example(void)
421         {
422                 return tdb_open("example.tdb", 0, TDB_CLEAR_IF_FIRST,
423                                    O_CREAT|O_RDWR, 0600);
424         }
425
426         static enum NTDB_ERROR clear_if_first(int fd, void *unused)
427         {
428                 /* We hold a lock offset 4 always, so we can tell if
429                  * anyone else is. */
430                 struct flock fl;
431
432                 fl.l_type = F_WRLCK;
433                 fl.l_whence = SEEK_SET;
434                 fl.l_start = 4; /* ACTIVE_LOCK */
435                 fl.l_len = 1;
436
437                 if (fcntl(fd, F_SETLK, &fl) == 0) {
438                         /* We must be first ones to open it!  Clear it. */
439                         if (ftruncate(fd, 0) != 0) {
440                                 return NTDB_ERR_IO;
441                         }
442                 }
443                 fl.l_type = F_RDLCK;
444                 if (fcntl(fd, F_SETLKW, &fl) != 0) {
445                         return NTDB_ERR_IO;
446                 }
447                 return NTDB_SUCCESS;
448         }
449
450         struct ntdb_context *ntdb_example(void)
451         {
452                 union ntdb_attribute open_attr;
453
454                 open_attr.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
455                 open_attr.openhook.base.next = NULL;
456                 open_attr.openhook.fn = clear_if_first;
457
458                 return ntdb_open("example.ntdb", NTDB_DEFAULT,
459                                  O_CREAT|O_RDWR, 0600, &open_attr);
460         }
461
462 - ntdb traversals are not reliable if the database is changed during
463   the traversal, ie your traversal may not cover all elements, or may
464   cover elements multiple times.  As a special exception, deleting the
465   current record within ntdb_traverse() is reliable.
466
467 - There is no ntdb_traverse_read, since ntdb_traverse does not hold
468   a lock across the entire traversal anyway.  If you want to make sure
469   that your traversal function does not write to the database, you can
470   set and clear the NTDB_RDONLY flag around the traversal.
471
472 - ntdb does not need tdb_reopen() or tdb_reopen_all().  If you call
473   fork() after during certain operations the child should close the
474   ntdb, or complete the operations before continuing to use the tdb:
475
476         ntdb_transaction_start(): child must ntdb_transaction_cancel()
477         ntdb_lockall(): child must call ntdb_unlockall()
478         ntdb_lockall_read(): child must call ntdb_unlockall_read()
479         ntdb_chainlock(): child must call ntdb_chainunlock()
480         ntdb_parse() callback: child must return from ntdb_parse()
481
482 - ntdb will not open a non-ntdb file, even if O_CREAT is specified.  tdb
483   will overwrite an unknown file in that case.