libdb/util/db_sql_codegen/generate_test.c
2011-09-13 13:44:24 -04:00

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" : "");
}