ソースを参照

Add db_oracle module and toolses for it

git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@3997 689a6050-402a-0410-94f2-e92a70836424
Iouri Kharon 17 年 前
コミット
76a936dc64

+ 66 - 0
utils/db_oracle/Makefile

@@ -0,0 +1,66 @@
+# $Id$
+#
+#  db_orasel Makefile
+# 
+
+include ../../Makefile.defs
+
+auto_gen=
+NAME=openser_orasel
+
+include ../../Makefile.sources
+
+ORAPATH=
+
+# use for multiple client sdk version install
+ifneq ($(ORAVERSION),)
+    ORAVERDIR=/$(ORAVERSION)
+endif
+
+# use include/library path's for full client installation
+ifneq ($(ORAHOME),)
+    DEFS +=-I$(ORAHOME)/include
+    LIBS +=-L$(ORAHOME)/lib
+ifeq ($(ORAPATH),)
+    ORAPATH=$(ORAHOME)/lib
+endif
+else
+# use standard know paths oci.h locations (linux)
+    DEFS +=-I$(LOCALBASE)/include/oracle$(ORAVERDIR) \
+	-I$(SYSBASE)/include/oracle$(ORAVERDIR)
+endif
+
+# search 'so' path if it non standard (possible liboclntsh locations on linux)
+ifeq ($(ORAPATH),)
+    ORAPATH=$(shell [ -f $(LOCALBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \
+	    echo $(LOCALBASE)/lib64/oracle$(ORAVERDIR) )
+endif
+ifeq ($(ORAPATH),)
+    ORAPATH=$(shell [ -f $(SYSBASE)/lib64/oracle$(ORAVERDIR)/libocci.so ] && \
+	    echo $(SYSBASE)/lib64/oracle$(ORAVERDIR) )
+endif
+ifeq ($(ORAPATH),)
+    ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \
+	    echo $(SYSBASE)/lib/oracle$(ORAVERDIR) )
+endif
+ifeq ($(ORAPATH),)
+    ORAPATH=$(shell [ -f $(SYSBASE)/lib/oracle$(ORAVERDIR)/libocci.so ] && \
+	    echo $(SYSBASE)/lib/oracle$(ORAVERDIR) )
+endif
+
+ifneq ($(ORAPATH),)
+    LIBS +=-L$(ORAPATH)
+endif
+
+LIBS +=-locci -lclntsh
+
+#DEFS +=-DLINUX -D_GNU_SOURCE -D_REENTRANT
+#LIBS +=-lpthread
+
+ifneq ($(ORAPATH),)
+    LIBS +=-Wl,-rpath $(ORAPATH)
+endif
+
+include ../../Makefile.rules
+
+modules:

+ 348 - 0
utils/db_oracle/getres.c

@@ -0,0 +1,348 @@
+#include "orasel.h"
+#include <math.h>
+#include <assert.h>
+
+/*
+ * Uncomment next string if you will sell 'NULL' on unitialized NON text field
+ */
+//#define NULL_ID "NULL"
+
+static char st_buf[65536];
+
+enum type_t {
+  DB_STR = 0,
+  DB_DATETIME,
+  /* end of left alignment */
+  DB_INT,
+  DB_BITMAP,
+  DB_DOUBLE   /* MUST belast */
+};
+
+//---------------------------------------------------------
+struct dmap {
+    OCIDefine** defh;
+    union {
+	dvoid* v;
+	double* f;
+	int* i;
+	char* c;
+	OCIDate* o;
+    }* pv;
+    dvoid** pval;
+    ub2* ilen;
+    sb2* ind;
+    ub2* len;
+};
+typedef struct dmap dmap_t;
+
+//-----------------------------------------------------------------------------
+static void dmap_init(dmap_t* _d, unsigned n)
+{
+	size_t sz = sizeof(*_d->defh) + sizeof(*_d->pv) + sizeof(*_d->pval) +
+		    sizeof(*_d->ilen) + sizeof(*_d->ind) + sizeof(*_d->len);
+	unsigned char *p = safe_malloc(sz * n);
+
+	_d->defh = (void*)p;
+	p += n*sizeof(*_d->defh);
+	_d->pv = (void*)p;
+	p += n*sizeof(*_d->pv);
+	_d->pval = (void*)p;
+	p += n*sizeof(*_d->pval);
+	_d->ilen = (void*)p;
+	p += n*sizeof(*_d->ilen);
+	_d->ind = (void*)p;
+	p += n*sizeof(*_d->ind);
+	_d->len = (void*)p;
+//	p += n*sizeof(*_d->len);
+}
+
+//-----------------------------------------------------------------------------
+/*
+ * Get and convert columns from a result. Define handlers and buffers
+ */
+static void get_columns(const con_t* con, res_t* _r, dmap_t* _d)
+{
+	OCIParam *param;
+	size_t tsz;
+	ub4 i, n;
+	sword status;
+
+	status = OCIAttrGet(con->stmthp, OCI_HTYPE_STMT, &n, NULL,
+			OCI_ATTR_PARAM_COUNT, con->errhp);
+
+	if (status != OCI_SUCCESS) oraxit(status, con);
+
+	if (!n) donegood("Empty table");
+
+	dmap_init(_d, n);
+
+	_r->names = (Str**)safe_malloc(n * sizeof(Str*));
+	_r->types = (unsigned char*)safe_malloc(n * sizeof(unsigned char));
+	_r->col_n = n;
+
+	tsz = 0;
+	memset(_d->defh, 0, sizeof(_d->defh[0]) * n);
+	for (i = 0; i < n; i++) {
+		ub4 len;
+		ub2 dtype;
+		unsigned char ctype = DB_DOUBLE;
+
+		status = OCIParamGet(con->stmthp, OCI_HTYPE_STMT, con->errhp,
+				(dvoid**)(dvoid*)&param, i+1);
+		if (status != OCI_SUCCESS) goto ora_err;
+
+		{
+			text *name;
+			status = OCIAttrGet(param, OCI_DTYPE_PARAM, 
+					(dvoid**)(dvoid*)&name, &len,
+					OCI_ATTR_NAME, con->errhp);
+			if (status != OCI_SUCCESS) goto ora_err;
+			_r->names[i] = str_alloc((char*)name, len);
+		}
+
+		status = OCIAttrGet(param, OCI_DTYPE_PARAM,
+				(dvoid**)(dvoid*)&dtype, NULL,
+				OCI_ATTR_DATA_TYPE, con->errhp);
+		if (status != OCI_SUCCESS) goto ora_err;
+
+		switch (dtype) {
+		case SQLT_UIN:
+set_bitmap:
+			ctype = DB_BITMAP;
+			len = sizeof(unsigned);
+			break;
+
+		case SQLT_INT:
+set_int:
+			ctype = DB_INT;
+			len = sizeof(int);
+			break;
+
+		case SQLT_VNU:
+		case SQLT_NUM:
+			len = 0;  /* PRECISION is ub1 (byte) */
+			status = OCIAttrGet(param, OCI_DTYPE_PARAM,
+					(dvoid**)(dvoid*)&len, NULL,
+					OCI_ATTR_PRECISION, con->errhp);
+			if (status != OCI_SUCCESS) goto ora_err;
+			if (len <= 11) {
+				sb1 sc;
+				status = OCIAttrGet(param, OCI_DTYPE_PARAM,
+						(dvoid**)(dvoid*)&sc, NULL,
+						OCI_ATTR_SCALE, con->errhp);
+				if (status != OCI_SUCCESS) goto ora_err;
+				if (!sc) {
+					dtype = SQLT_INT;
+					if (len != 11) goto set_int;
+					dtype = SQLT_UIN;
+					goto set_bitmap;
+				}
+				if(sc < 0) sc = 0;
+				ctype += sc;
+			}
+		case SQLT_FLT:
+		case SQLT_BFLOAT:
+		case SQLT_BDOUBLE:
+		case SQLT_IBFLOAT:
+		case SQLT_IBDOUBLE:
+		case SQLT_PDN:
+			len = sizeof(double);
+			dtype = SQLT_FLT;
+			break;
+
+		case SQLT_DATE:
+		case SQLT_DAT:
+		case SQLT_ODT:
+		case SQLT_TIMESTAMP:
+		case SQLT_TIMESTAMP_TZ:
+		case SQLT_TIMESTAMP_LTZ:
+			ctype = DB_DATETIME;
+			len = sizeof(OCIDate);
+			dtype = SQLT_ODT;
+			break;
+
+		case SQLT_CLOB:
+		case SQLT_BLOB:
+		case SQLT_CHR:
+		case SQLT_STR:
+		case SQLT_VST:
+		case SQLT_VCS:
+		case SQLT_AFC:
+		case SQLT_AVC:
+			ctype = DB_STR;
+			dtype = SQLT_CHR;
+			len = 0;  /* DATA_SIZE is ub2 (word) */
+			status = OCIAttrGet(param, OCI_DTYPE_PARAM,
+					(dvoid**)(dvoid*)&len, NULL,
+					OCI_ATTR_DATA_SIZE, con->errhp);
+			if (status != OCI_SUCCESS) goto ora_err;
+			++len;
+			break;
+
+		default:
+			errxit("unsupported datatype");
+		}
+		_r->types[i] = ctype;
+		_d->ilen[i] = (ub2)len;
+		_d->pv[i].v = st_buf + tsz;
+		tsz += len;
+		status = OCIDefineByPos(con->stmthp, &_d->defh[i], con->errhp,
+				i+1, _d->pv[i].v, len, dtype, &_d->ind[i],
+				&_d->len[i], NULL, OCI_DEFAULT);
+		if (status != OCI_SUCCESS) goto ora_err;
+	}
+
+	if (tsz > sizeof(st_buf)) errxit("too large row");
+	return;
+
+ora_err:
+	oraxit(status, con);
+}
+
+//-----------------------------------------------------------------------------
+/*
+ * Convert data fron db format to internal format
+ */
+static void convert_row(const res_t* _res, Str*** _r, const dmap_t* _d)
+{
+	unsigned i, n = _res->col_n;
+	Str** v;
+
+	*_r = v = (Str**)safe_malloc(n * sizeof(Str**));
+
+	for (i = 0; i < n; i++, v++) {
+		char buf[64];
+		unsigned char t = _res->types[i];
+
+		if (_d->ind[i] == -1) {
+			static const struct {
+			    unsigned len;
+			    char     s[1];
+			}_empty = { 0, "" };
+#ifdef NULL_ID
+			static const struct {
+			    unsigned len;
+			    char     s[sizeof(NULL_ID)];
+			}_null = { sizeof(NULL_ID)-1, NULL_ID };
+
+			*v = (Str*)&_null;
+			if (t != DB_STR) continue;
+#endif			
+			*v = (Str*)&_empty;
+			continue;
+		}
+
+//		if (_d->ind[i]) errxit("truncated value in DB");
+
+		switch (t) {
+		case DB_STR:
+			*v = str_alloc(_d->pv[i].c, _d->len[i]);
+			break;
+
+		case DB_INT:
+			*v = str_alloc(buf, snprintf(buf, sizeof(buf), "%i",
+					*_d->pv[i].i));
+			break;
+
+		case DB_BITMAP:
+			*v = str_alloc(buf, snprintf(buf, sizeof(buf), "0x%X",
+					*_d->pv[i].i));
+			break;
+
+		case DB_DATETIME:
+			{
+				struct tm tm;
+				memset(&tm, 0, sizeof(tm));
+				OCIDateGetTime(_d->pv[i].o, &tm.tm_hour,
+						&tm.tm_min, &tm.tm_sec);
+				OCIDateGetDate(_d->pv[i].o, &tm.tm_year,
+						&tm.tm_mon, &tm.tm_mday);
+				if (tm.tm_mon)
+					--tm.tm_mon;
+				if (tm.tm_year >= 1900)
+					tm.tm_year -= 1900;
+				*v = str_alloc(buf, strftime(buf, sizeof(buf),
+						"%d-%b-%Y %T", &tm));
+			}
+			break;
+
+		case DB_DOUBLE:
+			*v = str_alloc(buf, snprintf(buf, sizeof(buf), "%g",
+					*_d->pv[i].f));
+			break;
+
+		default:
+			{
+				double x = fabs(*_d->pv[i].f);
+				const char *fmt = "%.*f";
+				if (x && (x >= 1.0e6 || x < 1.0e-5))
+					fmt = "%.*e";
+				*v = str_alloc(buf, snprintf(buf, sizeof(buf),
+						fmt, (t - DB_DOUBLE), *_d->pv[i].f));
+			}
+			break;
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+/*
+ * Get rows and convert it from oracle to db API representation
+ */
+static void get_rows(const con_t* con, res_t* _r, dmap_t* _d)
+{
+	ub4 rcnt;
+	sword status;
+	unsigned n = _r->col_n;
+
+	memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n);
+
+	status = OCIStmtFetch2(con->stmthp, con->errhp, 1, OCI_FETCH_LAST, 0,
+				OCI_DEFAULT);
+	if (status != OCI_SUCCESS) {
+		if (status == OCI_NO_DATA) donegood("Empty set");
+		goto ora_err;
+	}
+
+	status = OCIAttrGet(con->stmthp, OCI_HTYPE_STMT, &rcnt, NULL,
+			OCI_ATTR_CURRENT_POSITION, con->errhp);
+	if (status != OCI_SUCCESS) goto ora_err;
+	if (!rcnt) errxit("lastpos==0");
+
+	_r->row_n = rcnt;
+	_r->rows = (Str***)safe_malloc(rcnt * sizeof(Str**));
+	while ( 1 ) {
+		convert_row(_r, &_r->rows[--rcnt], _d);
+		if (!rcnt) return;
+
+		memcpy(_d->len, _d->ilen, sizeof(_d->len[0]) * n);
+		status = OCIStmtFetch2(con->stmthp, con->errhp, 1,
+					OCI_FETCH_PRIOR, 0, OCI_DEFAULT);
+		if (status != OCI_SUCCESS) break;
+	}
+ora_err:
+	oraxit(status, con);
+}
+
+//-----------------------------------------------------------------------------
+/*
+ * Read database answer and fill the structure
+ */
+void get_res(const con_t* con, res_t* _r)
+{
+	dmap_t dmap;
+	unsigned n;
+	unsigned char *pt;
+
+	get_columns(con, _r, &dmap);
+	get_rows(con, _r, &dmap);
+	n = _r->col_n;
+	pt = _r->types;
+	do {
+		--n;
+		assert(DB_STR == 0 && DB_DATETIME == 1);
+		pt[n] = (pt[n] <= DB_DATETIME);
+	}while(n);
+}
+
+//-----------------------------------------------------------------------------

+ 116 - 0
utils/db_oracle/orasel.c

@@ -0,0 +1,116 @@
+#include "orasel.h"
+#include <ctype.h>
+#include <unistd.h>
+
+outmode_t outmode;
+
+//-----------------------------------------------------------------------------
+static void prepare_uri(con_t* con, const char *uri)
+{
+	const char *p = strchr(uri, '/');
+
+	if(!p || p == uri) goto bad;
+	con->username = str_alloc(uri, p - uri);
+	uri = p+1;
+	p = strchr(uri, '@');
+	if(!p || p == uri) goto bad;
+	con->password = str_alloc(uri, p - uri);
+	if(strchr(con->password->s, '/')) goto bad;
+	if(!*++p) goto bad;
+	con->uri = str_alloc(p, strlen(p));
+	return;
+
+bad:
+	errxit("invalid db (must be as name/password@dbname)");
+}
+
+//-----------------------------------------------------------------------------
+static const Str* prepare_req(const char* req)
+{
+	Str* ps;
+	char *p;
+
+	while(*req && isspace((unsigned char)*req)) ++req;
+	if(strncasecmp(req, "select", sizeof("select")-1)) goto bad;
+	p = (char*)req + sizeof("select")-1;
+	if(!*p || !isspace((unsigned char)*p)) goto bad;
+	ps = str_alloc(req, strlen(req));
+	p = (char*) ps->s + sizeof("select")-1;
+	do if(isspace((unsigned char)*p)) *p = ' '; while(*++p);
+	do --p; while(isspace((unsigned char)*p));
+	if(*p != ';') goto bad;
+	do {
+		do --p; while(isspace((unsigned char)*p));
+	}while(*p == ';');
+	*++p = '\0';
+	ps->len = p - ps->s;
+	if(ps->len <= sizeof("select")-1) {
+bad:
+		errxit("support only 'select ...;' request");
+	}
+	return ps;
+}
+
+//-----------------------------------------------------------------------------
+static void get_opt(int argc, char* argv[])
+{
+	int opt = 0;
+
+	if(argc <= 1 || (argc == 2 && !strcmp(argv[1], "--help"))) {
+help:
+		fprintf(stderr, "OpenSER for oracle 'select' request utility\n");
+		opt = -2;  /* flag for help print */
+	} else {
+		while((opt = getopt(argc-1, argv+1, "BLNe:")) != -1) {
+			switch(opt) {
+			case 'B':
+				outmode.raw = 1;;
+				break;
+			case 'L':
+				outmode.hdr = 1;
+				break;
+			case 'N':
+				outmode.emp = 1;
+				break;
+			case 'e':
+				if(optind == argc-1) return;
+				// pass thru
+			default:
+				goto help;
+			}
+		}
+	}
+	fprintf(stderr, "use: %s user/password@db [-BLN] -e \"select ...;\"\n",
+		argv[0]);
+	if(opt == -2) {
+		fprintf(stderr, "     where   -B - print using tab separator\n");
+		fprintf(stderr, "             -L - skip column headers\n");
+		fprintf(stderr, "             -N - skip notify of empty result\n");
+	}
+	exit(1);
+}
+
+//-----------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+	con_t con;
+	res_t res;
+	const Str* req;
+
+
+	get_opt(argc, argv);
+
+	memset(&con, 0, sizeof(con));
+	memset(&res, 0, sizeof(res));
+
+	prepare_uri(&con, argv[1]);
+	req = prepare_req(optarg);
+	open_sess(&con);
+	send_req(&con, req);
+	get_res(&con, &res);
+	OCITerminate(OCI_DEFAULT);
+	out_res(&res);
+	return 0;
+}
+
+//-----------------------------------------------------------------------------

+ 53 - 0
utils/db_oracle/orasel.h

@@ -0,0 +1,53 @@
+#ifndef __orasel_h__
+#define __orasel_h__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <oci.h>
+
+typedef struct {
+    unsigned  len;
+    char      s[];
+}Str;
+
+typedef struct {
+    const Str*  username;
+    const Str*  password;
+    const Str*  uri;
+    OCIError*   errhp;
+    OCISvcCtx*  svchp;
+    OCIEnv*     envhp;
+    OCISession* authp;
+    OCIServer*  srvhp;
+    OCIStmt*    stmthp;
+}con_t;
+
+typedef struct {
+    Str**          names;
+    Str***         rows;
+    unsigned char* types;
+    unsigned       col_n;
+    unsigned       row_n;
+}res_t;
+
+void __attribute__((noreturn)) donegood(const char *msg);
+void __attribute__((noreturn)) errxit(const char *msg);
+void __attribute__((noreturn)) oraxit(sword status, const con_t* con);
+void* safe_malloc(size_t sz);
+Str* str_alloc(const char *s, size_t len);
+
+void open_sess(con_t* con);
+void send_req(con_t* con, const Str* req);
+void get_res(const con_t* con, res_t* _r);
+void out_res(const res_t* _r);
+
+typedef struct {
+    unsigned raw : 1,
+	     hdr : 1,
+	     emp : 1;
+}outmode_t;
+extern outmode_t outmode;
+
+#endif

+ 70 - 0
utils/db_oracle/outres.c

@@ -0,0 +1,70 @@
+#include "orasel.h"
+
+//-----------------------------------------------------------------------------
+static void out_delim(const unsigned* pl, unsigned nc)
+{
+	unsigned i;
+
+	for(i = 0; i < nc; i++) {
+		unsigned j = pl[i] + 2;
+
+		putchar('+');
+		do putchar('-'); while(--j);
+	}
+	printf("+\n");
+}
+
+//-----------------------------------------------------------------------------
+void out_res(const res_t* _r)
+{
+	unsigned* pl = NULL;
+	unsigned  nc = _r->col_n, nr = _r->row_n, i, j;
+	Str**     ps = _r->names;
+
+	if(!outmode.raw) {
+		pl = safe_malloc(nc * sizeof(unsigned));
+		for(i = 0; i < nc; i++)
+			pl[i] = ps[i]->len;
+		for(j = 0; j < nr; j++) {
+			ps = _r->rows[j];
+			for(i = 0; i < nc; i++) 
+				if(pl[i] < ps[i]->len) pl[i] = ps[i]->len;
+		}
+
+		out_delim(pl, nc);
+	}
+	if(!outmode.hdr) {
+		ps = _r->names;
+		for(i = 0; i < nc; i++) {
+			if(!outmode.raw) {
+				printf("| %-*.*s ", pl[i], ps[i]->len, ps[i]->s);
+			} else {
+				if(i) putchar('\t');
+				printf("%.*s", ps[i]->len, ps[i]->s);
+			}
+		}
+		if(outmode.raw) putchar('\n');
+		else {
+			printf("|\n");
+			out_delim(pl, nc);
+		}
+	}
+	for(j = 0; j < nr; j++) {
+		ps = _r->rows[j];
+		if(!outmode.raw) {
+			for(i = 0; i < nc; i++)
+				printf(_r->types[i] ? "| %-*.*s " : "| %*.*s ",
+					pl[i], ps[i]->len, ps[i]->s);
+			printf("|\n");
+		} else {
+			for(i = 0; i < nc; i++) {
+				if(i) putchar('\t');
+				printf("%.*s", ps[i]->len, ps[i]->s);
+			}
+			putchar('\n');
+		}
+	}
+	if(!outmode.raw) out_delim(pl, nc);
+}
+
+//-----------------------------------------------------------------------------

+ 66 - 0
utils/db_oracle/selcon.c

@@ -0,0 +1,66 @@
+#include "orasel.h"
+
+//-----------------------------------------------------------------------------
+void open_sess(con_t* con)
+{
+	sword status;
+
+	if (   OCIEnvCreate(&con->envhp, OCI_DEFAULT | OCI_NEW_LENGTH_SEMANTICS,
+			    NULL, NULL, NULL, NULL, 0, NULL) != OCI_SUCCESS
+	    || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->errhp,
+				OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS
+	    || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->srvhp,
+				OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS
+	    || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->svchp,
+				OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS
+	    || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->authp,
+				OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS
+	    || OCIHandleAlloc(con->envhp, (dvoid**)(dvoid*)&con->stmthp,
+				OCI_HTYPE_STMT, 0, NULL) != OCI_SUCCESS)
+	{
+		errxit("no oracle memory left");
+	}
+
+	status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->srvhp, 0,
+			OCI_ATTR_SERVER, con->errhp);
+	if (status != OCI_SUCCESS) goto connect_err;
+	status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION,
+			(text*)con->username->s, con->username->len,
+			OCI_ATTR_USERNAME, con->errhp);
+	if (status != OCI_SUCCESS) goto connect_err;
+	status = OCIAttrSet(con->authp, OCI_HTYPE_SESSION,
+			(text*)con->password->s, con->password->len,
+			OCI_ATTR_PASSWORD, con->errhp);
+	if (status != OCI_SUCCESS) goto connect_err;
+	status = OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, con->authp, 0,
+			OCI_ATTR_SESSION, con->errhp);
+	if (status != OCI_SUCCESS) goto connect_err;
+	status = OCIServerAttach(con->srvhp, con->errhp, (OraText*)con->uri->s,
+			con->uri->len, 0);
+	if (status != OCI_SUCCESS) goto connect_err;
+	status = OCISessionBegin(con->svchp, con->errhp, con->authp,
+			OCI_CRED_RDBMS, OCI_DEFAULT);
+	if (status != OCI_SUCCESS) {
+connect_err:
+		oraxit(status, con);
+	}
+}
+
+//-----------------------------------------------------------------------------
+void send_req(con_t* con, const Str* req)
+{
+	sword status;
+
+	status = OCIStmtPrepare(con->stmthp, con->errhp, (text*)req->s, req->len,
+			OCI_NTV_SYNTAX, OCI_DEFAULT);
+	if (status != OCI_SUCCESS) goto request_err;
+	status = OCIStmtExecute(con->svchp, con->stmthp, con->errhp, 0, 0, NULL,
+			NULL, OCI_STMT_SCROLLABLE_READONLY);
+	if (status != OCI_SUCCESS) {
+request_err:
+		fprintf(stderr, "%.*s\n", req->len, req->s);
+		oraxit(status, con);
+	}
+}
+
+//-----------------------------------------------------------------------------

+ 97 - 0
utils/db_oracle/util.c

@@ -0,0 +1,97 @@
+#include "orasel.h"
+
+//-----------------------------------------------------------------------------
+void __attribute__((noreturn)) donegood(const char *msg)
+{
+	OCITerminate(OCI_DEFAULT);
+	if (msg && !outmode.emp)
+		printf("%s\n", msg);
+	exit(0);
+}
+
+//-----------------------------------------------------------------------------
+void __attribute__((noreturn)) errxit(const char *msg)
+{
+	OCITerminate(OCI_DEFAULT);
+	fprintf(stderr, "ERROR: %s\n", msg);
+	exit(1);
+}
+
+//-----------------------------------------------------------------------------
+void __attribute__((noreturn)) oraxit(sword status, const con_t* con)
+{
+	const char *p = NULL;
+	char buf[512];
+	sword ecd;
+
+	switch (status) {
+	case OCI_SUCCESS_WITH_INFO:
+	case OCI_ERROR:
+		ecd = 0;
+		if(OCIErrorGet(con->errhp, 1, NULL, &ecd, (OraText*)buf,
+			sizeof(buf), OCI_HTYPE_ERROR) != OCI_SUCCESS) 
+		{
+			snprintf(buf, sizeof(buf), "unknown ORAERR %u", ecd);
+		}
+		break;
+
+	default:
+		snprintf(buf, sizeof(buf), "unknown status %u", status);
+		break;
+
+	case OCI_SUCCESS:
+		p = "success";
+		break;
+
+	case OCI_NEED_DATA:
+		p = "need data";
+		break;
+
+	case OCI_NO_DATA:
+		p = "no data";
+		break;
+
+	case OCI_INVALID_HANDLE:
+		p = "invalid handle";
+		break;
+
+	case OCI_STILL_EXECUTING: /* ORA-3123 */
+		p = "executing";
+		break;
+
+	case OCI_CONTINUE:
+		p = "continue";
+		break;
+	}
+	if (p) {
+		snprintf(buf, sizeof(buf), "logic error (%s)", p);
+	}
+	errxit(buf);
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+static void __attribute__((noreturn)) nomem(void)
+{
+	errxit("no enough memory");
+}
+
+//-----------------------------------------------------------------------------
+void* safe_malloc(size_t sz)
+{
+	void *p = malloc(sz);
+	if (!p) nomem();
+	return p;
+}
+
+//-----------------------------------------------------------------------------
+Str* str_alloc(const char *s, size_t len)
+{
+	Str* ps = (Str*)safe_malloc(sizeof(Str) + len + 1);
+	ps->len = len;
+	memcpy(ps->s, s, len);
+	ps->s[len] = '\0';
+	return ps;
+}
+
+//-----------------------------------------------------------------------------