|
@@ -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*)¶m, 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);
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|