This part of Ovrimos documentation contains bits and pieces of C code as well as a few examples, such as using a cipher etc.
It is useful information that did not fit in the normal flow of the document, because it is slightly more technical than what the average user might find interesting in the first reading.
We have mentioned in 13.3.(Data Encryption) that users may provide their own ciphers.
Ovrimos can use any 64-bit block cipher. The user can specify as value of the
CIPHER parameter a shared object file. (a .so in Unix ot .dll
in Windows) and a function name, for example:
CIPHER=nullcipher.dll nullcipher
It is possible to keep many different ciphers in one object file.
At run-time (i.e. when the database is started), the shared object file is dynamically linked to Ovrimos and the address of the function specified is resolved. The function is called to provide Ovrimos with the address of a 'cipher' struct, which defines the interface needed by Ovrimos to use the corresponding cipher. All ciphers must return a pointer to this structure to be used by Ovrimos.
Below follows the definition of that struct.
typedef struct { const char *name; void* (*cipher_new_context)(); void (*cipher_key) (void *ctx, void *key); void (*cipher_delete_context) (void *ctx); void (*cipher_encrypt) (void *ctx, void *d); void (*cipher_decrypt) (void *ctx, void *d); } cipher;
Each cipher should be able to create its own context, which holds initialized data for the particular algorithm. The first three functions of the struct create a context, populate it with a key, and destroy it.
The other two functions perform encryption and decryption on a 64-bit block (in-place). The implementation of the CBC mode is done internally by Ovrimos.
The following example implements the null cipher ("None").
The declaration "_declspec(dllexport)" is Visual C++ specific to define an exported function.
The nullcipher |
/* nullcipher.c */ typedef struct { const char *name; void* (*cipher_new_context)(); void (*cipher_key)(void *ctx, void *key); void (*cipher_delete_context)(void *ctx); void (*cipher_encrypt)(void *ctx, void *d); void (*cipher_decrypt)(void *ctx, void *d); } cipher; typedef cipher* (*cipher_impl)(); static void *null_cipher_new_context(); static void null_cipher_key(void *ctx, uint32 *key); static void null_cipher_delete_context(void *ctx); static void null_cipher_encrypt(void *ctx, uint32 *d); static void null_cipher_decrypt(void *ctx, uint32 *d); static cipher the_nullcipher={ "None", null_cipher_new_context, null_cipher_key, null_cipher_delete_context, null_cipher_encrypt, null_cipher_decrypt }; __declspec( dllexport ) cipher *nullcipher() { return &the_nullcipher; } static void *null_cipher_new_context() { return 0; } static void null_cipher_key(void *ctx, uint32 *key) { } static void null_cipher_delete_context(void *ctx) { } static void null_cipher_encrypt(void *ctx, uint32 *d) { } static void null_cipher_decrypt(void *ctx, uint32 *d) { }
Ovrimos provides developers with a C, C++ client library, to allow connections to
databases for storage, handling and retrieval of relational data. The description
of the client libary functions is beyond the scope of this documentation, therefore, it
is not provided here. You may contact support@ovrimos.com for a description
of the library functions.
A few examples of C code, however, were considered essential for demonstrating all
of Ovrimos functionality.
The following code is an example of a C program, making a connection to the database and requesting data from the table Books. The description of Books may be found in B.3.(Sample Data).
Database connection and selection of data |
#include <stdlib.h> #include "sqlcli.h" int main() { SQLH conn; SQLS stmt; /* Establish connection */ if(!sqlConnect("localhost","3000","admin","pegasus",&conn)) { char *msg; /* Determine the reason of failure */ c_outcome outc=sqlConnectOutcome(); switch(outc) { case c_conn_failed: msg="Host unreachable\n"; break; case c_trans_failed: msg="Negotiation with host failed\n"; break; case c_auth_failed: msg="Authentication failed or license exceeded\n"; break; case c_ok: msg="Database configuration could not be read"; break; /*NT*/ } puts(msg); exit(1); } if(sqlAllocStmt(conn,&stmt)) { if(sqlExecDirect(stmt,"select isbn,title,price from books")) { sqlGetOutputColDescr(stmt); sqlSetRowsetSize(stmt,1); while(sqlCursorNext(stmt,0)) { const char *isbn=sqlColValueString(stmt,0,0); const char *title=sqlColValueString(stmt,1,0); double price=sqlColValueDouble(stmt,2,0); printf("isbn=\"%s\", title=\"%s\", price=%lf\n",isbn,title,price); } } else { puts("Statement could not be executed"); } sqlFreeStmt(stmt); } else { puts("Statement could not be allocated"); } sqlDisConnect(conn); exit(0); }
The following is a small example of a stored procedure, which makes a selection from table books. For a discussion on Stored Procedures, see 10.5.(Stored Procedures).
A stored procedure in C |
#include <stdlib.h> #include "sqlcli.h" int main(int argc, char **argv) { SQLH conn; SQLS stmt; if(sqlConnect("AGI",0,0,0,&conn)) { if(sqlAllocStmt(conn,&stmt)) { sqlExecDirect(stmt,"select * from books"); sqlFreeStmt(stmt); } sqlDisConnect(conn); } exit(0); }
Programmers should notice that the connection statement is different from the one of the C example above. Here, the host is indicated as AGI and the rest of the parameters (dbase, userid, password) are 0.
A more substantial example of a stored procedure is the following, where a table is created dynamically as a result of a select statement. The file is called newcreate.c and the call is made as follows:
CALL newcreate <tablename> as <select stmt>
e.g. CALL newcreate tnames as select table_name from sys.tables;
Creating a table with C stored procedures |
#include <string.h> #include "sqlcli.h" static void make_type_name(SQLS stmt, int icol, char *type_name); int main(int argc, char **argv) { SQLH conn; SQLS stmt; char create_buffer[1024]; char select_buffer[1024]; char insert_buffer[1024]; char *p; char *table_name; if(argc<6) { exit(1); } table_name=argv[1]; if(stricmp(argv[2],"as")!=0) { exit(1); } argv+=2; *select_buffer=0; p=select_buffer; while(*++argv) { p=strchr(p,0); sprintf(p,"%s ",*argv); } if(sqlConnect("AGI",0,0,0,&conn)) { if(sqlAllocStmt(conn,&stmt)) { if(sqlPrepare(stmt,select_buffer)) { int i; int colnb; int ucount=0; char unnamed[20]; sqlGetOutputColDescr(stmt); colnb=sqlGetOutputColNb(stmt); sprintf(create_buffer,"create table %s(",table_name); p=strchr(create_buffer,0); sprintf(unnamed,"%d\n",colnb); for(i=0; i<colnb; i++) { const char *cname; if(i!=0) { sprintf(p,", "); p=strchr(p,0); } cname=sqlGetOutputColName(stmt,i); if(cname[0]==0) { sprintf(unnamed,"unnamed%d",ucount++); cname=unnamed; } sprintf(p,"%s ",cname); p=strchr(p,0); make_type_name(stmt,i,p); p=strchr(p,0); if(sqlGetOutputColNullable(stmt,i)==false) { sprintf(p,"not null"); p=strchr(p,0); } } sprintf(p,")"); if(sqlExecDirect(stmt,create_buffer)) { sprintf(insert_buffer,"insert into %s.%s", table_name,select_buffer); sqlExecDirect(stmt,insert_buffer); } } sqlFreeStmt(stmt); } sqlDisConnect(conn); } exit(0); } static void make_type_name(SQLS stmt, int icol, char *type_name) { int t,len,prec,scale; t=sqlGetOutputColType(stmt,icol); len=sqlGetOutputColLength(stmt,icol); prec=sqlGetOutputColPrecision(stmt,icol); scale=sqlGetOutputColScale(stmt,icol); switch(t) { case T_INTEGER: strcpy(type_name,"integer"); break; case T_SMALLINT: strcpy(type_name,"smallint"); break; case T_TINYINT: strcpy(type_name,"tinyint"); break; case T_BIT: strcpy(type_name,"bit"); break; case T_BIGINT: strcpy(type_name,"bigint"); break; case T_UINTEGER: strcpy(type_name,"unsigned integer"); break; case T_USMALLINT: strcpy(type_name,"unsigned smallint"); break; case T_UTINYINT: strcpy(type_name,"unsigned tinyint"); break; case T_UBIGINT: strcpy(type_name,"unsigned bigint"); break; case T_FLOAT: strcpy(type_name,"float"); break; case T_REAL: strcpy(type_name,"real"); break; case T_DOUBLE: strcpy(type_name,"double"); break; case T_DECIMAL: sprintf(type_name,"decimal(%d,%d)",prec,scale); break; case T_NUMERIC: sprintf(type_name,"numeric(%d,%d)",prec,scale); break; case T_CHAR: sprintf(type_name,"char(%d)",len); break; case T_VARCHAR: sprintf(type_name,"varchar(%d)",len); break; case T_UNI_CHAR: sprintf(type_name,"unicode char(%d)",len); break; case T_UNI_VARCHAR: sprintf(type_name,"unicode varchar(%d)",len); break; case T_BINARY: sprintf(type_name,"binary(%d)",len); break; case T_VARBINARY: sprintf(type_name,"varbinary(%d)",len); break; case T_DATE: sprintf(type_name,"date"); break; case T_TIME: sprintf(type_name,"time"); break; case T_TIMESTAMP: sprintf(type_name,"timestamp"); break; case T_LONGVARBINARY: sprintf(type_name,"long varbinary"); break; case T_LONGVARCHAR: sprintf(type_name,"long varchar"); break; default: strcpy(type_name,"<Unknown type>"); } }