1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * db.cc
24 *
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28
29 #pragma ident "%Z%%M% %I% %E% SMI"
30
31 #include <stdio.h>
32 #include <string.h>
33 #ifdef TDRPC
34 #include <sysent.h>
35 #else
36 #include <unistd.h>
37 #endif
38
39 #include "nisdb_mt.h"
40 #include "db_headers.h"
41 #include "db.h"
42
43 extern db_result *empty_result(db_status);
44 extern int add_to_standby_list(db*);
45 extern int remove_from_standby_list(db*);
46
47 /* for db_next_desc */
48
49 #define LINEAR 1
50 #define CHAINED 2
51
52 struct db_next_info {
53 int next_type; /* linear or chained */
54 void* next_value; /* linear: entryp; */
55 /* chained: db_next_index_desc* */
56 };
57
58
59 /* Constructor: Create a database using the given name, 'dbname.'
60 The database is stored in a file named 'dbname'.
61 The log file is stored in a file named 'dbname'.log.
62 A temporary file 'dbname'.tmp is also used. */
db(char * dbname)63 db::db(char* dbname)
64 {
65 int len = strlen(dbname);
66 dbfilename = new char[len+1];
67 if (dbfilename == NULL)
68 FATAL("db::db: cannot allocate space", DB_MEMORY_LIMIT);
69 logfilename = new char[len+5];
70 if (logfilename == NULL) {
71 delete dbfilename;
72 FATAL("db::db: cannot allocate space", DB_MEMORY_LIMIT);
73 }
74 tmpfilename = new char[len+5];
75 if (tmpfilename == NULL) {
76 delete dbfilename;
77 delete logfilename;
78 FATAL("db::db: cannot allocate space", DB_MEMORY_LIMIT);
79 }
80 sprintf(dbfilename, "%s", dbname);
81 sprintf(logfilename, "%s.log", dbname);
82 sprintf(tmpfilename, "%s.tmp", dbname);
83 logfile = NULL;
84 logfile_opened = FALSE;
85 changed = FALSE;
86 INITRW(db);
87 READLOCKOK(db);
88
89 internal_db.setDbPtr(this);
90 (void) internal_db.configure(dbname);
91 }
92
93 /* destructor: note that associated files should be removed separated */
~db()94 db::~db()
95 {
96 (void)acqexcl();
97 internal_db.reset(); /* clear any associated data structures */
98 delete dbfilename;
99 delete logfilename;
100 delete tmpfilename;
101 close_log();
102 delete logfile;
103 (void)destroylock();
104 }
105
106
107 static void
assign_next_desc(db_next_desc * desc,entryp value)108 assign_next_desc(db_next_desc* desc, entryp value)
109 {
110 db_next_info * store = new db_next_info;
111 if (store == NULL) {
112 desc->db_next_desc_val = NULL;
113 desc->db_next_desc_len = 0;
114 FATAL("db::assign_next_desc: cannot allocate space",
115 DB_MEMORY_LIMIT);
116 }
117
118 store->next_type = LINEAR;
119 store->next_value = (void*)value;
120 desc->db_next_desc_val = (char*) store;
121 desc->db_next_desc_len = sizeof (db_next_info);
122 }
123
124 static void
assign_next_desc(db_next_desc * desc,db_next_index_desc * value)125 assign_next_desc(db_next_desc* desc, db_next_index_desc * value)
126 {
127 db_next_info * store = new db_next_info;
128 if (store == NULL) {
129 desc->db_next_desc_val = NULL;
130 desc->db_next_desc_len = 0;
131 FATAL("db::assign_next_desc: cannot allocate space (2)",
132 DB_MEMORY_LIMIT);
133 }
134 store->next_type = CHAINED;
135 store->next_value = (void*)value;
136 desc->db_next_desc_val = (char*) store;
137 desc->db_next_desc_len = sizeof (db_next_info);
138 }
139
140 static entryp
extract_next_desc(db_next_desc * desc,int * next_type,db_next_index_desc ** place2)141 extract_next_desc(db_next_desc* desc, int *next_type,
142 db_next_index_desc** place2)
143 {
144 entryp place;
145
146 if (desc == NULL || desc->db_next_desc_len != sizeof (db_next_info)) {
147 *next_type = 0;
148 return (0);
149 }
150 *next_type = ((db_next_info*) desc->db_next_desc_val)->next_type;
151 switch (*next_type) {
152 case LINEAR:
153 place = (entryp)
154 ((db_next_info*) desc->db_next_desc_val)->next_value;
155 return (place);
156
157 case CHAINED:
158 *place2 = (db_next_index_desc*)
159 ((db_next_info*) desc->db_next_desc_val) ->next_value;
160 return (0);
161 default:
162 *next_type = 0; // invalid type
163 return (0);
164 }
165 }
166
167 /* Execute the specified action using the rest of the arguments as input.
168 Return a structure db_result containing the result. */
169 db_result *
exec_action(db_action action,db_query * query,entry_object * content,db_next_desc * previous)170 db::exec_action(db_action action, db_query *query,
171 entry_object *content, db_next_desc* previous)
172 {
173 entryp where, prev;
174 db_result *res = new db_result;
175 long num_answers;
176 entry_object_p * ans;
177 entry_object * single;
178 db_next_index_desc *index_desc;
179 int next_type;
180 db_next_index_desc *prev_desc;
181
182 if (res == NULL)
183 FATAL3("db::exec_action: cannot allocate space for result",
184 DB_MEMORY_LIMIT, NULL);
185
186 res->objects.objects_len = 0; /* default */
187 res->objects.objects_val = NULL; /* default */
188
189 switch (action) {
190 case DB_LOOKUP:
191 res->status = internal_db.lookup(query, &num_answers, &ans);
192 res->objects.objects_len = (int) num_answers;
193 res->objects.objects_val = ans;
194 break;
195
196 case DB_ADD:
197 res->status = internal_db.add(query, content);
198 break;
199
200 case DB_REMOVE:
201 res->status = internal_db.remove(query);
202 break;
203
204 case DB_FIRST:
205 if (query == NULL) {
206 res->status = internal_db.first(&where, &single);
207 if (res->status == DB_SUCCESS)
208 assign_next_desc(&(res->nextinfo), where);
209 } else {
210 res->status = internal_db.first(query,
211 &index_desc,
212 &single);
213 if (res->status == DB_SUCCESS)
214 assign_next_desc(&(res->nextinfo), index_desc);
215 }
216 if (res->status == DB_SUCCESS) {
217 res->objects.objects_val = new entry_object_p;
218 if (res->objects.objects_val == NULL) {
219 res->objects.objects_len = 0;
220 delete res;
221 FATAL3(
222 "db::exec_action: cannot allocate space for DB_FIRST result",
223 DB_MEMORY_LIMIT, NULL);
224 }
225 res->objects.objects_len = 1;
226 res->objects.objects_val[0] = single;
227 }
228 break;
229
230 case DB_NEXT:
231 prev = extract_next_desc(previous, &next_type, &prev_desc);
232 switch (next_type) {
233 case LINEAR:
234 if (prev != 0) {
235 res->status = internal_db.next(prev, &where,
236 &single);
237 if (res->status == DB_SUCCESS)
238 assign_next_desc(&(res->nextinfo),
239 where);
240 } else
241 // invalid previous indicator
242 res->status = DB_NOTFOUND;
243 break;
244 case CHAINED:
245 if (prev_desc != NULL) {
246 res->status = internal_db.next(prev_desc,
247 &index_desc, &single);
248 if (res->status == DB_SUCCESS)
249 assign_next_desc(&(res->nextinfo),
250 index_desc);
251 } else
252 // invalid previous indicator
253 res->status = DB_NOTFOUND;
254 break;
255 default:
256 WARNING("db::exec_action: invalid previous indicator");
257 res->status = DB_BADQUERY;
258 }
259 if (previous && previous->db_next_desc_val) {
260 delete previous->db_next_desc_val;
261 previous->db_next_desc_len = 0;
262 previous->db_next_desc_val = NULL;
263 }
264 if (res->status == DB_SUCCESS) {
265 res->objects.objects_len = 1;
266 res->objects.objects_val = new entry_object_p;
267 if (res->objects.objects_val == NULL) {
268 res->objects.objects_len = 0;
269 delete res;
270 FATAL3(
271 "db::exec_action: cannot allocate space for DB_NEXT result",
272 DB_MEMORY_LIMIT, NULL);
273 }
274 res->objects.objects_val[0] = single;
275 }
276 break;
277
278 case DB_RESET_NEXT:
279 prev = extract_next_desc(previous, &next_type, &prev_desc);
280 switch (next_type) {
281 case LINEAR:
282 res->status = DB_SUCCESS;
283 if (previous->db_next_desc_val) {
284 delete previous->db_next_desc_val;
285 previous->db_next_desc_len = 0;
286 previous->db_next_desc_val = NULL;
287 }
288 break; // do nothing
289 case CHAINED:
290 res->status = internal_db.reset_next(prev_desc);
291 if (previous->db_next_desc_val) {
292 delete previous->db_next_desc_val;
293 previous->db_next_desc_len = 0;
294 previous->db_next_desc_val = NULL;
295 }
296 break;
297 default:
298 WARNING("db::exec_action: invalid previous indicator");
299 res->status = DB_BADQUERY;
300 }
301 break;
302
303 case DB_ALL:
304 res->status = internal_db.all(&num_answers, &ans);
305 res->objects.objects_len = (int) num_answers;
306 res->objects.objects_val = ans;
307 break;
308
309 default:
310 WARNING("unknown request");
311 res->status = DB_BADQUERY;
312 return (res);
313 }
314 return (res);
315 }
316
317 /*
318 * Log the given action and execute it.
319 * The minor version of the database is updated after the action has
320 * been executed and the database is flagged as being changed.
321 * Return the structure db_result, or NULL if the logging failed or the
322 * action is unknown.
323 */
324 db_result *
log_action(db_action action,db_query * query,entry_object * content)325 db::log_action(db_action action, db_query *query, entry_object *content)
326 {
327 vers *v = internal_db.get_version()->nextminor();
328 db_result * res;
329 db_log_entry le(action, v, query, content);
330 bool_t copylog = FALSE;
331
332 WRITELOCK(this, empty_result(DB_LOCK_ERROR), "w db::log_action");
333 /*
334 * If this is a synchronous operation on the master we should
335 * not copy the log for each operation. Doing so causes
336 * massive disk IO that hampers the performance of these operations.
337 * Where as on the replica these operations are not synchronous
338 * (batched) and don't affect the performance as much.
339 */
340
341 if ((action == DB_ADD_NOSYNC) || (action == DB_REMOVE_NOSYNC))
342 copylog = TRUE;
343
344 if (open_log(copylog) < 0) {
345 delete v;
346 WRITEUNLOCK(this, empty_result(DB_LOCK_ERROR),
347 "wu db::log_action DB_STORAGE_LIMIT");
348 return (empty_result(DB_STORAGE_LIMIT));
349 }
350
351 if (logfile->append(&le) < 0) {
352 close_log();
353 WARNING_M("db::log_action: could not add log entry: ");
354 delete v;
355 WRITEUNLOCK(this, empty_result(DB_LOCK_ERROR),
356 "wu db::log_action DB_STORAGE_LIMIT");
357 return (empty_result(DB_STORAGE_LIMIT));
358 }
359
360 switch (action) {
361 case DB_ADD_NOSYNC:
362 action = DB_ADD;
363 break;
364 case DB_REMOVE_NOSYNC:
365 action = DB_REMOVE;
366 break;
367 default:
368 if (logfile->sync_log() < 0) {
369 close_log();
370 WARNING_M("db::log_action: could not add log entry: ");
371 delete v;
372 WRITEUNLOCK(this, empty_result(DB_LOCK_ERROR),
373 "wu db::log_action DB_STORAGE_LIMIT");
374 return (empty_result(DB_STORAGE_LIMIT));
375 }
376 break;
377 }
378 res = exec_action(action, query, content, NULL);
379 internal_db.change_version(v);
380 delete v;
381 changed = TRUE;
382 WRITEUNLOCK(this, empty_result(DB_LOCK_ERROR), "wu db::log_action");
383
384 return (res);
385 }
386
387 /*
388 * Execute 'action' using the rest of the arguments as input.
389 * Return the result of the operation in a db_result structure;
390 * Return NULL if the request is unknown.
391 * If the action involves updates (ADD and REMOVE), it is logged first.
392 */
393 db_result *
execute(db_action action,db_query * query,entry_object * content,db_next_desc * previous)394 db::execute(db_action action, db_query *query,
395 entry_object *content, db_next_desc* previous)
396 {
397 db_result *res;
398
399 switch (action) {
400 case DB_LOOKUP:
401 case DB_FIRST:
402 case DB_NEXT:
403 case DB_ALL:
404 case DB_RESET_NEXT:
405 READLOCK(this, empty_result(DB_LOCK_ERROR), "r db::execute");
406 res = exec_action(action, query, content, previous);
407 READUNLOCK(this, empty_result(DB_LOCK_ERROR),
408 "ru db::execute");
409 return (res);
410
411 case DB_ADD_NOLOG:
412 WRITELOCK(this, empty_result(DB_LOCK_ERROR), "w db::execute");
413 changed = TRUE;
414 res = exec_action(DB_ADD, query, content, previous);
415 WRITEUNLOCK(this, empty_result(DB_LOCK_ERROR),
416 "wu db::execute");
417 return (res);
418
419 case DB_ADD:
420 case DB_REMOVE:
421 case DB_ADD_NOSYNC:
422 case DB_REMOVE_NOSYNC:
423 /* log_action() will do the locking */
424 return (log_action(action, query, content));
425
426 default:
427 WARNING("db::execute: unknown request");
428 return (empty_result(DB_INTERNAL_ERROR));
429 }
430 }
431
432 /* close existing logfile and delete its structure */
433 int
reset_log()434 db::reset_log()
435 {
436 WRITELOCK(this, -1, "w db::reset_log");
437 /* try to close old log file */
438 /* doesnot matter since we do synchronous writes only */
439 if (logfile != NULL) {
440 if (logfile_opened == TRUE) {
441 logfile->sync_log();
442 if (logfile->close() < 0) {
443 WARNING_M("db::reset_log: could not close log file: ");
444 }
445 remove_from_standby_list(this);
446 }
447 delete logfile;
448 logfile = NULL;
449 }
450 logfile_opened = FALSE;
451 WRITEUNLOCK(this, -1, "wu db::reset_log");
452 return (0);
453 }
454
455 /* close existing logfile, but leave its structure if exists */
456 int
close_log(int bypass_standby)457 db::close_log(int bypass_standby)
458 {
459 WRITELOCK(this, -1, "w db::close_log");
460 if (logfile != NULL && logfile_opened == TRUE) {
461 logfile->sync_log();
462 logfile->close();
463 if (!bypass_standby)
464 remove_from_standby_list(this);
465 }
466 logfile_opened = FALSE;
467 WRITEUNLOCK(this, -1, "wu db::close_log");
468 return (0);
469 }
470
471 /* open logfile, creating its structure if it does not exist */
472 int
open_log(bool_t copylog)473 db::open_log(bool_t copylog)
474 {
475 WRITELOCK(this, -1, "w db::open_log");
476 if (logfile == NULL) {
477 if ((logfile = new db_log(logfilename, PICKLE_APPEND))
478 == NULL)
479 FATAL3("db::reset_log: cannot allocate space",
480 DB_MEMORY_LIMIT, -1);
481 }
482
483 if (logfile_opened == TRUE) {
484 WRITEUNLOCK(this, -1, "wu db::open_log");
485 return (0);
486 }
487
488 logfile->copylog = copylog;
489
490 if ((logfile->open()) == NULL){
491 WARNING_M("db::open_log: could not open log file: ");
492 delete logfile;
493 logfile = NULL;
494 WRITEUNLOCK(this, -1, "wu db::open_log");
495 return (-1);
496 }
497 add_to_standby_list(this);
498 logfile_opened = TRUE;
499 WRITEUNLOCK(this, -1, "wu db::open_log");
500 return (0);
501 }
502
503 /*
504 * Execute log entry 'j' on the database identified by 'dbchar' if the
505 * version of j is later than that of the database. If 'j' is executed,
506 * 'count' is incremented and the database's verison is updated to that of 'j'.
507 * Returns TRUE always for valid log entries; FALSE otherwise.
508 */
509 static bool_t
apply_log_entry(db_log_entry * j,char * dbchar,int * count)510 apply_log_entry(db_log_entry * j, char * dbchar, int *count)
511 {
512 db_mindex * db = (db_mindex *) dbchar;
513 bool_t status = TRUE;
514
515 WRITELOCK(db, FALSE, "db::apply_log_entry");
516
517 if (db->get_version()->earlier_than(j->get_version())) {
518 ++ *count;
519 #ifdef DEBUG
520 j->print();
521 #endif /* DEBUG */
522 switch (j->get_action()) {
523 case DB_ADD:
524 case DB_ADD_NOSYNC:
525 db->add(j->get_query(), j->get_object());
526 break;
527
528 case DB_REMOVE:
529 case DB_REMOVE_NOSYNC:
530 db->remove(j->get_query());
531 break;
532
533 default:
534 WARNING("db::apply_log_entry: unknown action_type");
535 WRITEUNLOCK(db, FALSE, "db::apply_log_entry");
536 return (FALSE);
537 }
538 db->change_version(j->get_version());
539 }
540
541 WRITEUNLOCK(db, FALSE, "db::apply_log_entry");
542
543 return (TRUE); /* always want to TRUE if action valid ? */
544 }
545
546 /*
547 * Execute log entry 'j' on this db. 'j' is executed if its version is
548 * later than that of the database; if executed, the database's version
549 * will be changed to that of 'j', regardless of the status of the operation.
550 * Returns TRUE if 'j' was executed; FALSE if it was not.
551 * Log entry is added to this database's log if log_entry is applied.
552 */
553 bool_t
execute_log_entry(db_log_entry * j)554 db::execute_log_entry(db_log_entry *j)
555 {
556 int count = 0;
557 apply_log_entry (j, (char *) &internal_db, &count);
558 bool_t copylog = FALSE;
559 db_action action;
560
561 /*
562 * If this is a synchronous operation on the master we should
563 * not copy the log for each operation. Doing so causes
564 * massive disk IO that hampers the performance of these operations.
565 * Where as on the replica these operations are not synchronous
566 * (batched) and don't affect the performance as much.
567 */
568
569 action = j->get_action();
570 if ((action == DB_ADD_NOSYNC) || (action == DB_REMOVE_NOSYNC))
571 copylog = TRUE;
572
573 /*
574 * should really record the log entry first, but can''t do that without
575 * knowing whether the log entry is applicable.
576 */
577 WRITELOCK(this, FALSE, "w db::execute_log_entry");
578 if (count == 1) {
579 if (open_log(copylog) < 0) {
580 WRITEUNLOCK(this, FALSE, "wu db::execute_log_entry");
581 return (FALSE);
582 }
583
584 if (logfile->append(j) < 0) {
585 close_log();
586 WARNING_M(
587 "db::execute_log_entry: could not add log entry: ");
588 WRITEUNLOCK(this, FALSE, "wu db::execute_log_entry");
589 return (FALSE);
590 }
591 // close_log(); /* do this asynchronously */
592 }
593 WRITEUNLOCK(this, FALSE, "wu db::execute_log_entry");
594
595 return (count == 1);
596 }
597
598 /* Incorporate updates in log to database already loaded.
599 Does not affect "logfile" */
600 int
incorporate_log(char * filename)601 db::incorporate_log(char* filename)
602 {
603 db_log f(filename, PICKLE_READ);
604 int ret;
605
606 WRITELOCK(this, -1, "w db::incorporate_log");
607 WRITELOCK2((&internal_db), -1, "w internal_db db::incorporate_log",
608 this);
609 internal_db.setNoWriteThrough();
610 ret = f.execute_on_log(&(apply_log_entry), (char *) &internal_db);
611 internal_db.clearNoWriteThrough();
612 WRITEUNLOCK2(this, (&internal_db), ret, ret,
613 "wu db::incorporate_log",
614 "wu mindex db::incorporate_log");
615 return (ret);
616 }
617
618 /* Load database and incorporate any logged updates into the loaded copy.
619 Return TRUE if load succeeds; FALSE otherwise. */
620 bool_t
load()621 db::load()
622 {
623 int count;
624 int load_status;
625
626 WRITELOCK(this, FALSE, "w db::load");
627 if (changed == TRUE)
628 syslog(LOG_ERR,
629 "WARNING: the current db '%s' has been changed but not checkpointed",
630 dbfilename);
631
632 unlink(tmpfilename); /* get rid of partial checkpoints */
633
634 if ((load_status = internal_db.load(dbfilename)) != 0) {
635 if (load_status < 0)
636 syslog(LOG_ERR, "Load of db '%s' failed", dbfilename);
637 /* otherwise, there was just nothing to load */
638 WRITEUNLOCK(this, FALSE, "wu db::load");
639 return (FALSE);
640 }
641
642 changed = FALSE;
643 reset_log();
644 WRITELOCK2((&internal_db), FALSE, "w internal_db db::load", this);
645 internal_db.setInitialLoad();
646 if ((count = incorporate_log(logfilename)) < 0)
647 syslog(LOG_ERR, "incorporation of db logfile '%s' load failed",
648 logfilename);
649 changed = (count > 0);
650 internal_db.clearInitialLoad();
651 WRITEUNLOCK2(this, (&internal_db),
652 (changed ? TRUE : FALSE), (changed ? TRUE : FALSE),
653 "wu db::load", "wu internal_db db::load");
654 return (TRUE);
655 }
656
657 /*
658 * Initialize the database using table scheme 's'.
659 * Because the 'scheme' must be 'remembered' between restarts,
660 * after the initialization, the empty database is checkpointed to record
661 * the scheme. Returns TRUE if initialization succeeds; FALSE otherwise.
662 */
663 bool_t
init(db_scheme * s)664 db::init(db_scheme * s)
665 {
666 bool_t ret = FALSE;
667
668 WRITELOCK(this, FALSE, "w db::init");
669 internal_db.init(s);
670 if (internal_db.good()) {
671 unlink(tmpfilename); /* delete partial checkpoints */
672 unlink(logfilename); /* delete previous logfile */
673 reset_log();
674 changed = TRUE; /* force dump to get scheme stored. */
675 ret = checkpoint();
676 }
677 WRITEUNLOCK(this, FALSE, "wu db::init");
678 return (ret);
679 }
680
681 /*
682 Write out in-memory copy of database to file.
683 1. Update major version.
684 2. Dump contents to temporary file.
685 3. Rename temporary file to real database file.
686 4. Remove log file.
687 A checkpoint is done only if it has changed since the previous checkpoint.
688 Returns TRUE if checkpoint was successful; FALSE otherwise.
689 */
690 bool_t
checkpoint()691 db::checkpoint()
692 {
693 WRITELOCK(this, FALSE, "w db::checkpoint");
694 if (changed == FALSE) {
695 WRITEUNLOCK(this, FALSE, "wu db::checkpoint");
696 return (TRUE);
697 }
698
699 vers *oldversion = new vers(internal_db.get_version()); /* copy */
700 vers *nextversion = oldversion->nextmajor(); /* get next version */
701 internal_db.change_version(nextversion); /* change version */
702
703 if (internal_db.dump(tmpfilename) < 0) { /* dump to tempfile */
704 WARNING_M("db::checkpoint: could not dump database: ");
705 internal_db.change_version(oldversion); /* rollback */
706 delete nextversion;
707 delete oldversion;
708 WRITEUNLOCK(this, FALSE, "wu db::checkpoint");
709 return (FALSE);
710 }
711 if (rename(tmpfilename, dbfilename) < 0){ /* rename permanently */
712 WARNING_M(
713 "db::checkpoint: could not rename temp file to db file: ");
714 internal_db.change_version(oldversion); /* rollback */
715 delete nextversion;
716 delete oldversion;
717 WRITEUNLOCK(this, FALSE, "wu db::checkpoint");
718 return (FALSE);
719 }
720 reset_log(); /* should check for what? */
721 unlink(logfilename); /* should do atomic rename and log delete */
722 delete nextversion;
723 delete oldversion;
724 changed = FALSE;
725 WRITEUNLOCK(this, FALSE, "wu db::checkpoint");
726 return (TRUE);
727 }
728
729
730 /* For generating log_list */
731
732 struct traverse_info {
733 vers *version; // version to check for
734 db_log_entry * head; // head of list of log entries found
735 db_log_entry * tail; // tail of list of log entries found
736 };
737
738 /*
739 * For the given entry determine, if it is later than the version supplied,
740 * 1. increment 'count'.
741 * 2. add the entry to the list of log entries found.
742 *
743 * Since traversal happens on an automatic (struct traverse_info) in
744 * db::get_log_entries_since(), no locking is necessary.
745 */
entry_since(db_log_entry * j,char * tichar,int * count)746 static bool_t entry_since(db_log_entry * j, char * tichar, int *count)
747 {
748 traverse_info *ti = (traverse_info*) tichar;
749
750 if (ti->version->earlier_than(j->get_version())) {
751 ++ *count;
752 // j->print(); // debug
753 if (ti->head == NULL)
754 ti->head = j;
755 else {
756 ti->tail->setnextptr(j); // make last entry point to j
757 }
758 ti->tail = j; // make j new last entry
759 }
760
761 return (TRUE);
762 }
763
764 /* Return structure db_log_list containing entries that are later
765 than the version 'v' given. */
766 db_log_list*
get_log_entries_since(vers * v)767 db::get_log_entries_since(vers * v)
768 {
769 int count;
770 struct traverse_info ti;
771 db_log f(logfilename, PICKLE_READ);
772
773 ti.version = v;
774 ti.head = ti.tail = NULL;
775
776 count = f.execute_on_log(&(entry_since), (char *) &ti, FALSE);
777
778 db_log_list * answer = new db_log_list;
779
780 if (answer == NULL)
781 FATAL3("db::get_log_entries_since: cannot allocate space",
782 DB_MEMORY_LIMIT, NULL);
783
784 answer->list.list_len = count;
785
786 if (count > 0) {
787 db_log_entry_p *entries;
788 db_log_entry_p currentry, nextentry;
789 int i;
790
791 entries = answer->list.list_val = new db_log_entry_p[count];
792 if (entries == NULL) {
793 delete answer;
794 FATAL3(
795 "db::get_log_entries_since: cannot allocate space for entries",
796 DB_MEMORY_LIMIT, NULL);
797 }
798 currentry = ti.head;
799 for (i = 0, currentry = ti.head;
800 i < count && currentry != NULL;
801 i++) {
802 entries[i] = currentry;
803 nextentry = currentry->getnextptr();
804 currentry->setnextptr(NULL);
805 currentry = nextentry;
806 }
807 } else
808 answer->list.list_val = NULL;
809
810 return (answer);
811 }
812
813 /* Delete all files associated with database. */
814 int
remove_files()815 db::remove_files()
816 {
817 WRITELOCK(this, -1, "w db::remove_files");
818 unlink(tmpfilename); /* delete partial checkpoints */
819 reset_log();
820 unlink(logfilename); /* delete logfile */
821 unlink(dbfilename); /* delete database file */
822 WRITEUNLOCK(this, -1, "wu db::remove_files");
823 return (0);
824 }
825
826 db_status
sync_log()827 db::sync_log() {
828
829 db_status ret;
830
831 WRITELOCK(this, DB_LOCK_ERROR, "w db::sync_log");
832 if (logfile == 0) {
833 ret = DB_BADTABLE;
834 } else {
835 if (logfile_opened == FALSE || logfile->sync_log())
836 ret = DB_SUCCESS;
837 else
838 ret = DB_SYNC_FAILED;
839 }
840 WRITEUNLOCK(this, DB_LOCK_ERROR, "wu db::sync_log");
841 return (ret);
842 }
843
844 /* Pass configuration information to the db_mindex */
845 bool_t
configure(char * objName)846 db::configure(char *objName) {
847 return (internal_db.configure(objName));
848 }
849
850 db_mindex *
mindex(void)851 db::mindex(void) {
852 return (&internal_db);
853 }
854