Oracle Call Interface
Nov. 13th, 2009 05:47 pmРассуждая в начале года про vim и dbext, я пришёл к выводу, что вызов sql*plus для выполнения запросов не годится, так как он некорректно обрабатывает «широкие» таблицы. И что надо писать свою программку.™
На чём её писать? Нужно что-то такое, чтобы работало на тех же платформах, где и vim. Причём не хотелось бы, чтобы потреблялось много ресурсов — ну неохота мне ждать, пока запустится какая-нибудь виртуальная машина только ради того, чтобы выбрать sysdate из dual. Так что пусть будет Си + OCI.
OCI оказался мудрёным, выборка sysdate вылилась чуть не в две сотни строчек, которые для истории лежат под катом.
#include «oci.h»
#include «stdio.h»
int check_err(OCIError *errhp, sword status)
{
text errbuf[512];
ub4 buflen;
sb4 errcode;
switch (status) {
case OCI_SUCCESS:
return 0;
case OCI_SUCCESS_WITH_INFO:
fprintf(stderr,"Error - OCI_SUCCESS_WITH_INFO\n");
return 0;
case OCI_NEED_DATA:
fprintf(stderr,"Error - OCI_NEED_DATA\n");
return 1;
case OCI_NO_DATA:
fprintf(stderr,"Error - OCI_NO_DATA\n");
return 1;
case OCI_ERROR:
OCIErrorGet(errhp, 1, NULL, &errcode, errbuf, sizeof(errbuf), OCI_HTYPE_ERROR);
fprintf(stderr,"Error - %s\n", errbuf);
return 1;
case OCI_INVALID_HANDLE:
fprintf(stderr,"Error - OCI_INVALID_HANDLE\n");
return 1;
case OCI_STILL_EXECUTING:
fprintf(stderr,"Error - OCI_STILL_EXECUTE\n");
return 1;
case OCI_CONTINUE:
fprintf(stderr,"Error - OCI_CONTINUE\n");
return 0;
default:
return 1;
}
}
void parse_connect_string(char *connect_str, char **username, char **password, char **dbname)
{
*username = connect_str;
while (*connect_str) {
if (*connect_str == '/') {
*connect_str++ = '\0';
break;
}
connect_str++;
}
*password = connect_str;
while (*connect_str) {
if (*connect_str == '@') {
*connect_str++ = '\0';
break;
}
connect_str++;
}
*dbname = connect_str;
}
int oci_init(
OCIEnv **envhpp,
OCIError **errhpp,
OCIStmt **stmtpp,
OCISvcCtx **svchpp,
char *username,
char *password,
char *dbname
)
{
if (check_err(*errhpp, OCIEnvCreate(envhpp, OCI_DEFAULT, 0, 0, 0, 0, 0, 0) )) {
printf("OCIEnvCreate failed\n");
return 0;
}
if (check_err(*errhpp, OCIHandleAlloc(*envhpp, (dvoid *)errhpp, OCI_HTYPE_ERROR, 0, 0) )) {
printf("OCIHandleAlloc err failed\n");
return 0;
}
if (check_err(*errhpp, OCIHandleAlloc(*envhpp, (dvoid *)stmtpp, OCI_HTYPE_STMT, 0, 0) )) {
printf("OCIHandleAlloc stmt failed\n");
return 0;
}
printf("un=%s, pw=%s, db=%s\n",username,password,dbname);
if (check_err(*errhpp, OCILogon(*envhpp, *errhpp, svchpp, username, strlen(username),
password, strlen(password), dbname, strlen(dbname)) )) {
printf("OCILogin failed\n");
return 0;
}
return 1;
}
void oci_cleanup(
OCIEnv *envhp,
OCIError *errhp,
OCIStmt *stmtp,
OCISvcCtx *svchp
)
{
OCILogoff(svchp,errhp);
if (envhp) {
OCIHandleFree(envhp, OCI_HTYPE_ENV);
envhp = 0;
}
if (stmtp) {
OCIHandleFree(stmtp, OCI_HTYPE_STMT);
stmtp = 0;
}
}
int oci_sample(
OCIError *errhp,
OCIStmt *stmtp,
OCISvcCtx *svchp
)
{
OCIDefine *defnp = 0;
char *query = "select to_char(sysdate,'DD.MM.YYYY HH24:MI:SS') from dual";
char retval[20];
if (check_err(errhp, OCIStmtPrepare(stmtp, errhp, (text*)query, strlen(query),
OCI_NTV_SYNTAX, OCI_DEFAULT) )) {
printf("OCIStmtPrepare failed\n");
return 0;
}
if (check_err(errhp, OCIDefineByPos(stmtp, &defnp, errhp, 1, retval, sizeof(retval),
SQLT_AVC, NULL, NULL, NULL, OCI_DEFAULT) )) {
printf("OCIDefineByPos failed\n");
return 0;
}
if (check_err(errhp, OCIStmtExecute(svchp, stmtp, errhp, 1, 0, NULL, NULL, OCI_EXACT_FETCH) )) {
printf("OCIStmtExecute failed\n");
return 0;
}
printf("%s\n",retval);
return 1;
}
int main(int argc, char* argv[])
{
OCIEnv *envhp = 0;
OCIError *errhp = 0;
OCISvcCtx *svchp = 0;
OCIStmt *stmtp = 0;
char *username, *password, *dbname;
if (argc < 2) {
printf("usage %s username/password[@dbname]\n",argv[0]);
return 1;
}
parse_connect_string(argv[1], &username, &password, &dbname);
if (!oci_init(&envhp, &errhp, &stmtp, &svchp, username, password, dbname)) {
return 1;
}
if (!oci_sample(errhp, stmtp, svchp)) {
oci_cleanup(envhp, errhp, stmtp, svchp);
return 1;
}
oci_cleanup(envhp, errhp, stmtp, svchp);
return 0;
}
Программка отлично скомпилировалась gcc под cygwin следующим образом:
gcc -o test.exe -I%ORACLE_HOME%\oci\include test.c %ORACLE_HOME%\bin\oci.dll
Но как же я привык к исключениям! Страдаю без них. И как же я отвык от вот этого:
6585 [main] test 196 _cygtls::handle_exceptions: Exception: STATUS_ACCESS_VIOLATION 10471 [main] test 196 open_stackdumpfile: Dumping stack trace to test.exe.stackdump
PL/SQL forever! (Кстати, DB2 с недавних пор поддерживает PL/SQL, так что...)
no subject
Date: 2009-11-13 03:57 pm (UTC)if (envhp) { OCIHandleFree(envhp, OCI_HTYPE_ENV); envhp = 0; }Если ты уверен, что эта функция никогда больше не будет вызвана, "envhp=0" не надо. Если хотел написать универсальную функцию, то надо передавать не OCIEnv*, а OCIEnv**
no subject
Date: 2009-11-14 05:55 pm (UTC)no subject
Date: 2014-11-27 10:03 am (UTC)if (envhp) {
OCIHandleFree(envhp, OCI_HTYPE_ENV);
envhp = 0;
}
if (stmtp) {
OCIHandleFree(stmtp, OCI_HTYPE_STMT);
stmtp = 0;
}
Сначала нужно закрыть stmt, а уж потом - envh.
no subject
Date: 2014-11-30 07:48 pm (UTC)А что это тебя потянуло?
no subject
Date: 2014-12-01 06:45 am (UTC)Но оказалось, что проще написать программку на perl'е, предварительно скомпилировав нужный модуль :)