//-< LOCALCLI.CPP >--------------------------------------------------*--------*
// FastDB Version 1.0 (c) 1999 GARRET * ? *
// (Main Memory Database Management System) * /\| *
// * / \ *
// Created: 20-Jun-2002 K.A. Knizhnik * / [] \ *
// Last update: 20-Jun-2002 K.A. Knizhnik * GARRET *
//-------------------------------------------------------------------*--------*
// Implementation of local C interface to database
//-------------------------------------------------------------------*--------*
#define INSIDE_FASTDB
#include "localcli.h"
#include "ttree.h"
#include "hashtab.h"
#include "symtab.h"
#include <ctype.h>
dbCLI dbCLI::instance;
int cli_open(char const* server_url,
int max_connect_attempts,
int reconnect_timeout_sec)
{
return cli_bad_address;
}
int cli_create(char const* databaseName,
char const* filePath,
unsigned transactionCommitDelay,
int openAttr,
size_t initDatabaseSize,
size_t extensionQuantum,
size_t initIndexSize,
size_t fileSizeLimit)
{
return dbCLI::instance.create_session(databaseName, filePath, transactionCommitDelay, openAttr,
initDatabaseSize, extensionQuantum, initIndexSize, fileSizeLimit);
}
int dbCLI::create_session(char const* databaseName,
char const* filePath,
unsigned transactionCommitDelay,
int openAttr,
size_t initDatabaseSize,
size_t extensionQuantum,
size_t initIndexSize,
size_t fileSizeLimit)
{
dbCriticalSection cs(sessionMutex);
dbDatabase* db = NULL;
session_desc* s;
for (s = active_session_list; s != NULL; s = s->next) {
if (strcmp(s->name, databaseName) == 0) {
db = s->db;
db->accessCount += 1;
break;
}
}
if (db == NULL) {
db = new dbDatabase((openAttr & cli_open_readonly)
? (openAttr & cli_open_concurrent)
? dbDatabase::dbConcurrentRead : dbDatabase::dbReadOnly
: (openAttr & cli_open_concurrent)
? dbDatabase::dbConcurrentUpdate : dbDatabase::dbAllAccess,
initDatabaseSize,
extensionQuantum,
initIndexSize);
if (!db->open(databaseName, filePath, INFINITE, transactionCommitDelay)) {
db->close();
delete db;
return cli_database_not_found;
}
db->setFileSizeLimit(fileSizeLimit);
dbTable* table = (dbTable*)db->getRow(dbMetaTableId);
dbTableDescriptor* metatable = new dbTableDescriptor(table);
db->linkTable(metatable, dbMetaTableId);
oid_t tableId = table->firstRow;
while (tableId != 0) {
table = (dbTable*)db->getRow(tableId);
dbTableDescriptor* desc;
for (desc = db->tables; desc != NULL && desc->tableId != tableId; desc = desc->nextDbTable);
if (desc == NULL) {
desc = new dbTableDescriptor(table);
db->linkTable(desc, tableId);
desc->setFlags();
}
tableId = table->next;
}
if (!db->completeDescriptorsInitialization()) {
db->close();
delete db;
return cli_table_not_found;
}
db->accessCount = 1;
}
s = sessions.allocate();
s->name = new char[strlen(databaseName) + 1];
strcpy(s->name, databaseName);
s->db = db;
s->stmts = NULL;
s->next = active_session_list;
s->existed_tables = db->tables;
s->dropped_tables = NULL;
active_session_list = s;
return s->id;
}
int cli_create_replication_node(int nodeId,
int nServers,
char* nodeNames[],
char const* databaseName,
char const* filePath,
int openAttr,
size_t initDatabaseSize,
size_t extensionQuantum,
size_t initIndexSize,
size_t fileSizeLimit)
{
return dbCLI::instance.create_replication_node(nodeId,
nServers,
nodeNames,
databaseName,
filePath,
openAttr,
initDatabaseSize,
extensionQuantum,
initIndexSize,
fileSizeLimit);
}
int dbCLI::create_replication_node(int nodeId,
int nServers,
char* nodeNames[],
char const* databaseName,
char const* filePath,
int openAttr,
size_t initDatabaseSize,
size_t extensionQuantum,
size_t initIndexSize,
size_t fileSizeLimit)
{
#ifdef REPLICATION_SUPPORT
dbCriticalSection cs(sessionMutex);
dbDatabase* db = NULL;
session_desc* s;
for (s = active_session_list; s != NULL; s = s->next) {
if (strcmp(s->name, databaseName) == 0) {
db = s->db;
db->accessCount += 1;
break;
}
}
if (db == NULL) {
db = new dbReplicatedDatabase((openAttr & cli_open_readonly)
? (openAttr & cli_open_concurrent)
? dbDatabase::dbConcurrentRead : dbDatabase::dbReadOnly
: (openAttr & cli_open_concurrent)
? dbDatabase::dbConcurrentUpdate : dbDatabase::dbAllAccess,
initDatabaseSize,
extensionQuantum,
initIndexSize);
if (!((dbReplicatedDatabase*)db)->open(databaseName, filePath, nodeId, nodeNames, nServers)) {
return cli_database_not_found;
}
db->setFileSizeLimit(fileSizeLimit);
dbTable* table = (dbTable*)db->getRow(dbMetaTableId);
dbTableDescriptor* metatable = new dbTableDescriptor(table);
db->linkTable(metatable, dbMetaTableId);
oid_t tableId = table->firstRow;
while (tableId != 0) {
table = (dbTable*)db->getRow(tableId);
dbTableDescriptor* desc;
for (desc = db->tables; desc != NULL && desc->tableId != tableId; desc = desc->nextDbTable);
if (desc == NULL) {
desc = new dbTableDescriptor(table);
db->linkTable(desc, tableId);
desc->setFlags();
}
tableId = table->next;
}
if (!db->completeDescriptorsInitialization()) {
db->close();
delete db;
return cli_table_not_found;
}
db->accessCount = 1;
}
s = sessions.allocate();
s->name = new char[strlen(databaseName) + 1];
strcpy(s->name, databaseName);
s->db = db;
s->stmts = NULL;
s->next = active_session_list;
s->existed_tables = db->tables;
s->dropped_tables = NULL;
active_session_list = s;
return s->id;
#else
return cli_not_implemented;
#endif
}
int cli_close(int session)
{
return dbCLI::instance.close(session);
}
int dbCLI::close(int session)
{
dbCriticalSection cs(sessionMutex);
statement_desc *stmt, *next;
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbCriticalSection cs2(s->mutex);
for (stmt = s->stmts; stmt != NULL; stmt = next) {
next = stmt->next;
free_statement(stmt);
}
if (--s->db->accessCount == 0) {
dbTableDescriptor *desc, *next_desc;
for (desc = s->db->tables; desc != NULL; desc = next_desc) {
next_desc = desc->nextDbTable;
if (!desc->isStatic) {
delete desc;
}
}
s->db->tables = NULL;
s->db->close();
delete s->db;
}
while (s->dropped_tables != NULL) {
dbTableDescriptor* next = s->dropped_tables->nextDbTable;
delete s->dropped_tables;
s->dropped_tables = next;
}
session_desc** spp;
for (spp = &active_session_list; *spp != s; spp = &(*spp)->next);
*spp = s->next;
delete[] s->name;
sessions.free(s);
return cli_ok;
}
int cli_statement(int session, char const* sql)
{
return dbCLI::instance.create_statement(session, sql);
}
int dbCLI::create_statement(int session, char const* sql)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
statement_desc* stmt = statements.allocate();
stmt->sql.put(strlen(sql)+1);
strcpy(stmt->sql.base(), sql);
stmt->columns = NULL;
stmt->params = NULL;
stmt->session = s;
stmt->for_update = 0;
stmt->first_fetch = true;
stmt->prepared = false;
stmt->n_params = 0;
stmt->n_columns = 0;
stmt->record_struct = NULL;
stmt->n_autoincremented_columns = 0;
stmt->oid = 0;
stmt->updated = false;
stmt->table = NULL;
{
dbCriticalSection cs(s->mutex);
stmt->next = s->stmts;
s->stmts = stmt;
}
char const* p = sql;
parameter_binding** last = &stmt->params;
while (*p != '\0') {
if (*p == '\'') {
do {
do {
p += 1;
} while (*p != '\0' && *p != '\'');
if (*p == '\0') {
*last = NULL;
free_statement(stmt);
return cli_bad_statement;
}
} while (*++p == '\'');
} else if (*p == '%') {
stmt->n_params += 1;
char const* q = p++;
while (isalnum((unsigned char)*p) || *p == '_') p += 1;
if (*p == '%') {
*last = NULL;
free_statement(stmt);
return cli_bad_statement;
}
parameter_binding* pb = parameter_allocator.allocate();
int len = p - q;
pb->name = new char[len+1];
memcpy(pb->name, q, len);
pb->name[len] = '\0';
*last = pb;
last = &pb->next;
pb->var_ptr = NULL;
} else {
p += 1;
}
}
*last = NULL;
return stmt->id;
}
int cli_parameter(int statement,
char const* param_name,
int var_type,
void* var_ptr)
{
return dbCLI::instance.bind_parameter(statement, param_name, var_type, var_ptr);
}
int dbCLI::bind_parameter(int statement,
char const* param_name,
int var_type,
void* var_ptr)
{
if ((unsigned)var_type >= cli_array_of_oid) {
return cli_unsupported_type;
}
statement_desc* s = statements.get(statement);
if (s == NULL) {
return cli_bad_descriptor;
}
s->prepared = false;
for (parameter_binding* pb = s->params; pb != NULL; pb = pb->next) {
if (strcmp(pb->name, param_name) == 0) {
pb->var_ptr = var_ptr;
pb->var_type = var_type;
return cli_ok;
}
}
return cli_parameter_not_found;
}
int cli_column(int statement,
char const* column_name,
int var_type,
int* var_len,
void* var_ptr)
{
return dbCLI::instance.bind_column(statement, column_name, var_type, var_len, var_ptr);
}
int dbCLI::bind_column(int statement,
char const* column_name,
int var_type,
int* var_len,
void* var_ptr)
{
statement_desc* s = statements.get(statement);
if (s == NULL) {
return cli_bad_descriptor;
}
if ((unsigned)var_type >= cli_unknown) {
return cli_unsupported_type;
}
s->prepared = false;
if (var_type == cli_autoincrement) {
s->n_autoincremented_columns += 1;
}
column_binding* cb = column_allocator.allocate();
cb->name = new char[strlen(column_name) + 1];
cb->next = s->columns;
s->columns = cb;
s->n_columns += 1;
strcpy(cb->name, column_name);
cb->var_type = var_type;
cb->var_len = var_len;
cb->var_ptr = var_ptr;
cb->set_fnc = NULL;
cb->get_fnc = NULL;
return cli_ok;
}
int cli_array_column(int statement,
char const* column_name,
int var_type,
void* var_ptr,
cli_column_set set,
cli_column_get get)
{
return cli_array_column_ex(statement, column_name, var_type, var_ptr,
(cli_column_set_ex)set, (cli_column_get_ex)get);
}
int cli_array_column_ex(int statement,
char const* column_name,
int var_type,
void* var_ptr,
cli_column_set_ex set,
cli_column_get_ex get)
{
return dbCLI::instance.bind_array_column(statement, column_name, var_type, var_ptr, set, get);
}
int dbCLI::bind_array_column(int statement,
char const* column_name,
int var_type,
void* var_ptr,
cli_column_set_ex set,
cli_column_get_ex get)
{
statement_desc* s = statements.get(statement);
if (s == NULL) {
return cli_bad_descriptor;
}
if (var_type < cli_asciiz || var_type > cli_array_of_string) {
return cli_unsupported_type;
}
s->prepared = false;
column_binding* cb = new column_binding;
cb->name = new char[strlen(column_name) + 1];
cb->next = s->columns;
s->columns = cb;
s->n_columns += 1;
strcpy(cb->name, column_name);
cb->var_type = var_type;
cb->var_len = NULL;
cb->var_ptr = var_ptr;
cb->set_fnc = set;
cb->get_fnc = get;
return cli_ok;
}
int dbCLI::match_columns(char const* table_name, statement_desc* stmt)
{
stmt->table = stmt->session->db->findTable(table_name);
if (stmt->table == NULL) {
return cli_table_not_found;
}
for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next) {
cb->field = stmt->table->find(cb->name);
if (cb->field == NULL) {
return cli_column_not_found;
}
}
return cli_ok;
}
int cli_fetch(int statement, int for_update)
{
return dbCLI::instance.fetch(statement, for_update);
}
int dbCLI::fetch(int statement, int for_update)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
stmt->for_update = for_update;
stmt->oid = 0;
if (!stmt->prepared) {
int tkn;
sql_scanner scanner(stmt->sql.base());
if (scanner.get() != tkn_select) {
return cli_bad_statement;
}
if ((tkn = scanner.get()) == tkn_all) {
tkn = scanner.get();
}
if (tkn == tkn_from && scanner.get() == tkn_ident) {
int rc = match_columns(scanner.identifier(), stmt);
if (rc != cli_ok) {
return rc;
}
} else {
return cli_bad_statement;
}
char* p = scanner.current_position(), *q = p;
parameter_binding* pb = stmt->params;
stmt->query.reset();
while (*p != '\0') {
if (*p == '\'') {
do {
do {
p += 1;
} while (*p != '\0' && *p != '\'');
if (*p == '\0') {
return cli_bad_statement;
}
} while (*++p == '\'');
} else if (*p == '%') {
if (p != q) {
*p = '\0';
stmt->query.append(dbQueryElement::qExpression, q);
}
if (pb->var_ptr == NULL) {
return cli_unbound_parameter;
}
switch(pb->var_type) {
case cli_oid:
stmt->query.append(dbQueryElement::qVarReference, pb->var_ptr);
break;
case cli_bool:
stmt->query.append(dbQueryElement::qVarBool, pb->var_ptr);
break;
case cli_int1:
stmt->query.append(dbQueryElement::qVarInt1, pb->var_ptr);
break;
case cli_int2:
stmt->query.append(dbQueryElement::qVarInt2, pb->var_ptr);
break;
case cli_int4:
stmt->query.append(dbQueryElement::qVarInt4, pb->var_ptr);
break;
case cli_int8:
stmt->query.append(dbQueryElement::qVarInt8, pb->var_ptr);
break;
case cli_real4:
stmt->query.append(dbQueryElement::qVarReal4, pb->var_ptr);
break;
case cli_real8:
stmt->query.append(dbQueryElement::qVarReal8, pb->var_ptr);
break;
case cli_asciiz:
stmt->query.append(dbQueryElement::qVarString, pb->var_ptr);
break;
case cli_pasciiz:
stmt->query.append(dbQueryElement::qVarStringPtr, pb->var_ptr);
break;
case cli_array_of_oid:
stmt->query.append(dbQueryElement::qVarArrayOfRef, pb->var_ptr);
break;
default:
return cli_unsupported_type;
}
while (isalnum((unsigned char)*++p) || *p == '_');
q = p;
pb = pb->next;
} else {
p += 1;
}
}
if (p != q) {
stmt->query.append(dbQueryElement::qExpression, q);
}
stmt->prepared = true;
}
stmt->cursor.setTable(stmt->table);
stmt->cursor.reset();
return stmt->cursor.select(stmt->query, for_update ? dbCursorForUpdate : dbCursorViewOnly);
}
int dbCLI::fetch_columns(statement_desc* stmt)
{
stmt->first_fetch = false;
if (stmt->cursor.isEmpty()) {
return cli_not_found;
}
stmt->updated = false;
if (stmt->record_struct != NULL) {
stmt->cursor.fetch();
return cli_ok;
}
char* data = (char*)stmt->session->db->getRow(stmt->cursor.currId);
for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next) {
dbFieldDescriptor* fd = cb->field;
char* src = data + fd->dbsOffs;
char* dst = (char*)cb->var_ptr;
// Allow fetching of structures with one component
if (fd->type == dbField::tpStructure && fd->components->next == NULL) {
fd = fd->components;
}
switch (fd->type) {
case dbField::tpBool:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(bool*)src;
continue;
case cli_int1:
*(cli_int1_t*)dst = *(bool*)src ? 1 : 0;
continue;
case cli_int2:
*(cli_int2_t*)dst = *(bool*)src ? 1 : 0;
continue;
case cli_int4:
*(cli_int4_t*)dst = *(bool*)src ? 1 : 0;
continue;
case cli_int8:
*(db_int8*)dst = *(bool*)src ? 1 : 0;
continue;
case cli_real4:
*(cli_real4_t*)dst = (cli_real4_t)(*(bool*)src ? 1 : 0);
continue;
case cli_real8:
*(cli_real8_t*)dst = *(bool*)src ? 1 : 0;
continue;
}
break;
case dbField::tpInt1:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(int1*)src != 0;
continue;
case cli_int1:
*(cli_int1_t*)dst = *(int1*)src;
continue;
case cli_int2:
*(cli_int2_t*)dst = *(int1*)src;
continue;
case cli_int4:
*(cli_int4_t*)dst = *(int1*)src;
continue;
case cli_int8:
*(db_int8*)dst = *(int1*)src;
continue;
case cli_real4:
*(cli_real4_t*)dst = *(int1*)src;
continue;
case cli_real8:
*(cli_real8_t*)dst = *(int1*)src;
continue;
}
break;
case dbField::tpInt2:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(int2*)src != 0;
continue;
case cli_int1:
*(cli_int1_t*)dst = (int1)*(int2*)src;
continue;
case cli_int2:
*(cli_int2_t*)dst = *(int2*)src;
continue;
case cli_int4:
*(cli_int4_t*)dst = *(int2*)src;
continue;
case cli_int8:
*(db_int8*)dst = *(int2*)src;
continue;
case cli_real4:
*(cli_real4_t*)dst = *(int2*)src;
continue;
case cli_real8:
*(cli_real8_t*)dst = *(int2*)src;
continue;
}
break;
case dbField::tpInt4:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(int4*)src != 0;
continue;
case cli_int1:
*(cli_int1_t*)dst = (cli_int1_t)*(int4*)src;
continue;
case cli_int2:
*(cli_int2_t*)dst = (cli_int2_t)*(int4*)src;
continue;
case cli_int4:
case cli_autoincrement:
*(cli_int4_t*)dst = *(int4*)src;
continue;
case cli_int8:
*(db_int8*)dst = *(int4*)src;
continue;
case cli_real4:
*(cli_real4_t*)dst = (cli_real4_t)*(int4*)src;
continue;
case cli_real8:
*(cli_real8_t*)dst = *(int4*)src;
continue;
}
break;
case dbField::tpInt8:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(db_int8*)src != 0;
continue;
case cli_int1:
*(cli_int1_t*)dst = (cli_int1_t)*(db_int8*)src;
continue;
case cli_int2:
*(cli_int2_t*)dst = (cli_int2_t)*(db_int8*)src;
continue;
case cli_int4:
*(cli_int4_t*)dst = (cli_int4_t)*(db_int8*)src;
continue;
case cli_int8:
*(db_int8*)dst = *(db_int8*)src;
continue;
case cli_real4:
*(cli_real4_t*)dst = (cli_real4_t)*(db_int8*)src;
continue;
case cli_real8:
*(cli_real8_t*)dst = (cli_real8_t)*(db_int8*)src;
continue;
}
break;
case dbField::tpReal4:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(real4*)src != 0;
continue;
case cli_int1:
*(cli_int1_t*)dst = (cli_int1_t)*(real4*)src;
continue;
case cli_int2:
*(cli_int2_t*)dst = (cli_int2_t)*(real4*)src;
continue;
case cli_int4:
*(cli_int4_t*)dst = (cli_int4_t)*(real4*)src;
continue;
case cli_int8:
*(db_int8*)dst = (db_int8)*(real4*)src;
continue;
case cli_real4:
*(cli_real4_t*)dst = *(real4*)src;
continue;
case cli_real8:
*(cli_real8_t*)dst = *(real4*)src;
continue;
}
break;
case dbField::tpReal8:
switch (cb->var_type) {
case cli_bool:
*(cli_bool_t*)dst = *(real8*)src != 0;
continue;
case cli_int1:
*(cli_int1_t*)dst = (cli_int1_t)*(real8*)src;
continue;
case cli_int2:
*(cli_int2_t*)dst = (cli_int2_t)*(real8*)src;
continue;
case cli_int4:
*(cli_int4_t*)dst = (cli_int4_t)*(real8*)src;
continue;
case cli_int8:
*(db_int8*)dst = (db_int8)*(real8*)src;
continue;
case cli_real4:
*(cli_real4_t*)dst = (real4)*(real8*)src;
continue;
case cli_real8:
*(cli_real8_t*)dst = *(real8*)src;
continue;
}
break;
case dbField::tpReference:
if (cb->var_type == cli_oid) {
*(cli_oid_t*)dst = *(oid_t*)src;
continue;
}
break;
case dbField::tpString:
if (cb->var_type == cli_asciiz || cb->var_type == cli_pasciiz) {
if (cb->var_type == cli_pasciiz) {
dst = *(char**)dst;
}
dbVarying* v = (dbVarying*)src;
int size = v->size;
if (cb->set_fnc != NULL) {
dst = (char*)cb->set_fnc(cli_asciiz, dst, size,
cb->name, stmt->id, data + v->offs);
if (dst != NULL) {
memcpy(dst, data + v->offs, size);
}
} else {
int n = size;
if (cb->var_len != NULL) {
if (n > *cb->var_len) {
n = *cb->var_len;
}
*cb->var_len = size;
}
memcpy(dst, data + v->offs, n);
}
continue;
}
break;
case dbField::tpArray:
if (cb->var_type >= cli_array_of_oid && cb->var_type <= cli_array_of_string)
{
dbVarying* v = (dbVarying*)src;
int n = v->size;
src = data + v->offs;
if (cb->set_fnc != NULL) {
dst = (char*)cb->set_fnc(cb->var_type, dst, n,
cb->name, stmt->id, src);
if (dst == NULL) {
continue;
}
} else {
int size = n;
if (cb->var_len != NULL) {
if (n > *cb->var_len) {
n = *cb->var_len;
}
*cb->var_len = size;
}
}
switch (fd->components->type) {
case dbField::tpBool:
if (cb->var_type == cli_array_of_bool) {
cli_bool_t* dst_elem = (cli_bool_t*)dst;
bool* src_elem = (bool*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpInt1:
if (cb->var_type == cli_array_of_int1) {
cli_int1_t* dst_elem = (cli_int1_t*)dst;
int1* src_elem = (int1*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpInt2:
if (cb->var_type == cli_array_of_int2) {
cli_int2_t* dst_elem = (cli_int2_t*)dst;
int2* src_elem = (int2*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpInt4:
if (cb->var_type == cli_array_of_int4) {
cli_int4_t* dst_elem = (cli_int4_t*)dst;
int4* src_elem = (int4*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpInt8:
if (cb->var_type == cli_array_of_int8) {
cli_int8_t* dst_elem = (cli_int8_t*)dst;
db_int8* src_elem = (db_int8*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpReal4:
if (cb->var_type == cli_array_of_real4) {
cli_real4_t* dst_elem = (cli_real4_t*)dst;
real4* src_elem = (real4*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpReal8:
if (cb->var_type == cli_array_of_real8) {
cli_real8_t* dst_elem = (cli_real8_t*)dst;
real8* src_elem = (real8*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpReference:
if (cb->var_type == cli_array_of_oid) {
cli_oid_t* dst_elem = (cli_oid_t*)dst;
oid_t* src_elem = (oid_t*)src;
while (--n >= 0) *dst_elem++ = *src_elem++;
continue;
}
break;
case dbField::tpString:
if (cb->var_type == cli_array_of_string) {
char** dst_elem = (char**)dst;
dbVarying* src_elem = (dbVarying*)src;
while (--n >= 0) {
*dst_elem++ = (char*)((char*)src_elem + src_elem->offs);
src_elem += 1;
}
continue;
}
}
}
}
return cli_unsupported_type;
}
return cli_ok;
}
int dbCLI::store_columns(char* data, statement_desc* stmt)
{
for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next)
{
dbFieldDescriptor* fd = cb->field;
char* dst = data + fd->appOffs;
char* src = (char*)cb->var_ptr;
// Allow storing to structures with one component
if (fd->type == dbField::tpStructure && fd->components->next == NULL) {
fd = fd->components;
}
switch (fd->type) {
case dbField::tpBool:
switch (cb->var_type) {
case cli_bool:
*(bool*)dst = *(cli_bool_t*)src;
continue;
case cli_int1:
*(bool*)dst = *(cli_int1_t*)src != 0;
continue;
case cli_int2:
*(bool*)dst = *(cli_int2_t*)src != 0;
continue;
case cli_int4:
*(bool*)dst = *(cli_int4_t*)src != 0;
continue;
case cli_int8:
*(bool*)dst = *(db_int8*)src != 0;
continue;
case cli_real4:
*(bool*)dst = *(cli_real4_t*)src != 0;
continue;
case cli_real8:
*(bool*)dst = *(cli_real8_t*)src != 0;
continue;
}
break;
case dbField::tpInt1:
switch (cb->var_type) {
case cli_bool:
*(int1*)dst = *(cli_bool_t*)src ? 1 : 0;
continue;
case cli_int1:
*(int1*)dst = *(cli_int1_t*)src;
continue;
case cli_int2:
*(int1*)dst = (int1)*(cli_int2_t*)src;
continue;
case cli_int4:
*(int1*)dst = (int1)*(cli_int4_t*)src;
continue;
case cli_int8:
*(int1*)dst = (int1)*(db_int8*)src;
continue;
case cli_real4:
*(int1*)dst = (int1)*(cli_real4_t*)src;
continue;
case cli_real8:
*(int1*)dst = (int1)*(cli_real8_t*)src;
continue;
}
break;
case dbField::tpInt2:
switch (cb->var_type) {
case cli_bool:
*(int2*)dst = *(cli_bool_t*)src ? 1 : 0;
continue;
case cli_int1:
*(int2*)dst = *(cli_int1_t*)src;
continue;
case cli_int2:
*(int2*)dst = *(cli_int2_t*)src;
continue;
case cli_int4:
*(int2*)dst = (int2)*(cli_int4_t*)src;
continue;
case cli_int8:
*(int2*)dst = (int2)*(db_int8*)src;
continue;
case cli_real4:
*(int2*)dst = (int2)*(cli_real4_t*)src;
continue;
case cli_real8:
*(int2*)dst = (int2)*(cli_real8_t*)src;
continue;
}
break;
case dbField::tpInt4:
switch (cb->var_type) {
case cli_bool:
*(int4*)dst = *(cli_bool_t*)src ? 1 : 0;
continue;
case cli_int1:
*(int4*)dst = *(cli_int1_t*)src;
continue;
case cli_int2:
*(int4*)dst = *(cli_int2_t*)src;
continue;
case cli_autoincrement:
#ifdef AUTOINCREMENT_SUPPORT
*(int4*)dst = cb->field->defTable->autoincrementCount;
#endif
continue;
case cli_int4:
*(int4*)dst = *(cli_int4_t*)src;
continue;
case cli_int8:
*(int4*)dst = (int4)*(db_int8*)src;
continue;
case cli_real4:
*(int4*)dst = (int4)*(cli_real4_t*)src;
continue;
case cli_real8:
*(int4*)dst = (int4)*(cli_real8_t*)src;
continue;
}
break;
case dbField::tpInt8:
switch (cb->var_type) {
case cli_bool:
*(db_int8*)dst = *(cli_bool_t*)src ? 1 : 0;
continue;
case cli_int1:
*(db_int8*)dst = *(cli_int1_t*)src;
continue;
case cli_int2:
*(db_int8*)dst = *(cli_int2_t*)src;
continue;
case cli_int4:
*(db_int8*)dst = *(cli_int4_t*)src;
continue;
case cli_int8:
*(db_int8*)dst = *(db_int8*)src;
continue;
case cli_real4:
*(db_int8*)dst = (db_int8)*(cli_real4_t*)src;
continue;
case cli_real8:
*(db_int8*)dst = (db_int8)*(cli_real8_t*)src;
continue;
}
break;
case dbField::tpReal4:
switch (cb->var_type) {
case cli_bool:
*(real4*)dst = (real4)(*(cli_bool_t*)src ? 1 : 0);
continue;
case cli_int1:
*(real4*)dst = *(cli_int1_t*)src;
continue;
case cli_int2:
*(real4*)dst = *(cli_int2_t*)src;
continue;
case cli_int4:
*(real4*)dst = (real4)*(cli_int4_t*)src;
continue;
case cli_int8:
*(real4*)dst = (real4)*(db_int8*)src;
continue;
case cli_real4:
*(real4*)dst = *(cli_real4_t*)src;
continue;
case cli_real8:
*(real4*)dst = (real4)*(cli_real8_t*)src;
continue;
}
break;
case dbField::tpReal8:
switch (cb->var_type) {
case cli_bool:
*(real8*)dst = *(cli_bool_t*)src ? 1 : 0;
continue;
case cli_int1:
*(real8*)dst = *(cli_int1_t*)src;
continue;
case cli_int2:
*(real8*)dst = *(cli_int2_t*)src;
continue;
case cli_int4:
*(real8*)dst = *(cli_int4_t*)src;
continue;
case cli_int8:
*(real8*)dst = (real8)*(db_int8*)src;
continue;
case cli_real4:
*(real8*)dst = *(cli_real4_t*)src;
continue;
case cli_real8:
*(real8*)dst = *(cli_real8_t*)src;
continue;
}
break;
case dbField::tpReference:
if (cb->var_type == cli_oid) {
*(oid_t*)dst = *(cli_oid_t*)src;
continue;
}
break;
case dbField::tpString:
if (cb->var_type == cli_pasciiz) {
*(char**)dst = *(char**)src;
continue;
} else if (cb->var_type == cli_asciiz) {
if (cb->get_fnc != NULL) {
int len;
src = (char*)cb->get_fnc(cb->var_type, src, &len,
cb->name, stmt->id);
}
*(char**)dst = src;
continue;
}
break;
case dbField::tpArray:
if (cb->var_type >= cli_array_of_oid && cb->var_type <= cli_array_of_string)
{
int size = 0;
if (cb->get_fnc != NULL) {
src = (char*)cb->get_fnc(cb->var_type, src, &size,
cb->name, stmt->id);
} else {
if (cb->var_len != NULL) {
size = *cb->var_len;
} else {
return cli_incompatible_type;
}
}
if (cb->var_type == cli_array_of_string) {
if (fd->components->type != dbField::tpString) {
return cli_incompatible_type;
}
} else if ((size_t)sizeof_type[cb->var_type - cli_array_of_oid] != fd->components->appSize) {
return cli_incompatible_type;
}
dbAnyArray::arrayAllocator((dbAnyArray*)dst, src, size);
continue;
}
}
return cli_unsupported_type;
}
return cli_ok;
}
int cli_insert(int statement, cli_oid_t* oid)
{
return dbCLI::instance.insert(statement, oid);
}
int dbCLI::insert(int statement, cli_oid_t* oid)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
sql_scanner scanner(stmt->sql.base());
if (scanner.get() != tkn_insert
|| scanner.get() != tkn_into
|| scanner.get() != tkn_ident)
{
return cli_bad_statement;
}
int rc = match_columns(scanner.identifier(), stmt);
if (rc != cli_ok) {
return rc;
}
stmt->prepared = true;
}
dbSmallBuffer buf(stmt->table->appSize);
char* obj = buf.base();
memset(obj, 0, stmt->table->appSize);
dbFieldDescriptor *first = stmt->table->columns, *fd = first;
do {
if (fd->appType == dbField::tpString) {
*(const char**)(obj + fd->appOffs) = "";
}
} while ((fd = fd->next) != first);
int rc = store_columns(buf.base(), stmt);
if (rc != cli_ok) {
return rc;
}
dbAnyReference ref;
stmt->session->db->insertRecord(stmt->table, &ref, buf.base());
stmt->oid = ref.getOid();
if (oid != NULL) {
*oid = ref.getOid();
}
if (stmt->n_autoincremented_columns > 0) {
for (column_binding* cb = stmt->columns; cb != NULL; cb = cb->next) {
if (cb->var_type == cli_autoincrement) {
*(cli_int4_t*)cb->var_ptr = ref.getOid();
}
}
}
return cli_ok;
}
int cli_update(int statement)
{
return dbCLI::instance.update(statement);
}
int dbCLI::update(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
if (!stmt->for_update) {
return cli_not_update_mode;
}
if (stmt->updated) {
return cli_already_updated;
}
if (stmt->cursor.isEmpty()) {
return cli_not_found;
}
if (stmt->record_struct == NULL) {
dbSmallBuffer buf(stmt->table->appSize);
char* record = buf.base();
memset(record, 0, stmt->table->appSize);
stmt->cursor.setRecord((byte*)record);
stmt->cursor.fetch();
int rc = store_columns(buf.base(), stmt);
if (rc != cli_ok) {
return rc;
}
}
stmt->cursor.update();
stmt->updated = true;
return cli_ok;
}
int cli_freeze(int statement)
{
return dbCLI::instance.freeze(statement);
}
int dbCLI::freeze(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
stmt->cursor.freeze();
return cli_ok;
}
int cli_unfreeze(int statement)
{
return dbCLI::instance.unfreeze(statement);
}
int dbCLI::unfreeze(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
stmt->cursor.unfreeze();
return cli_ok;
}
int cli_get_first(int statement)
{
return dbCLI::instance.get_first(statement);
}
int dbCLI::get_first(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
if (!stmt->cursor.gotoFirst()) {
return cli_not_found;
}
return fetch_columns(stmt);
}
int cli_get_last(int statement)
{
return dbCLI::instance.get_last(statement);
}
int dbCLI::get_last(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
if (!stmt->cursor.gotoLast()) {
return cli_not_found;
}
return fetch_columns(stmt);
}
int cli_get_next(int statement)
{
return dbCLI::instance.get_next(statement);
}
int dbCLI::get_next(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
if (!((stmt->first_fetch && stmt->cursor.gotoFirst()) ||
(!stmt->first_fetch && stmt->cursor.gotoNext())))
{
return cli_not_found;
}
return fetch_columns(stmt);
}
int cli_get_prev(int statement)
{
return dbCLI::instance.get_prev(statement);
}
int dbCLI::get_prev(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
if (!((stmt->first_fetch && stmt->cursor.gotoLast()) ||
(!stmt->first_fetch && stmt->cursor.gotoPrev())))
{
return cli_not_found;
}
return fetch_columns(stmt);
}
int cli_skip(int statement, int n)
{
return dbCLI::instance.skip(statement, n);
}
int dbCLI::skip(int statement, int n)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
if ((n > 0 && !((stmt->first_fetch && stmt->cursor.gotoFirst() && stmt->cursor.skip(n-1)
|| (!stmt->first_fetch && stmt->cursor.skip(n)))))
|| (n < 0 && !((stmt->first_fetch && stmt->cursor.gotoLast() && stmt->cursor.skip(n+1)
|| (!stmt->first_fetch && stmt->cursor.skip(n))))))
{
return cli_not_found;
}
return fetch_columns(stmt);
}
int cli_seek(int statement, cli_oid_t oid)
{
return dbCLI::instance.seek(statement, oid);
}
int dbCLI::seek(int statement, cli_oid_t oid)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
if (!stmt->prepared) {
return cli_not_fetched;
}
int pos = stmt->cursor.seek(oid);
if (pos < 0) {
return cli_not_found;
}
int rc = fetch_columns(stmt);
if (rc == cli_ok) {
return pos;
} else {
return rc;
}
}
cli_oid_t cli_get_oid(int statement)
{
return dbCLI::instance.get_current_oid(statement);
}
cli_oid_t dbCLI::get_current_oid(int statement)
{
statement_desc* s = statements.get(statement);
if (s == NULL) {
return cli_bad_descriptor;
}
return s->cursor.currId;
}
int cli_free(int statement)
{
return dbCLI::instance.free_statement(statement);
}
int dbCLI::free_statement(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL) {
return cli_bad_descriptor;
}
return free_statement(stmt);
}
int dbCLI::free_statement(statement_desc* stmt)
{
{
dbCriticalSection cs(stmt->session->mutex);
statement_desc *sp, **spp = &stmt->session->stmts;
while ((sp = *spp) != stmt) {
if (sp == NULL) {
return cli_bad_descriptor;
}
spp = &sp->next;
}
*spp = stmt->next;
}
column_binding *cb, *next_cb;
for (cb = stmt->columns; cb != NULL; cb = next_cb) {
next_cb = cb->next;
delete[] cb->name;
column_allocator.free(cb);
}
parameter_binding *pb, *next_pb;
for (pb = stmt->params; pb != NULL; pb = next_pb) {
next_pb = pb->next;
delete[] pb->name;
parameter_allocator.free(pb);
}
statements.free(stmt);
return cli_ok;
}
int cli_commit(int session)
{
return dbCLI::instance.commit(session);
}
int dbCLI::commit(int session)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
while (s->dropped_tables != NULL) {
dbTableDescriptor* next = s->dropped_tables->nextDbTable;
delete s->dropped_tables;
s->dropped_tables = next;
}
s->db->commit();
s->existed_tables = s->db->tables;
return cli_ok;
}
int cli_precommit(int session)
{
return dbCLI::instance.precommit(session);
}
int dbCLI::precommit(int session)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
s->db->precommit();
return cli_ok;
}
int cli_abort(int session)
{
return dbCLI::instance.abort(session);
}
int dbCLI::abort(int session)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbDatabase* db = s->db;
while (s->dropped_tables != NULL) {
dbTableDescriptor* next = s->dropped_tables->nextDbTable;
db->linkTable(s->dropped_tables, s->dropped_tables->tableId);
s->dropped_tables = next;
}
s->db->rollback();
while (db->tables != s->existed_tables) {
dbTableDescriptor* table = db->tables;
db->unlinkTable(table);
delete table;
}
return cli_ok;
}
int cli_remove(int statement)
{
return dbCLI::instance.remove(statement);
}
int dbCLI::remove(int statement)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL || !stmt->prepared) {
return cli_bad_descriptor;
}
if (!stmt->for_update) {
return cli_not_update_mode;
}
if (stmt->cursor.isEmpty()) {
return cli_not_found;
}
stmt->cursor.removeAllSelected();
return cli_ok;
}
int cli_describe(int session, char const* table, cli_field_descriptor** fields)
{
return dbCLI::instance.describe(session, table, fields);
}
int dbCLI::describe(int session, char const* table, cli_field_descriptor** fields)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbDatabase* db = s->db;
dbTableDescriptor* desc = db->findTableByName(table);
if (desc == NULL) {
return cli_table_not_found;
} else {
int nColumns = desc->nColumns;
cli_field_descriptor* fp =
(cli_field_descriptor*)malloc(nColumns*sizeof(cli_field_descriptor));
dbFieldDescriptor* fd = desc->columns;
*fields = fp;
for (int i = 0; i < nColumns; i++, fp++) {
fp->type = (cli_var_type)map_type(fd);
fp->name = fd->name;
fp->refTableName = (fd->type == dbField::tpArray) ? fd->components->refTableName : fd->refTableName;
fp->inverseRefFieldName = fd->inverseRefName;
fp->flags = 0;
if (fd->tTree != 0) {
fp->flags |= cli_indexed;
}
if (fd->hashTable != 0) {
fp->flags |= cli_hashed;
}
fd = fd->next;
}
return nColumns;
}
}
int cli_show_tables(int session, cli_table_descriptor** tables)
{
return dbCLI::instance.show_tables(session, tables);
}
int dbCLI::show_tables(int session, cli_table_descriptor** tables)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbTableDescriptor* desc;
int nTables = 0;
for (desc = s->db->tables; desc != NULL; desc = desc->nextDbTable) {
if (strcmp(desc->name, "Metatable")) {
nTables += 1;
}
}
if (nTables != 0) {
cli_table_descriptor* td = (cli_table_descriptor*)malloc(nTables*sizeof(cli_table_descriptor));
*tables = td;
for (desc = s->db->tables; desc != NULL; desc = desc->nextDbTable) {
if (strcmp(desc->name, "Metatable")) {
td->name = desc->name;
td += 1;
}
}
} else {
*tables = NULL;
}
return nTables;
}
#define MAX_QUERY_INDETIFIER_LENGTH 256
int sql_scanner::get()
{
char buf[MAX_QUERY_INDETIFIER_LENGTH];
int i = 0, ch;
do {
ch = *p++;
if (ch == '\0') {
return tkn_eof;
}
} while (ch > 0 && ch <= 32);
if (ch == '*') {
return tkn_all;
} else if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-') {
int const_type = tkn_iconst;
while (true) {
ch = *p++;
if (ch == '.' || ch == 'e' || ch == 'E') {
const_type = tkn_fconst;
} else if (!((ch >= '0' && ch <= '9') || ch == '+' || ch == '-')) {
break;
}
}
return const_type;
} else if (isalnum(ch) || ch == '$' || ch == '_') {
do {
buf[i++] = ch;
if (i == MAX_QUERY_INDETIFIER_LENGTH) {
// Identifier too long
return tkn_error;
}
ch = *p++;
} while (ch != EOF && (isalnum(ch) || ch == '$' || ch == '_'));
p -= 1;
buf[i] = '\0';
ident = buf;
return dbSymbolTable::add(ident, tkn_ident);
} else {
// Invalid symbol
return tkn_error;
}
}
int cli_create_table(int session, char const* tableName, int nColumns,
cli_field_descriptor* columns)
{
return dbCLI::instance.create_table(session, tableName, nColumns, columns);
}
int dbCLI::create_table(int session, char const* tableName, int nColumns,
cli_field_descriptor* columns)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
s->db->beginTransaction(dbDatabase::dbExclusiveLock);
if (s->existed_tables == NULL) {
s->existed_tables = s->db->tables;
}
return create_table(s->db, tableName, nColumns, columns);
}
int dbCLI::create_table(dbDatabase* db, char const* tableName, int nColumns,
cli_field_descriptor* columns)
{
int i;
db->modified = true;
if (db->findTableByName(tableName) != NULL) {
return cli_table_already_exists;
}
int nFields = nColumns;
int varyingLength = strlen(tableName) + 1;
for (i = 0; i < nColumns; i++) {
int type = columns[i].type;
varyingLength += strlen(columns[i].name) + 3;
if (type == cli_oid || type == cli_array_of_oid) {
varyingLength += strlen(columns[i].refTableName);
if (columns[i].inverseRefFieldName != NULL) {
varyingLength += strlen(columns[i].inverseRefFieldName);
}
}
switch (type) {
case cli_oid:
case cli_bool:
case cli_int1:
case cli_int2:
case cli_int4:
case cli_autoincrement:
case cli_int8:
case cli_real4:
case cli_real8:
case cli_asciiz:
case cli_pasciiz:
break;
case cli_array_of_oid:
case cli_array_of_bool:
case cli_array_of_int1:
case cli_array_of_int2:
case cli_array_of_int4:
case cli_array_of_int8:
case cli_array_of_real4:
case cli_array_of_real8:
case cli_array_of_string:
varyingLength += strlen(columns[i].name) + 2 + 3;
nFields += 1;
break;
case cli_unknown:
return cli_unsupported_type;
}
}
db->beginTransaction(dbDatabase::dbExclusiveLock);
oid_t oid = db->allocateRow(dbMetaTableId,
sizeof(dbTable) + sizeof(dbField)*nFields + varyingLength);
dbTable* table = (dbTable*)db->putRow(oid);
int offs = sizeof(dbTable) + sizeof(dbField)*nFields;
table->name.offs = offs;
table->name.size = strlen(tableName)+1;
strcpy((char*)table + offs, tableName);
offs += table->name.size;
size_t size = sizeof(dbRecord);
table->fields.offs = sizeof(dbTable);
int fieldOffs = table->fields.offs;
dbField* field = (dbField*)((char*)table + fieldOffs);
offs -= sizeof(dbTable);
for (i = 0; i < nColumns; i++, fieldOffs += sizeof(dbField), field++, offs -= sizeof(dbField)) {
field->name.offs = offs;
field->name.size = strlen(columns[i].name) + 1;
strcpy((char*)field + offs, columns[i].name);
offs += field->name.size;
field->tableName.offs = offs;
int type = columns[i].type;
if (type == cli_oid || type == cli_array_of_oid) {
if (type == cli_oid) {
field->tableName.size = strlen(columns[i].refTableName) + 1;
strcpy((char*)field + offs, columns[i].refTableName);
offs += field->tableName.size;
} else {
field->tableName.size = 1;
*((char*)field + offs) = '\0';
offs += 1;
}
field->inverse.offs = offs;
if (columns[i].inverseRefFieldName != NULL) {
field->inverse.size = strlen(columns[i].inverseRefFieldName) + 1;
strcpy((char*)field + offs, columns[i].inverseRefFieldName);
offs += field->inverse.size;
} else {
field->inverse.size = 1;
*((char*)field + offs) = '\0';
offs += 1;
}
} else {
field->tableName.size = 1;
*((char*)field + offs) = '\0';
offs += 1;
field->inverse.size = 1;
field->inverse.offs = offs;
*((char*)field + offs) = '\0';
offs += 1;
}
field->tTree = field->hashTable = 0;
switch (type) {
case cli_oid:
field->type = dbField::tpReference;
field->size = sizeof(oid_t);
break;
case cli_bool:
field->type = dbField::tpBool;
field->size = sizeof(bool);
break;
case cli_int1:
field->type = dbField::tpInt1;
field->size = sizeof(int1);
break;
case cli_int2:
field->type = dbField::tpInt2;
field->size = sizeof(int2);
break;
case cli_int4:
case cli_autoincrement:
field->type = dbField::tpInt4;
field->size = sizeof(int4);
break;
case cli_int8:
field->type = dbField::tpInt8;
field->size = sizeof(db_int8);
break;
case cli_real4:
field->type = dbField::tpReal4;
field->size = sizeof(real4);
break;
case cli_real8:
field->type = dbField::tpReal8;
field->size = sizeof(real8);
break;
case cli_asciiz:
case cli_pasciiz:
field->type = dbField::tpString;
field->size = sizeof(dbVarying);
field->offset = DOALIGN(size, sizeof(int4));
size = field->offset + sizeof(dbVarying);
if (columns[i].flags & cli_hashed) {
oid_t hashOid = dbHashTable::allocate(db);
table = (dbTable*)db->getRow(oid);
field = (dbField*)((char*)table + fieldOffs);
field->hashTable = hashOid;
} else {
field->hashTable = 0;
}
if (columns[i].flags & cli_indexed) {
oid_t treeOid = dbTtree::allocate(db);
table = (dbTable*)db->getRow(oid);
field = (dbField*)((char*)table + fieldOffs);
field->tTree = treeOid;
} else {
field->tTree = 0;
}
continue;
case cli_array_of_oid:
case cli_array_of_bool:
case cli_array_of_int1:
case cli_array_of_int2:
case cli_array_of_int4:
case cli_array_of_int8:
case cli_array_of_real4:
case cli_array_of_real8:
case cli_array_of_string:
field->type = dbField::tpArray;
field->size = sizeof(dbVarying);
field->offset = DOALIGN(size, sizeof(int4));
size = field->offset + sizeof(dbVarying);
field->hashTable = field->tTree = 0;
field += 1;
fieldOffs += sizeof(dbField);
offs -= sizeof(dbField);
field->name.offs = offs;
field->name.size = strlen(columns[i].name) + 3;
sprintf((char*)field + offs, "%s[]", columns[i].name);
offs += field->name.size;
field->tableName.offs = offs;
if (type == cli_array_of_oid) {
field->tableName.size = strlen(columns[i].refTableName) + 1;
strcpy((char*)field + offs, columns[i].refTableName);
offs += field->tableName.size;
} else {
field->tableName.size = 1;
*((char*)field + offs) = '\0';
offs += 1;
}
field->inverse.offs = offs;
field->inverse.size = 1;
*((char*)field + offs) = '\0';
offs += 1;
field->offset = 0;
field->hashTable = field->tTree = 0;
switch (type) {
case cli_array_of_oid:
field->type = dbField::tpReference;
field->size = sizeof(oid_t);
break;
case cli_array_of_bool:
field->type = dbField::tpBool;
field->size = sizeof(bool);
break;
case cli_array_of_int1:
field->type = dbField::tpInt1;
field->size = sizeof(int1);
break;
case cli_array_of_int2:
field->type = dbField::tpInt2;
field->size = sizeof(int2);
break;
case cli_array_of_int4:
field->type = dbField::tpInt4;
field->size = sizeof(int4);
break;
case cli_array_of_int8:
field->type = dbField::tpInt8;
field->size = sizeof(db_int8);
break;
case cli_array_of_real4:
field->type = dbField::tpReal4;
field->size = sizeof(real4);
break;
case cli_array_of_real8:
field->type = dbField::tpReal8;
field->size = sizeof(real8);
break;
case cli_array_of_string:
field->type = dbField::tpString;
field->size = sizeof(dbVarying);
break;
}
continue;
}
if (columns[i].flags & cli_hashed) {
oid_t hashOid = dbHashTable::allocate(db);
table = (dbTable*)db->getRow(oid);
field = (dbField*)((char*)table + fieldOffs);
field->hashTable = hashOid;
} else {
field->hashTable = 0;
}
if (columns[i].flags & cli_indexed) {
oid_t treeOid = dbTtree::allocate(db);
table = (dbTable*)db->getRow(oid);
field = (dbField*)((char*)table + fieldOffs);
field->tTree = treeOid;
} else {
field->tTree = 0;
}
field->offset = DOALIGN(size, field->size);
size = field->offset + sizeof(dbVarying);
}
table->fields.size = nFields;
table->fixedSize = size;
table->nRows = 0;
table->nColumns = nColumns;
table->firstRow = 0;
table->lastRow = 0;
db->linkTable(new dbTableDescriptor(table), oid);
if (!db->completeDescriptorsInitialization()) {
return cli_table_not_found;
}
return cli_ok;
}
int cli_drop_table(int session, char const* tableName)
{
return dbCLI::instance.drop_table(session, tableName);
}
int dbCLI::drop_table(int session, char const* tableName)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbDatabase* db = s->db;
dbTableDescriptor* desc = db->findTableByName(tableName);
if (desc == NULL) {
return cli_table_not_found;
}
db->dropTable(desc);
if (desc == s->existed_tables) {
s->existed_tables = desc->nextDbTable;
}
db->unlinkTable(desc);
desc->nextDbTable = s->dropped_tables;
s->dropped_tables = desc;
return cli_ok;
}
int cli_alter_index(int session, char const* tableName, char const* fieldName, int newFlags)
{
return dbCLI::instance.alter_index(session, tableName, fieldName, newFlags);
}
int dbCLI::alter_index(int session, char const* tableName, char const* fieldName, int newFlags)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
return alter_index(s->db, tableName, fieldName, newFlags);
}
int dbCLI::alter_index(dbDatabase* db, char const* tableName, char const* fieldName, int newFlags)
{
db->beginTransaction(dbDatabase::dbExclusiveLock);
dbTableDescriptor* desc = db->findTableByName(tableName);
if (desc == NULL) {
return cli_table_not_found;
}
dbFieldDescriptor* fd = desc->find(fieldName);
if (fd == NULL) {
return cli_column_not_found;
}
if (fd->tTree != 0 && (newFlags & cli_indexed) == 0) {
db->dropIndex(fd);
}
if (fd->hashTable != 0 && (newFlags & cli_hashed) == 0) {
db->dropHashTable(fd);
}
if (fd->tTree == 0 && (newFlags & cli_indexed) != 0) {
db->createIndex(fd);
}
if (fd->hashTable == 0 && (newFlags & cli_hashed) != 0) {
db->createHashTable(fd);
}
return cli_ok;
}
cli_error_handler cli_set_error_handler(int session, cli_error_handler new_handler)
{
return dbCLI::instance.set_error_handler(session, new_handler);
}
cli_error_handler dbCLI::set_error_handler(int session, cli_error_handler new_handler)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return NULL;
}
return (cli_error_handler)s->db->setErrorHandler(dbDatabase::dbErrorHandler(new_handler));
}
int cli_attach(int session)
{
return dbCLI::instance.attach(session);
}
int dbCLI::attach(int session)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
s->db->attach();
return cli_ok;
}
int cli_detach(int session, int detach_mode)
{
return dbCLI::instance.detach(session, detach_mode);
}
int dbCLI::detach(int session, int detach_mode)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
s->db->detach(detach_mode);
return cli_ok;
}
void cli_free_memory(int, void* ptr)
{
free(ptr);
}
int cli_get_database_state(int session, cli_database_monitor* monitor)
{
return dbCLI::instance.get_database_state(session, monitor);
}
int dbCLI::get_database_state(int session, cli_database_monitor* monitor) {
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbMonitor* dbm = s->db->monitor;
monitor->n_readers = dbm->nReaders;
monitor->n_writers = dbm->nWriters;
monitor->n_blocked_readers = dbm->nWaitReaders;
monitor->n_blocked_writers = dbm->nWaitWriters;
monitor->n_users = dbm->users;
return cli_ok;
}
void cli_set_trace_function(cli_trace_function_t func)
{
dbTraceFunction = func;
}
int cli_prepare_query(int session, char const* query)
{
return dbCLI::instance.prepare_query(session, query);
}
int dbCLI::prepare_query(int session, char const* query)
{
char *p, *q;
int tkn;
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
statement_desc* stmt = statements.allocate();
stmt->columns = NULL;
stmt->params = NULL;
stmt->session = s;
stmt->for_update = 0;
stmt->first_fetch = true;
stmt->prepared = false;
stmt->n_params = 0;
stmt->n_columns = 0;
stmt->n_autoincremented_columns = 0;
stmt->oid = 0;
stmt->updated = false;
stmt->query.reset();
stmt->sql.put(strlen(query)+1);
p = stmt->sql.base();
strcpy(p, query);
sql_scanner scanner(p);
if (scanner.get() != tkn_select) {
statements.free(stmt);
return cli_bad_statement;
}
if ((tkn = scanner.get()) == tkn_all) {
tkn = scanner.get();
}
if (tkn != tkn_from || scanner.get() != tkn_ident) {
statements.free(stmt);
return cli_bad_statement;
}
stmt->table = s->db->findTable(scanner.identifier());
if (stmt->table == NULL) {
statements.free(stmt);
return cli_table_not_found;
}
p = scanner.current_position();
q = p;
int offs = 0;
while (*p != '\0') {
if (*p == '\'') {
do {
do {
p += 1;
} while (*p != '\0' && *p != '\'');
if (*p == '\0') {
statements.free(stmt);
return cli_bad_statement;
}
} while (*++p == '\'');
} else if (*p == '%') {
if (p != q) {
*p = '\0';
stmt->query.append(dbQueryElement::qExpression, q);
}
switch (*++p) {
case 'd':
case 'i':
stmt->query.append(dbQueryElement::qVarInt4, (void*)offs);
offs += sizeof(cli_int4_t);
break;
case 'f':
offs = DOALIGN(offs, sizeof(cli_real8_t));
stmt->query.append(dbQueryElement::qVarReal8, (void*)offs);
offs += sizeof(cli_real8_t);
break;
case 'p':
offs = DOALIGN(offs, sizeof(cli_oid_t));
stmt->query.append(dbQueryElement::qVarReference, (void*)offs);
offs += sizeof(cli_oid_t);
break;
case 'l':
case 'L':
p += 1;
if (*p != 'd' && *p != 'i') {
statements.free(stmt);
return cli_bad_statement;
}
offs = DOALIGN(offs, sizeof(cli_int8_t));
stmt->query.append(dbQueryElement::qVarInt8, (void*)offs);
offs += sizeof(cli_int8_t);
break;
case 's':
offs = DOALIGN(offs, sizeof(char*));
stmt->query.append(dbQueryElement::qVarStringPtr, (void*)offs);
offs += sizeof(char*);
break;
default:
statements.free(stmt);
return cli_bad_statement;
}
p += 1;
q = p;
} else {
p += 1;
}
}
if (p != q) {
stmt->query.append(dbQueryElement::qExpression, q);
}
stmt->param_size = offs;
{
dbCriticalSection cs(s->mutex);
stmt->next = s->stmts;
s->stmts = stmt;
}
stmt->prepared = true;
return stmt->id;
}
int cli_execute_query(int statement, int for_update, void* record_struct, ...)
{
va_list args;
va_start(args, record_struct);
int rc = dbCLI::instance.execute_query(statement, for_update, record_struct, args);
va_end(args);
return rc;
}
int dbCLI::execute_query(int statement, int for_update, void* record_struct, va_list params)
{
statement_desc* stmt = statements.get(statement);
if (stmt == NULL || !stmt->prepared) {
return cli_bad_descriptor;
}
stmt->for_update = for_update;
stmt->oid = 0;
dbSmallBuffer paramBuf(stmt->param_size);
char* paramBase = paramBuf.base();
int offs = 0;
dbQueryElement* elem = stmt->query.elements;
while (elem != NULL) {
switch (elem->type) {
case dbQueryElement::qVarInt4:
*(cli_int4_t*)(paramBase + offs) = va_arg(params, cli_int4_t);
offs += sizeof(cli_int4_t);
break;
case dbQueryElement::qVarInt8:
offs = DOALIGN(offs, sizeof(cli_int8_t));
*(cli_int8_t*)(paramBase + offs) = va_arg(params, cli_int8_t);
offs += sizeof(cli_int8_t);
break;
case dbQueryElement::qVarReal8:
offs = DOALIGN(offs, sizeof(cli_real8_t));
*(cli_real8_t*)(paramBase + offs) = va_arg(params, cli_real8_t);
offs += sizeof(cli_real8_t);
break;
case dbQueryElement::qVarStringPtr:
offs = DOALIGN(offs, sizeof(char*));
*(char**)(paramBase + offs) = va_arg(params, char*);
offs += sizeof(char*);
break;
case dbQueryElement::qVarReference:
offs = DOALIGN(offs, sizeof(cli_oid_t));
*(cli_oid_t*)(paramBase + offs) = va_arg(params, cli_oid_t);
offs += sizeof(cli_oid_t);
break;
/////////////////////////////////////////////////////////////////////////////////////
// Note: The compiler generated the following warnings. As I am not familiar
// with this code, and I am trying to reduce the number of warnings that must
// be ignored, I added cases for ALL BUT ONE of the messages. Leaving one of
// out (qExpression) means that the warning will still be generated, just in
// case someone gets motivated to figure out what this code is doing, and what
// should really be done in the omitted cases.
//
// This code should behave no differently from before. The added "default"
// case just executes a "break;", which should be equivalent to the implicit
// action for the omitted cases.
//
// localcli.cpp: warning: enumeration value `qExpression' not handled in switch
// localcli.cpp: warning: enumeration value `qVarBool' not handled in switch
// localcli.cpp: warning: enumeration value `qVarInt1' not handled in switch
// localcli.cpp: warning: enumeration value `qVarInt2' not handled in switch
// localcli.cpp: warning: enumeration value `qVarReal4' not handled in switch
// localcli.cpp: warning: enumeration value `qVarString' not handled in switch
// localcli.cpp: warning: enumeration value `qVarArrayOfRef' not handled in switch
// localcli.cpp: warning: enumeration value `qVarArrayOfRefPtr' not handled in switch
// localcli.cpp: warning: enumeration value `qVarRawData' not handled in switch
/////////////////////////////////////////////////////////////////////////////////////
// "DEFAULT:"
//case dbQueryElement::qExpression:
case dbQueryElement::qVarArrayOfRef:
case dbQueryElement::qVarArrayOfRefPtr:
case dbQueryElement::qVarBool:
case dbQueryElement::qVarInt1:
case dbQueryElement::qVarInt2:
case dbQueryElement::qVarRawData:
case dbQueryElement::qVarReal4:
case dbQueryElement::qVarString:
break;
}
elem = elem->next;
}
stmt->record_struct = record_struct;
stmt->cursor.setTable(stmt->table);
stmt->cursor.reset();
stmt->cursor.setRecord(record_struct);
return stmt->cursor.select(stmt->query, for_update ? dbCursorForUpdate : dbCursorViewOnly, paramBase);
}
int cli_insert_struct(int session, char const* table_name, void* record_struct, cli_oid_t* oid)
{
return dbCLI::instance.insert_struct(session, table_name, record_struct, oid);
}
int dbCLI::insert_struct(int session, char const* table_name, void* record_struct, cli_oid_t* oid)
{
session_desc* s = sessions.get(session);
if (s == NULL) {
return cli_bad_descriptor;
}
dbTableDescriptor* table = s->db->findTableByName(table_name);
if (table == NULL) {
return cli_table_not_found;
}
dbAnyReference ref;
s->db->insertRecord(table, &ref, record_struct);
if (oid != NULL) {
*oid = (cli_oid_t)ref.getOid();
}
return cli_ok;
}
syntax highlighted by Code2HTML, v. 0.9.1