Commit ebd5618f authored by Bradley C. Kuszmaul's avatar Bradley C. Kuszmaul

Fix up error handling and add tests. Fixes #246.

git-svn-id: file:///svn/tokudb@1535 c7de825b-a66e-492c-adef-691d508d4ae1
parent 66c9c1ef
...@@ -120,11 +120,13 @@ NO_VGRIND = \ ...@@ -120,11 +120,13 @@ NO_VGRIND = \
db_dbt_mem_behavior \ db_dbt_mem_behavior \
db_assoc3 \ db_assoc3 \
db_curs2 \ db_curs2 \
db_delete \
db_env_open_nocreate \ db_env_open_nocreate \
db_env_open_open_close \ db_env_open_open_close \
db_open_notexist_reopen \ db_open_notexist_reopen \
db_remove_subdb \ db_remove_subdb \
log4 \ log4 \
log5 \
# Comment to terminate list so the previous line can end with a slash # Comment to terminate list so the previous line can end with a slash
$(patsubst %,test_%.bdbrun,$(NO_VGRIND)): VGRIND= $(patsubst %,test_%.bdbrun,$(NO_VGRIND)): VGRIND=
......
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <db.h>
#ifndef DB_YESOVERWRITE #ifndef DB_YESOVERWRITE
#define DB_YESOVERWRITE 0 #define DB_YESOVERWRITE 0
......
// I want fmemopen
#define _GNU_SOURCE
#include "test.h"
#include <assert.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
const char *expect_errpfx=0;
int n_handle_error=0;
void handle_error (const DB_ENV *dbenv, const char *errpfx, const char *msg) {
assert(errpfx==expect_errpfx);
n_handle_error++;
}
int main (int argc, const char *argv[]) {
parse_args(argc, argv);
system("rm -rf " DIR);
int r=mkdir(DIR, 0777); assert(r==0);
{
DB_ENV *env;
r = db_env_create(&env, 0); assert(r==0);
r = env->open(env, DIR, -1, 0644);
assert(r==EINVAL && n_handle_error==0);
r = env->close(env, 0); assert(r==0);
}
int do_errfile, do_errcall;
for (do_errfile=0; do_errfile<2; do_errfile++) {
for (do_errcall=0; do_errcall<2; do_errcall++) {
DB_ENV *env;
char buf[10000]="";
FILE *write_here = fmemopen(buf, sizeof(buf), "w");
n_handle_error=0;
r = db_env_create(&env, 0); assert(r==0);
if (do_errfile)
env->set_errfile(env, write_here);
if (do_errcall)
env->set_errcall(env, handle_error);
r = env->open(env, DIR, -1, 0644);
assert(r==EINVAL);
r = env->close(env, 0); assert(r==0);
fclose(write_here);
if (do_errfile) {
assert(buf[0]!=0);
assert(buf[0]!=':');
} else {
assert(buf[0]==0);
}
if (do_errcall) {
assert(n_handle_error==1);
} else {
assert(n_handle_error==0);
}
}
}
return 0;
}
...@@ -39,7 +39,7 @@ static int do_associated_inserts (DB_TXN *txn, DBT *key, DBT *data, DB *secondar ...@@ -39,7 +39,7 @@ static int do_associated_inserts (DB_TXN *txn, DBT *key, DBT *data, DB *secondar
#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 1 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 1
typedef void (*toku_env_errcall_t)(const char *, char *); typedef void (*toku_env_errcall_t)(const char *, char *);
#elif DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3 #elif DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
typedef void (*toku_env_errcall_t)(DB_ENV *, const char *, const char *); typedef void (*toku_env_errcall_t)(const DB_ENV *, const char *, const char *);
#else #else
#error #error
#endif #endif
...@@ -50,7 +50,7 @@ struct __toku_db_env_internal { ...@@ -50,7 +50,7 @@ struct __toku_db_env_internal {
int open_mode; int open_mode;
toku_env_errcall_t errcall; toku_env_errcall_t errcall;
void *errfile; void *errfile;
char *errpfx; const char *errpfx;
char *dir; /* A malloc'd copy of the directory. */ char *dir; /* A malloc'd copy of the directory. */
char *tmp_dir; char *tmp_dir;
char *lg_dir; char *lg_dir;
...@@ -62,23 +62,54 @@ struct __toku_db_env_internal { ...@@ -62,23 +62,54 @@ struct __toku_db_env_internal {
TOKULOGGER logger; TOKULOGGER logger;
}; };
// Probably this do_error (which is dumb and stupid) should do something consistent with do_env_err. // If errcall is set, call it with the format string and optionally the stderrstring (if include_stderrstring). The prefix is passed as a separate argument.
static void do_error (DB_ENV *dbenv, const char *string) { // If errfile is set, print to the errfile: prefix, fmt string, maybe include the stderr string.
if (dbenv->i->errfile) // Both errcall and errfile may be called.
fprintf(dbenv->i->errfile, "%s\n", string); // If errfile is not set and errcall is not set, the use stderr as the errfile.
void do_error_all_cases(const DB_ENV * env, int error, int include_stderrstring, int use_stderr_if_nothing_else, const char *fmt, va_list ap) {
if (env->i->errcall) {
// errcall gets prefix sent separately
// the error message is the printf message, maybe followed by ": " and the dbstrerror (if include_stderrstring is set)
char buf [4000];
int count=0;
if (fmt) {
count=vsnprintf(buf, sizeof(buf), fmt, ap);
}
if (include_stderrstring) {
count+=snprintf(&buf[count], sizeof(buf)-count, ": %s", db_strerror(error));
}
env->i->errcall(env, env->i->errpfx, buf);
}
{
FILE *efile=env->i->errfile;
if (efile==0 && env->i->errcall==0 && use_stderr_if_nothing_else) {
efile = stderr;
}
if (efile) {
if (env->i->errpfx) fprintf(efile, "%s: ", env->i->errpfx);
vfprintf(efile, fmt, ap);
if (include_stderrstring) {
fprintf(efile, ": %s", db_strerror(error));
}
}
}
} }
void toku_db_env_err_vararg(const DB_ENV * env, int error, const char *fmt, va_list ap) {
FILE* ferr = env->i->errfile ? env->i->errfile : stderr; // Handle all the error cases (but don't do the default thing.)
if (env->i->errpfx && env->i->errpfx[0] != '\0') fprintf(stderr, "%s: ", env->i->errpfx); static int do_error (DB_ENV *dbenv, int error, const char *string, ...) {
fprintf(ferr, "YDB Error %d: ", error); va_list ap;
vfprintf(ferr, fmt, ap); va_start(ap, string);
do_error_all_cases(dbenv, error, 1, 0, string, ap);
va_end(ap);
return errno;
} }
static void toku_db_env_err(const DB_ENV * env, int error, const char *fmt, ...) { static void toku_db_env_err(const DB_ENV * env, int error, const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
toku_db_env_err_vararg(env, error, fmt, ap); do_error_all_cases(env, error, 1, 1, fmt, ap);
va_end(ap); va_end(ap);
} }
...@@ -253,11 +284,11 @@ static int db_env_read_config(DB_ENV *env) { ...@@ -253,11 +284,11 @@ static int db_env_read_config(DB_ENV *env) {
} }
if (0) { if (0) {
readerror: readerror:
env->err(env, r, "Error reading from DB_CONFIG:%d.\n", linenumber); do_error(env, r, "Error reading from DB_CONFIG:%d.\n", linenumber);
} }
if (0) { if (0) {
parseerror: parseerror:
env->err(env, r, "Error parsing DB_CONFIG:%d.\n", linenumber); do_error(env, r, "Error parsing DB_CONFIG:%d.\n", linenumber);
} }
cleanup: cleanup:
if (full_name) toku_free(full_name); if (full_name) toku_free(full_name);
...@@ -269,13 +300,18 @@ static int db_env_read_config(DB_ENV *env) { ...@@ -269,13 +300,18 @@ static int db_env_read_config(DB_ENV *env) {
static int toku_db_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mode) { static int toku_db_env_open(DB_ENV * env, const char *home, u_int32_t flags, int mode) {
int r; int r;
if (db_env_opened(env)) if (db_env_opened(env)) {
return EINVAL; return do_error(env, EINVAL, "The environment is already open\n");
}
if ((flags & DB_USE_ENVIRON) && (flags & DB_USE_ENVIRON_ROOT)) return EINVAL; if ((flags & DB_USE_ENVIRON) && (flags & DB_USE_ENVIRON_ROOT)) {
return do_error(env, EINVAL, "DB_USE_ENVIRON and DB_USE_ENVIRON_ROOT are incompatible flags\n");
}
if (home) { if (home) {
if ((flags & DB_USE_ENVIRON) || (flags & DB_USE_ENVIRON_ROOT)) return EINVAL; if ((flags & DB_USE_ENVIRON) || (flags & DB_USE_ENVIRON_ROOT)) {
return do_error(env, EINVAL, "DB_USE_ENVIRON and DB_USE_ENVIRON_ROOT are incompatible with specifying a home\n");
}
} }
else if ((flags & DB_USE_ENVIRON) || else if ((flags & DB_USE_ENVIRON) ||
((flags & DB_USE_ENVIRON_ROOT) && geteuid() == 0)) home = getenv("DB_HOME"); ((flags & DB_USE_ENVIRON_ROOT) && geteuid() == 0)) home = getenv("DB_HOME");
...@@ -286,31 +322,34 @@ static int toku_db_env_open(DB_ENV * env, const char *home, u_int32_t flags, int ...@@ -286,31 +322,34 @@ static int toku_db_env_open(DB_ENV * env, const char *home, u_int32_t flags, int
{ {
struct stat buf; struct stat buf;
r = stat(home, &buf); r = stat(home, &buf);
if (r!=0) return errno; if (r!=0) {
return do_error(env, errno, "Error from stat(\"%s\",...)\n", home);
}
} }
if (!(flags & DB_PRIVATE)) { if (!(flags & DB_PRIVATE)) {
// There is no good place to send this error message.
// fprintf(stderr, "tokudb requires DB_PRIVATE\n");
// This means that we don't have to do anything with shared memory. // This means that we don't have to do anything with shared memory.
// And that's good enough for mysql. // And that's good enough for mysql.
return EINVAL; return do_error(env, EINVAL, "TokuDB requires DB_PRIVATE when opening an env\n");
} }
if (env->i->dir) if (env->i->dir)
toku_free(env->i->dir); toku_free(env->i->dir);
env->i->dir = toku_strdup(home); env->i->dir = toku_strdup(home);
if (env->i->dir == 0) if (env->i->dir == 0) {
return ENOMEM; return do_error(env, ENOMEM, "Out of memory\n");
}
if (0) { if (0) {
died1: died1:
toku_free(env->i->dir); toku_free(env->i->dir);
env->i->dir = NULL; env->i->dir = NULL;
return r; return r;
} }
if ((r = db_env_read_config(env)) != 0) goto died1; if ((r = db_env_read_config(env)) != 0) {
goto died1;
}
env->i->open_flags = flags; env->i->open_flags = flags;
env->i->open_mode = mode; env->i->open_mode = mode;
...@@ -321,7 +360,10 @@ static int toku_db_env_open(DB_ENV * env, const char *home, u_int32_t flags, int ...@@ -321,7 +360,10 @@ static int toku_db_env_open(DB_ENV * env, const char *home, u_int32_t flags, int
r = toku_logger_create_and_open_logger( r = toku_logger_create_and_open_logger(
full_dir ? full_dir : env->i->dir, &env->i->logger); full_dir ? full_dir : env->i->dir, &env->i->logger);
if (full_dir) toku_free(full_dir); if (full_dir) toku_free(full_dir);
if (r!=0) goto died1; if (r!=0) {
do_error(env, r, "Could not open logger\n");
goto died1;
}
if (0) { if (0) {
died2: died2:
toku_logger_log_close(&env->i->logger); toku_logger_log_close(&env->i->logger);
...@@ -352,8 +394,6 @@ static int toku_db_env_close(DB_ENV * env, u_int32_t flags) { ...@@ -352,8 +394,6 @@ static int toku_db_env_close(DB_ENV * env, u_int32_t flags) {
toku_free(env->i->lg_dir); toku_free(env->i->lg_dir);
if (env->i->tmp_dir) if (env->i->tmp_dir)
toku_free(env->i->tmp_dir); toku_free(env->i->tmp_dir);
if (env->i->errpfx)
toku_free(env->i->errpfx);
toku_free(env->i->dir); toku_free(env->i->dir);
toku_free(env->i); toku_free(env->i);
toku_free(env); toku_free(env);
...@@ -402,8 +442,9 @@ static int toku_db_env_set_data_dir(DB_ENV * env, const char *dir) { ...@@ -402,8 +442,9 @@ static int toku_db_env_set_data_dir(DB_ENV * env, const char *dir) {
char** temp; char** temp;
char* new_dir; char* new_dir;
if (db_env_opened(env) || !dir) if (db_env_opened(env) || !dir) {
return EINVAL; return do_error(env, EINVAL, "You cannot set the data dir after opening the env\n");
}
if (env->i->data_dirs) { if (env->i->data_dirs) {
assert(env->i->n_data_dirs > 0); assert(env->i->n_data_dirs > 0);
...@@ -421,7 +462,10 @@ static int toku_db_env_set_data_dir(DB_ENV * env, const char *dir) { ...@@ -421,7 +462,10 @@ static int toku_db_env_set_data_dir(DB_ENV * env, const char *dir) {
toku_free(new_dir); toku_free(new_dir);
return r; return r;
} }
if (new_dir==NULL) {assert(errno == ENOMEM); return ENOMEM;} if (new_dir==NULL) {
assert(errno == ENOMEM);
return do_error(env, errno, "Out of memory\n");
}
temp = (char**) toku_realloc(env->i->data_dirs, (1 + env->i->n_data_dirs) * sizeof(char*)); temp = (char**) toku_realloc(env->i->data_dirs, (1 + env->i->n_data_dirs) * sizeof(char*));
if (temp==NULL) {assert(errno == ENOMEM); r = ENOMEM; goto died1;} if (temp==NULL) {assert(errno == ENOMEM); r = ENOMEM; goto died1;}
else env->i->data_dirs = temp; else env->i->data_dirs = temp;
...@@ -439,43 +483,45 @@ static void toku_db_env_set_errfile(DB_ENV*env, FILE*errfile) { ...@@ -439,43 +483,45 @@ static void toku_db_env_set_errfile(DB_ENV*env, FILE*errfile) {
} }
static void toku_db_env_set_errpfx(DB_ENV * env, const char *errpfx) { static void toku_db_env_set_errpfx(DB_ENV * env, const char *errpfx) {
if (env->i->errpfx) env->i->errpfx = errpfx;
toku_free(env->i->errpfx);
env->i->errpfx = toku_strdup(errpfx ? errpfx : "");
} }
static int toku_db_env_set_flags(DB_ENV * env, u_int32_t flags, int onoff) { static int toku_db_env_set_flags(DB_ENV * env, u_int32_t flags, int onoff) {
env=env; if (flags != 0 && onoff) {
if (flags != 0 && onoff) return do_error(env, EINVAL, "TokuDB does not (yet) support any nonzero ENV flags\n");
return EINVAL; /* no flags are currently supported */ }
return 0; return 0;
} }
static int toku_db_env_set_lg_bsize(DB_ENV * env, u_int32_t bsize) { static int toku_db_env_set_lg_bsize(DB_ENV * env, u_int32_t bsize) {
env=env; bsize=bsize; bsize=bsize;
return 1; return do_error(env, EINVAL, "TokuDB does not (yet) support ENV->set_lg_bsize\n");
} }
static int toku_db_env_set_lg_dir(DB_ENV * env, const char *dir) { static int toku_db_env_set_lg_dir(DB_ENV * env, const char *dir) {
if (db_env_opened(env)) return EINVAL; if (db_env_opened(env)) {
return do_error(env, EINVAL, "Cannot set log dir after opening the env\n");
}
if (env->i->lg_dir) toku_free(env->i->lg_dir); if (env->i->lg_dir) toku_free(env->i->lg_dir);
if (dir) { if (dir) {
env->i->lg_dir = toku_strdup(dir); env->i->lg_dir = toku_strdup(dir);
if (!env->i->lg_dir) return ENOMEM; if (!env->i->lg_dir) {
return do_error(env, ENOMEM, "Out of memory\n");
}
} }
else env->i->lg_dir = NULL; else env->i->lg_dir = NULL;
return 0; return 0;
} }
static int toku_db_env_set_lg_max(DB_ENV * env, u_int32_t lg_max) { static int toku_db_env_set_lg_max(DB_ENV * env, u_int32_t lg_max) {
env=env; lg_max=lg_max; lg_max=lg_max;
return 1; return do_error(env, EINVAL, "TokuDB does not (yet) support set_lg_max\n");
} }
static int toku_db_env_set_lk_detect(DB_ENV * env, u_int32_t detect) { static int toku_db_env_set_lk_detect(DB_ENV * env, u_int32_t detect) {
env=env; detect=detect; detect=detect;
return 1; return do_error(env, EINVAL, "TokuDB does not (yet) support set_lk_detect\n");
} }
#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR <= 4 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR <= 4
...@@ -490,8 +536,12 @@ static int toku_db_env_set_lk_max(DB_ENV * env, u_int32_t lk_max) { ...@@ -490,8 +536,12 @@ static int toku_db_env_set_lk_max(DB_ENV * env, u_int32_t lk_max) {
//} //}
static int toku_db_env_set_tmp_dir(DB_ENV * env, const char *tmp_dir) { static int toku_db_env_set_tmp_dir(DB_ENV * env, const char *tmp_dir) {
if (db_env_opened(env)) return EINVAL; if (db_env_opened(env)) {
if (!tmp_dir) return EINVAL; return do_error(env, EINVAL, "Cannot set the tmp dir after opening an env\n");
}
if (!tmp_dir) {
return do_error(env, EINVAL, "Tmp dir bust be non-null\n");
}
if (env->i->tmp_dir) if (env->i->tmp_dir)
toku_free(env->i->tmp_dir); toku_free(env->i->tmp_dir);
env->i->tmp_dir = toku_strdup(tmp_dir); env->i->tmp_dir = toku_strdup(tmp_dir);
...@@ -516,7 +566,7 @@ static int toku_db_env_txn_stat(DB_ENV * env, DB_TXN_STAT ** statp, u_int32_t fl ...@@ -516,7 +566,7 @@ static int toku_db_env_txn_stat(DB_ENV * env, DB_TXN_STAT ** statp, u_int32_t fl
#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 1 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 1
void toku_default_errcall(const char *errpfx, char *msg) { void toku_default_errcall(const char *errpfx, char *msg) {
#else #else
void toku_default_errcall(DB_ENV *env, const char *errpfx, const char *msg) { void toku_default_errcall(const DB_ENV *env, const char *errpfx, const char *msg) {
env = env; env = env;
#endif #endif
fprintf(stderr, "YDB: %s: %s", errpfx, msg); fprintf(stderr, "YDB: %s: %s", errpfx, msg);
...@@ -565,8 +615,8 @@ int db_env_create(DB_ENV ** envp, u_int32_t flags) { ...@@ -565,8 +615,8 @@ int db_env_create(DB_ENV ** envp, u_int32_t flags) {
} }
memset(result->i, 0, sizeof *result->i); memset(result->i, 0, sizeof *result->i);
result->i->ref_count = 1; result->i->ref_count = 1;
result->i->errcall = toku_default_errcall; result->i->errcall = 0;
result->i->errpfx = toku_strdup(""); result->i->errpfx = 0;
result->i->errfile = 0; result->i->errfile = 0;
ydb_add_ref(); ydb_add_ref();
...@@ -1428,8 +1478,7 @@ static int toku_db_put_noassociate(DB * db, DB_TXN * txn, DBT * key, DBT * data, ...@@ -1428,8 +1478,7 @@ static int toku_db_put_noassociate(DB * db, DB_TXN * txn, DBT * key, DBT * data,
if (r == 0) if (r == 0)
return DB_KEYEXIST; return DB_KEYEXIST;
#else #else
do_error(db->dbenv, "Tokudb requires that db->put specify DB_YESOVERWRITE or DB_NOOVERWRITE on DB_DUPSORT databases"); return do_error(db->dbenv, EINVAL, "Tokudb requires that db->put specify DB_YESOVERWRITE or DB_NOOVERWRITE on DB_DUPSORT databases");
return EINVAL;
#endif #endif
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment