egorius: (Default)
[personal profile] egorius

Рассуждая в начале года про 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, так что...)

Date: 2009-11-13 03:54 pm (UTC)
From: [identity profile] hardsign.livejournal.com
ъ) почему в первом абзаце у тебя ™ после точки?

ъъ) исключения® Я тут по старой памяти решил попрограммировать на С++, обплевался весь. С forever!

ъъъ) "отвык от этого" - это ты о чём? Визуальный дебаггеръ® тебе подавай?

ъъъъ) "DB2 поддерживает PL/SQL так что..." -- что?

Date: 2009-11-13 03:57 pm (UTC)
From: [identity profile] hardsign.livejournal.com
Кстати, oci_cleanup®

  if (envhp) {
    OCIHandleFree(envhp, OCI_HTYPE_ENV);
    envhp = 0;
  }


Если ты уверен, что эта функция никогда больше не будет вызвана, "envhp=0" не надо. Если хотел написать универсальную функцию, то надо передавать не OCIEnv*, а OCIEnv**

Date: 2009-11-14 05:55 pm (UTC)
From: [identity profile] egorius.livejournal.com
Я ж не с нуля всё это писал, вот и осталось® местами.

Date: 2014-11-27 10:03 am (UTC)
From: [identity profile] hardsign.livejournal.com
Вот тут грубая ошибка, вызывающая access violation:

if (envhp) {
OCIHandleFree(envhp, OCI_HTYPE_ENV);
envhp = 0;
}
if (stmtp) {
OCIHandleFree(stmtp, OCI_HTYPE_STMT);
stmtp = 0;
}

Сначала нужно закрыть stmt, а уж потом - envh.

Date: 2014-11-30 07:48 pm (UTC)
From: [identity profile] egorius.livejournal.com
Вот ведь, учту. Правда, программка так и не прижилась, приспособился к sql developer.
А что это тебя потянуло?

Date: 2014-12-01 06:45 am (UTC)
From: [identity profile] hardsign.livejournal.com
Так, надо было.
Но оказалось, что проще написать программку на perl'е, предварительно скомпилировав нужный модуль :)

Date: 2009-11-14 05:53 pm (UTC)
From: [identity profile] egorius.livejournal.com
ъ) Никак не могу для себя решить, что хуже: отрывать точку (или запятую) от слова, или отрывать от него знак типа ™ (или 1).

ъъ) Исключения™

ъъъ) Отвык от того, что программы падают и не сообщают, в какой строке это с ними приключилось и по какой причине. PL/SQL™

ъъъ) Что? Forever, вот что.

Date: 2009-11-13 06:47 pm (UTC)
From: [identity profile] owssrmn.livejournal.com
кстати, с недавних пор нормальная субд начала играть в детскую игру в крысу, что прискорбно.

Date: 2009-11-14 05:56 pm (UTC)
From: [identity profile] egorius.livejournal.com
Э-э? Я, наверное, в танке, но какая СУБД и что за игра?

Profile

egorius: (Default)
egorius

September 2025

M T W T F S S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Mar. 14th, 2026 08:58 pm
Powered by Dreamwidth Studios