mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-16 17:16:25 +00:00
599 lines
18 KiB
C
599 lines
18 KiB
C
/*
|
|
* See the file LICENSE for redistribution information.
|
|
*
|
|
* Copyright (c) 1996, 2011 Oracle and/or its affiliates. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This compilation unit contains functions related to the generation
|
|
* of a simple smoke test for the generated storage layer.
|
|
*/
|
|
#include "generation.h"
|
|
|
|
extern int maxbinsz; /* defined in buildpt.c */
|
|
|
|
static FILE *test_file; /* stream for generated test code */
|
|
static int test_indent_level = 0;
|
|
|
|
static void pr_test(char *, ...);
|
|
static void pr_test_comment(char *, ...);
|
|
static void callback_function_enter_entop(ENTITY *);
|
|
static void callback_function_exit_entop(ENTITY *);
|
|
static void callback_function_attrop(ENTITY *, ATTRIBUTE *, int, int);
|
|
static void declare_record_instances_enter_entop(ENTITY *);
|
|
static void initialize_database_enter_entop(ENTITY *);
|
|
static void initialize_index_enter_entop(DB_INDEX *);
|
|
static void insertion_test_enter_entop(ENTITY *);
|
|
static void insertion_test_exit_entop(ENTITY *);
|
|
static void insertion_test_attrop(ENTITY *, ATTRIBUTE *, int, int);
|
|
static void retrieval_test_enter_entop(ENTITY *);
|
|
static void retrieval_test_exit_entop(ENTITY *);
|
|
static void retrieval_test_attrop(ENTITY *, ATTRIBUTE *, int, int);
|
|
static void invoke_full_iteration_enter_entop(ENTITY *);
|
|
static void invoke_full_iteration_exit_entop(ENTITY *e);
|
|
static void invoke_query_iteration_idxop(DB_INDEX *);
|
|
static void deletion_test_enter_entop(ENTITY *);
|
|
static void deletion_test_exit_entop(ENTITY *e);
|
|
static void close_secondary_test_idxop(DB_INDEX *);
|
|
static void close_primary_test_enter_entop(ENTITY *);
|
|
static void remove_secondary_test_idxop(DB_INDEX *);
|
|
static void remove_primary_test_enter_entop(ENTITY *);
|
|
|
|
/*
|
|
* Generate_test is the sole entry point in this module. It
|
|
* orchestrates the generation of the C code for the smoke test.
|
|
*/
|
|
void
|
|
generate_test(tfile, hfilename)
|
|
FILE *tfile;
|
|
char *hfilename;
|
|
{
|
|
test_file = tfile;
|
|
|
|
pr_test_comment(
|
|
"Simple test for a Berkeley DB implementation \n\
|
|
generated from SQL DDL by db_sql \n\
|
|
");
|
|
|
|
pr_test("\n#include \"%s\"\n\n", hfilename);
|
|
|
|
if (maxbinsz != 0) {
|
|
pr_test_comment("Test data for raw binary types");
|
|
pr_test("#define MAXBINSZ %d\n", maxbinsz);
|
|
pr_test("char binary_data[MAXBINSZ];\n\n");
|
|
pr_test_comment("A very simple binary comparison function");
|
|
pr_test(
|
|
"char * compare_binary(char *p, int len) \n\
|
|
{ \n\
|
|
if (memcmp(p, binary_data, len) == 0) \n\
|
|
return \"*binary values match*\"; \n\
|
|
return \"*binary values don't match*\"; \n\
|
|
} \n\
|
|
\n\
|
|
");
|
|
}
|
|
|
|
pr_test_comment(
|
|
"These are the iteration callback functions. One is defined per \n\
|
|
database(table). They are used for both full iterations and for \n\
|
|
secondary index queries. When a retrieval returns multiple records, \n\
|
|
as in full iteration over an entire database, one of these functions \n\
|
|
is called for each record found");
|
|
|
|
iterate_over_entities(&callback_function_enter_entop,
|
|
&callback_function_exit_entop,
|
|
&callback_function_attrop);
|
|
|
|
pr_test(
|
|
" \n\
|
|
main(int argc, char **argv) \n\
|
|
{ \n\
|
|
int i; \n\
|
|
int ret; \n\
|
|
\n\
|
|
");
|
|
test_indent_level++;
|
|
|
|
iterate_over_entities(&declare_record_instances_enter_entop,
|
|
NULL,
|
|
NULL);
|
|
|
|
iterate_over_entities(&initialize_database_enter_entop,
|
|
NULL,
|
|
NULL);
|
|
|
|
iterate_over_indexes(&initialize_index_enter_entop);
|
|
|
|
pr_test("\n");
|
|
|
|
if (maxbinsz != 0) {
|
|
pr_test_comment(
|
|
"Fill the binary test data with random values");
|
|
pr_test(
|
|
"for (i = 0; i < MAXBINSZ; i++) binary_data[i] = rand();\n\n");
|
|
}
|
|
|
|
pr_test_comment(
|
|
"Use the convenience method to initialize the environment. \n\
|
|
The initializations for each entity and environment can be \n\
|
|
done discretely if you prefer, but this is the easy way.");
|
|
pr_test(
|
|
"ret = initialize_%s_environment(); \n\
|
|
if (ret != 0) { \n\
|
|
printf(\"Initialize error\"); \n\
|
|
goto exit_error; \n\
|
|
} \n\
|
|
\n\
|
|
",
|
|
the_schema.environment.name);
|
|
|
|
pr_test_comment(
|
|
"Now that everything is initialized, insert a single \n\
|
|
record into each database, using the ...insert_fields \n\
|
|
functions. These functions take each field of the \n\
|
|
record as a separate argument");
|
|
|
|
iterate_over_entities(&insertion_test_enter_entop,
|
|
&insertion_test_exit_entop,
|
|
&insertion_test_attrop);
|
|
|
|
pr_test("\n");
|
|
pr_test_comment(
|
|
"Next, retrieve the records just inserted, looking them up \n\
|
|
by their key values");
|
|
|
|
iterate_over_entities(&retrieval_test_enter_entop,
|
|
&retrieval_test_exit_entop,
|
|
&retrieval_test_attrop);
|
|
|
|
pr_test("\n");
|
|
pr_test_comment(
|
|
"Now try iterating over every record, using the ...full_iteration \n\
|
|
functions for each database. For each record found, the \n\
|
|
appropriate ...iteration_callback_test function will be invoked \n\
|
|
(these are defined above).");
|
|
|
|
iterate_over_entities(&invoke_full_iteration_enter_entop,
|
|
&invoke_full_iteration_exit_entop,
|
|
NULL);
|
|
|
|
pr_test("\n");
|
|
pr_test_comment(
|
|
"For the secondary indexes, query for the known keys. This also \n\
|
|
results in the ...iteration_callback_test function's being called \n\
|
|
for each record found.");
|
|
|
|
iterate_over_indexes(&invoke_query_iteration_idxop);
|
|
|
|
pr_test("\n");
|
|
pr_test_comment(
|
|
"Now delete a record from each database using its primary key.");
|
|
|
|
iterate_over_entities(&deletion_test_enter_entop,
|
|
&deletion_test_exit_entop,
|
|
NULL);
|
|
|
|
pr_test("\n");
|
|
test_indent_level--;
|
|
pr_test("exit_error:\n");
|
|
test_indent_level++;
|
|
|
|
pr_test_comment("Close the secondary index databases");
|
|
iterate_over_indexes(&close_secondary_test_idxop);
|
|
|
|
pr_test_comment("Close the primary databases");
|
|
iterate_over_entities(&close_primary_test_enter_entop,
|
|
NULL,
|
|
NULL);
|
|
|
|
pr_test("\n");
|
|
pr_test_comment("Delete the secondary index databases");
|
|
iterate_over_indexes(&remove_secondary_test_idxop);
|
|
|
|
pr_test("\n");
|
|
pr_test_comment("Delete the primary databases");
|
|
iterate_over_entities(&remove_primary_test_enter_entop,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (the_schema.environment.transactional) {
|
|
pr_test(
|
|
" \n\
|
|
if (ret != 0) \n\
|
|
%s_txnp->abort(%s_txnp); \n\
|
|
else \n\
|
|
%s_txnp->commit(%s_txnp, 0); \n\
|
|
",
|
|
the_schema.environment.name,
|
|
the_schema.environment.name,
|
|
the_schema.environment.name,
|
|
the_schema.environment.name);
|
|
}
|
|
|
|
pr_test("\n");
|
|
pr_test_comment("Finally, close the environment");
|
|
pr_test("%s_envp->close(%s_envp, 0);\n",
|
|
the_schema.environment.name, the_schema.environment.name);
|
|
|
|
pr_test("return ret;\n");
|
|
test_indent_level--;
|
|
pr_test("}\n");
|
|
}
|
|
|
|
/*
|
|
* Emit a printf-formatted string into the test code file.
|
|
*/
|
|
static void
|
|
pr_test(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *s;
|
|
static int enable_indent = 1;
|
|
|
|
s = prepare_string(fmt,
|
|
enable_indent ? test_indent_level : 0,
|
|
0);
|
|
|
|
va_start(ap, fmt);
|
|
vfprintf(test_file, s, ap);
|
|
va_end(ap);
|
|
|
|
/*
|
|
* If the last char emitted was a newline, enable indentation
|
|
* for the next time.
|
|
*/
|
|
if (s[strlen(s) - 1] == '\n')
|
|
enable_indent = 1;
|
|
else
|
|
enable_indent = 0;
|
|
}
|
|
|
|
/*
|
|
* Emit a formatted comment into the test file.
|
|
*/
|
|
static void
|
|
pr_test_comment(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *s;
|
|
|
|
s = prepare_string(fmt, test_indent_level, 1);
|
|
|
|
va_start(ap, fmt);
|
|
vfprintf(test_file, s, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* Return the appropriate printf format string for the given type.
|
|
*/
|
|
static char *
|
|
format_string_for_type(ATTR_TYPE *t)
|
|
{
|
|
char *c_type = t->c_type;
|
|
|
|
if (is_array(t)) {
|
|
return ("%s");
|
|
} else if (strcmp(c_type, "char") == 0 ||
|
|
strcmp(c_type, "short") == 0 ||
|
|
strcmp(c_type, "int") == 0) {
|
|
return ("%d");
|
|
} else if (strcmp(c_type, "long") == 0) {
|
|
return ("%ld");
|
|
} else if (strcmp(c_type, "float") == 0) {
|
|
return ("%f");
|
|
} else if (strcmp(c_type, "double") == 0) {
|
|
return ("%lf");
|
|
} else {
|
|
fprintf(stderr,
|
|
"Unexpected C type in schema: %s", c_type);
|
|
assert(0);
|
|
}
|
|
return NULL; /*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* Return a data literal appropriate for the given type, to use as test data.
|
|
*/
|
|
static char *
|
|
data_value_for_type(ATTR_TYPE *t)
|
|
{
|
|
char *c_type = t->c_type;
|
|
|
|
if (is_string(t)) {
|
|
/* If the field is really short, use a tiny string */
|
|
if (t->array_dim < 12)
|
|
return ("\"n\"");
|
|
return ("\"ninety-nine\"");
|
|
} else if (is_array(t)) {
|
|
return ("binary_data");
|
|
} else if (strcmp(c_type, "char") == 0 ||
|
|
strcmp(c_type, "short") == 0 ||
|
|
strcmp(c_type, "int") == 0 ||
|
|
strcmp(c_type, "long") == 0) {
|
|
return ("99");
|
|
} else if (strcmp(c_type, "float") == 0 ||
|
|
strcmp(c_type, "double") == 0) {
|
|
return ("99.5");
|
|
} else {
|
|
fprintf(stderr,
|
|
"Unexpected C type in schema: %s", c_type);
|
|
assert(0);
|
|
}
|
|
return NULL; /*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* This entity operation function is called by the attribute iterator
|
|
* when producing test code that declares record instances.
|
|
*/
|
|
static void
|
|
declare_record_instances_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test("%s_data %s_record;\n", e->name, e->name);
|
|
}
|
|
|
|
/*
|
|
* This entity operation function is called by the attribute iterator
|
|
* when producing test code that initialized database handle.
|
|
*/
|
|
static void
|
|
initialize_database_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test("%s_dbp = NULL;\n", e->name);
|
|
}
|
|
|
|
/*
|
|
* This entity operation function is called by the attribute iterator
|
|
* when producing test code that initialized index handle.
|
|
*/
|
|
static void
|
|
initialize_index_enter_entop(DB_INDEX *idx)
|
|
{
|
|
pr_test("%s_dbp = NULL;\n", idx->name);
|
|
}
|
|
|
|
static void
|
|
invoke_full_iteration_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test(
|
|
"ret = %s_full_iteration(%s_dbp, %s%s&%s_iteration_callback_test, \n\
|
|
\"retrieval of %s record through full iteration\");\n",
|
|
e->name, e->name,
|
|
e->transactional ? the_schema.environment.name : "",
|
|
e->transactional ? "_txnp, " : "",
|
|
e->name, e->name);
|
|
}
|
|
static void
|
|
invoke_full_iteration_exit_entop(ENTITY *e)
|
|
{
|
|
COMPQUIET(e, NULL);
|
|
pr_test(
|
|
"if (ret != 0) { \n\
|
|
printf(\"Full Iteration Error\\n\"); \n\
|
|
goto exit_error; \n\
|
|
} \n\
|
|
\n\
|
|
");
|
|
}
|
|
|
|
/*
|
|
* This index operation function is called by the attribute iterator
|
|
* when producing test code that creates the secondary databases.
|
|
*/
|
|
static void
|
|
invoke_query_iteration_idxop(DB_INDEX *idx)
|
|
{
|
|
ATTR_TYPE *key_type = idx->attribute->type;
|
|
|
|
pr_test("%s_query_iteration(%s_dbp, %s%s",
|
|
idx->name, idx->name,
|
|
idx->primary->transactional ? the_schema.environment.name : "",
|
|
idx->primary->transactional ? "_txnp, " : "");
|
|
|
|
pr_test("%s", data_value_for_type(key_type));
|
|
|
|
pr_test(
|
|
",\n &%s_iteration_callback_test, \n\
|
|
\"retrieval of %s record through %s query\");\n",
|
|
idx->primary->name, idx->primary->name, idx->name);
|
|
}
|
|
|
|
/*
|
|
* This next group of entity and attribute operation functions are
|
|
* called by the attribute iterator when generating insertion test code.
|
|
*/
|
|
static void
|
|
insertion_test_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test("ret = %s_insert_fields(%s_dbp, %s%s",
|
|
e->name,
|
|
e->name,
|
|
e->transactional ? the_schema.environment.name : "",
|
|
e->transactional ? "_txnp, " : "");
|
|
}
|
|
static void
|
|
insertion_test_exit_entop(ENTITY *e)
|
|
{
|
|
COMPQUIET(e, NULL);
|
|
pr_test(");\n");
|
|
pr_test(
|
|
"if (ret != 0) { \n\
|
|
printf(\"Insert error\\n\"); \n\
|
|
goto exit_error; \n\
|
|
} \n\
|
|
\n\
|
|
");
|
|
}
|
|
static void
|
|
insertion_test_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
|
|
{
|
|
COMPQUIET(e, NULL);
|
|
COMPQUIET(first, 0);
|
|
|
|
pr_test("%s", data_value_for_type(a->type));
|
|
if (!last)
|
|
pr_test(", ");
|
|
}
|
|
|
|
/*
|
|
* This next group of index and attribute operation functions are
|
|
* called by the attribute iterator when generating the iteration
|
|
* callback function.
|
|
*/
|
|
static void
|
|
callback_function_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
|
|
{
|
|
COMPQUIET(first, 0);
|
|
COMPQUIET(last, 0);
|
|
|
|
pr_test("printf(\"%s->%s: ", e->name, a->name);
|
|
|
|
pr_test("%s", format_string_for_type(a->type));
|
|
|
|
if (is_array(a->type) && !is_string(a->type)) {
|
|
pr_test("\\n\", compare_binary(%s_record->%s, %s));\n",
|
|
e->name, a->name, array_dim_name(e, a) );
|
|
} else {
|
|
pr_test("\\n\", %s_record->%s);\n", e->name, a->name);
|
|
}
|
|
}
|
|
static void
|
|
callback_function_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test(
|
|
" \n\
|
|
void %s_iteration_callback_test(void *msg, %s_data *%s_record) \n\
|
|
{ \n\
|
|
printf(\"In iteration callback, message is: %%s\\n\", (char *)msg);\n\n",
|
|
e->name, e->name, e->name);
|
|
|
|
test_indent_level++;
|
|
}
|
|
static void
|
|
callback_function_exit_entop(ENTITY *e)
|
|
{
|
|
COMPQUIET(e, NULL);
|
|
|
|
test_indent_level--;
|
|
pr_test("}\n\n");
|
|
}
|
|
|
|
/*
|
|
* This next group of entity and attribute operation functions are
|
|
* called by the attribute iterator when generating retrieval test code
|
|
*/
|
|
static void
|
|
retrieval_test_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test("\nprintf(\"Retrieval of %s record by key\\n\");\n", e->name);
|
|
pr_test("ret = get_%s_data( %s_dbp, %s%s%s, &%s_record);\n\n",
|
|
e->name, e->name,
|
|
e->transactional ? the_schema.environment.name : "",
|
|
e->transactional ? "_txnp, " : "",
|
|
data_value_for_type(e->primary_key->type), e->name);
|
|
}
|
|
static void
|
|
retrieval_test_exit_entop(ENTITY *e)
|
|
{
|
|
COMPQUIET(e, NULL);
|
|
pr_test(
|
|
"if (ret != 0) \n\
|
|
{ \n\
|
|
printf(\"Retrieve error\\n\"); \n\
|
|
goto exit_error; \n\
|
|
} \n\
|
|
\n\
|
|
");
|
|
}
|
|
|
|
static void
|
|
retrieval_test_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
|
|
{
|
|
COMPQUIET(first, 0);
|
|
COMPQUIET(last, 0);
|
|
|
|
pr_test("printf(\"%s.%s: ", e->name, a->name);
|
|
|
|
pr_test("%s", format_string_for_type(a->type));
|
|
|
|
if (is_array(a->type) && !is_string(a->type)) {
|
|
pr_test("\\n\", compare_binary(%s_record.%s, %s));\n",
|
|
e->name, a->name, array_dim_name(e, a) );
|
|
} else {
|
|
pr_test("\\n\", %s_record.%s);\n", e->name, a->name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This entity operation function is
|
|
* called by the attribute iterator when generating deletion test code.
|
|
*/
|
|
static void
|
|
deletion_test_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test("ret = delete_%s_key( %s_dbp, %s%s%s);\n",
|
|
e->name, e->name,
|
|
e->transactional ? the_schema.environment.name : "",
|
|
e->transactional ? "_txnp, " : "",
|
|
data_value_for_type(e->primary_key->type));
|
|
}
|
|
static void
|
|
deletion_test_exit_entop(ENTITY *e)
|
|
{
|
|
COMPQUIET(e, NULL);
|
|
pr_test(
|
|
"if (ret != 0) { \n\
|
|
printf(\"Delete error\\n\"); \n\
|
|
goto exit_error; \n\
|
|
} \n\
|
|
\n\
|
|
");
|
|
}
|
|
|
|
/* This entity operation function generates primary database closures. */
|
|
static void
|
|
close_primary_test_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test(
|
|
"if (%s_dbp != NULL) \n\
|
|
%s_dbp->close(%s_dbp, 0); \n\
|
|
\n\
|
|
",
|
|
e->name, e->name, e->name);
|
|
}
|
|
|
|
/* This entity operation function generates secondary database closures. */
|
|
static void
|
|
close_secondary_test_idxop(DB_INDEX *idx)
|
|
{
|
|
pr_test(
|
|
"if (%s_dbp != NULL) \n\
|
|
%s_dbp->close(%s_dbp, 0); \n\
|
|
\n\
|
|
",
|
|
idx->name, idx->name, idx->name);
|
|
}
|
|
|
|
/* This entity operation function generates primary database closures. */
|
|
static void
|
|
remove_primary_test_enter_entop(ENTITY *e)
|
|
{
|
|
pr_test("remove_%s_database(%s_envp%s%s%s);\n", e->name,
|
|
the_schema.environment.name,
|
|
e->transactional ? ", " : "",
|
|
e->transactional ? the_schema.environment.name : "",
|
|
e->transactional ? "_txnp" : "");
|
|
}
|
|
|
|
/* This entity operation function generates secondary database closures. */
|
|
static void
|
|
remove_secondary_test_idxop(DB_INDEX *idx)
|
|
{
|
|
pr_test("remove_%s_index(%s_envp%s%s%s);\n", idx->name,
|
|
the_schema.environment.name,
|
|
idx->primary->transactional ? ", " : "",
|
|
idx->primary->transactional ? the_schema.environment.name : "",
|
|
idx->primary->transactional ? "_txnp" : "");
|
|
}
|