I am running into a persistent "numeric value is out of range" error that is triggered by adding an additional column (of type double precision, varchar, smallint, intger, int64, they all fail) without binding a value for this insert. It also fails if I try to bind a value to the column.
I have attached a minimal reproducible example (I apologize, if I insert fewer columns the error doesn't occur, or occurs only sporadically). The code below will run successfully, but if I add in an 11th column (small0), it fails.
###############################################################
#include <stdlib.h>
#include <string.h>
#include <ibase.h>
#include <stdio.h>
#include <set>
#include <iostream>
#define RC_OK 0
using namespace std;
#define SQL_DIALECT_VERSION 3
#define DB_HANDLE_COUNT 1
#define USE_XSQLDA 1
#ifndef NDEBUG
# define ASSERT(condition, message) \
do { \
if (! (condition)) { \
std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \
<< " line " << __LINE__ << ": " << message << std::endl; \
std::terminate(); \
} \
} while (false)
#else
# define ASSERT(condition, message) do { } while (false)
#endif
isc_db_handle db = NULL;
isc_stmt_handle stmt = NULL;
size_t num_cols;
int initialize_db() {
int rc;
ISC_STATUS_ARRAY status; /* status vector */
isc_tr_handle trans = NULL; /* transaction handle */
string db_name = "correctness_test.fdb";
string create_db_query = "CREATE DATABASE '" + db_name + "' PAGE_SIZE=8192";
/*
* Create a new database.
* The database handle is zero.
*/
cout << "about to isc_dsql_execute_immediate" << endl;
if (isc_dsql_execute_immediate(status, &db, &trans, 0, create_db_query.c_str(), SQL_DIALECT_VERSION,
NULL))
{
/* Extract SQLCODE from the status vector. */
long sqlcode = isc_sqlcode(status);
/* Print a descriptive message based on the SQLCODE. */
if (sqlcode == -902)
{
printf("\nDatabase already exists.\n");
// printf("Remove %s before running this program.\n\n", new_dbname);
rc = isc_attach_database(status, 0, db_name.c_str(), &db, 0, NULL);
ASSERT(rc == RC_OK, isc_print_status(status));
}
}
cout << "about to isc_commit_transaction" << endl;
isc_commit_transaction(status, &trans);
printf("Created database '%s'.\n\n", db_name.c_str());
return rc;
}
//designed to create one at a time, so we don't have to worry about cross-table interference
int create_table() {
int rc;
ISC_STATUS_ARRAY status; /* status vector */
isc_tr_handle trans = NULL; /* transaction handle */
string create_table_query;
create_table_query = "recreate table metadata_chunks "
"(id bigint generated always as identity primary key, string0 varchar (50), int0 bigint, string2 varchar (50)"
", dbl0 double precision, dbl1 double precision, dbl2 double precision, dbl3 double precision, dbl4 double precision, dbl5 double precision, "
//uncommenting this causes the insert to produce "numeric value is out of range" error
// "small0 smallint, "
//uncommenting this causes the insert to produce "numeric value is out of range" error
// "small0 double precision, "
//uncommenting this causes the insert to produce "numeric value is out of range" error
// ", small0 varchar(256)"
"string3 varchar(256)"
")";
/* Create table. */
isc_start_transaction(status, &trans, DB_HANDLE_COUNT, &db, 0, NULL);
cout << "create_table_query: " << create_table_query << endl;
rc = isc_dsql_execute_immediate(status, &db, &trans, 0, create_table_query.c_str(), SQL_DIALECT_VERSION, NULL);
ASSERT(rc == RC_OK, isc_print_status(status));
isc_commit_transaction(status, &trans);
return rc;
}
int prepare_statements() {
int rc;
ISC_STATUS_ARRAY status; /* status vector */
isc_tr_handle trans = NULL; /* transaction handle */
rc = isc_dsql_allocate_statement(status, &db, &stmt);
// string query = "insert into metadata_chunks (string0, int0, string2, dbl0, dbl1, dbl2, dbl3, dbl4, dbl5, small0, string3) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
string query = "insert into metadata_chunks (string0, int0, string2, dbl0, dbl1, dbl2, dbl3, dbl4, dbl5, string3) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
num_cols = 10; //not including id
// num_cols = 11; //not including id
rc = isc_dsql_prepare(status, &trans, &stmt, 0, query.c_str(), SQL_DIALECT_VERSION, NULL);
ASSERT(rc == RC_OK, isc_print_status(status));
return rc;
}
int perform_write(char *name, size_t len_name, size_t *timestep, char *var_name, size_t len_var_name,
double *dbl0, double *dbl1, double *dbl2, double *dbl3, double *dbl4, double *dbl5
, unsigned short *data_type, char *data, size_t len_data
) {
int rc;
ISC_STATUS_ARRAY status; /* status vector */
isc_tr_handle trans = NULL; /* transaction handle */
short is_null[num_cols];
size_t length[num_cols];
XSQLDA *stmt_input_params = (XSQLDA *) malloc(XSQLDA_LENGTH(num_cols));
stmt_input_params->sqln = stmt_input_params->sqld = num_cols;
stmt_input_params->version = 1;
int i = 0;
is_null[i] = (len_name == 0);
length[i] = len_name;
stmt_input_params->sqlvar[i].sqltype = SQL_TEXT + 1;
stmt_input_params->sqlvar[i].sqldata = name;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(size_t);
stmt_input_params->sqlvar[i].sqltype = SQL_INT64;
stmt_input_params->sqlvar[i].sqldata = (char*)timestep;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = (len_var_name == 0);
length[i] = len_var_name;
stmt_input_params->sqlvar[i].sqltype = SQL_TEXT + 1;
stmt_input_params->sqlvar[i].sqldata = var_name;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(double);
stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
stmt_input_params->sqlvar[i].sqldata = (char*)dbl0;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(double);
stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
stmt_input_params->sqlvar[i].sqldata = (char*)dbl1;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(double);
stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
stmt_input_params->sqlvar[i].sqldata = (char*)dbl2;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(double);
stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
stmt_input_params->sqlvar[i].sqldata = (char*)dbl3;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(double);
stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
stmt_input_params->sqlvar[i].sqldata = (char*)dbl4;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = 0;
length[i] = sizeof(double);
stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
stmt_input_params->sqlvar[i].sqldata = (char*)dbl5;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
is_null[i] = (len_data == 0);
length[i] = len_data;
stmt_input_params->sqlvar[i].sqltype = SQL_TEXT + 1;
stmt_input_params->sqlvar[i].sqldata = data;
stmt_input_params->sqlvar[i].sqlind = &is_null[i];
stmt_input_params->sqlvar[i].sqllen = length[i];
i++;
//if I increase num_cols to 11 and add the extra column (small0) to the table with type double, varchar or smallint and uncomment one of the parameters below, it still produces a numeric overflow error
// is_null[i] = 0;
// length[i] = sizeof(double);
// stmt_input_params->sqlvar[i].sqltype = SQL_DOUBLE;
// stmt_input_params->sqlvar[i].sqldata = (char*)dbl5;
// stmt_input_params->sqlvar[i].sqlind = &is_null[i];
// stmt_input_params->sqlvar[i].sqllen = length[i];
// i++;
// is_null[i] = (len_data == 0);
// length[i] = len_data;
// stmt_input_params->sqlvar[i].sqltype = SQL_TEXT + 1;
// stmt_input_params->sqlvar[i].sqldata = data;
// stmt_input_params->sqlvar[i].sqlind = &is_null[i];
// stmt_input_params->sqlvar[i].sqllen = length[i];
// i++;
// is_null[i] = 0;
// length[i] = sizeof(unsigned short);
// stmt_input_params->sqlvar[i].sqltype = SQL_SHORT;
// stmt_input_params->sqlvar[i].sqldata = (char*)data_type;
// stmt_input_params->sqlvar[i].sqlind = &is_null[i];
// stmt_input_params->sqlvar[i].sqllen = length[i];
// i++;
/* Declare the cursor. */
isc_dsql_set_cursor_name(status, &stmt, "write md", 0);
rc = isc_start_transaction(status, &trans, DB_HANDLE_COUNT, &db, 0, NULL);
ASSERT(rc == RC_OK, isc_print_status(status));
cout << "inserting" << endl;
rc = isc_dsql_execute(status, &trans, &stmt, SQL_DIALECT_VERSION, stmt_input_params);
ASSERT(rc == RC_OK, isc_print_status(status));
rc = isc_commit_transaction(status, &trans);
ASSERT(rc == RC_OK, isc_print_status(status));
free(stmt_input_params);
cout << "finished successfully!" << endl;
return rc;
}
int main() {
char string0[256] = "string0";
char string1[256] = "string1";
char string2[256] = "string2";
size_t int0 = 123;
double dbl0 = 1.2, dbl1 = 2.3, dbl2 = 3.4, dbl3 = 4.5, dbl4 = 5.6, dbl5 = 6.7;
unsigned short short0 = 1;
initialize_db();
create_table();
prepare_statements();
perform_write(string0, strlen(string0), &int0, string1, strlen(string1)
, &dbl0, &dbl1, &dbl2, &dbl3, &dbl4, &dbl5
, &short0, string2, strlen(string2)
);
}
###############################################################
My best understanding is that this error indicates that one (or more) or my buffer sizes does not match the SQL datatype listed or the strlen listed. However I am failing to see where I am going wrong. Any ideas? My goal is to be able to add the 11th column in (ideally with type smallint) without having the numeric overflow error.