blob: 4d89bcf309c5cfcea4ef1a72aa1404fcc36daeca [file] [log] [blame]
/*************************************************************************
*
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**************************************************************************/
/*
* odb - v1.1.0
* vim:ru:scs:si:sm:sw=4:sta:ts=4:tw=0
*
* Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
* -- Antoine de Saint Exupéry
*/
char *odbid = "odb version 1.1.0";
char *odbauth = "Trafodion Dev <trafodion-development@lists.launchpad.net>";
#ifndef ODBBLD
#define ODBBLD "odb Undefined Platform"
#endif
#define CMD_CHUNK 512 /* Granularity of SQL command memory allocation */
#define ETAB_CHUNK 8 /* Granularity of etab[] memory allocation */
#define TD_CHUNK 32 /* Granularity of td[] memory allocation */
#define MAX_ARGS 11 /* Max arguments for interactive mode */
#define ARG_LENGTH 128 /* Max argument length in interactive mode */
#define LINE_CHUNK 51200 /* size of memory chunks allocated to store lines */
#define ERR_MSG_LEN 512 /* size of error message buffer */
#define MAX_VNLEN 32 /* Max variable name length */
#define MAX_PK_COLS 16 /* Max number of PK elements */
#define MAXCOL_LEN 128 /* Max table column name length */
#define MAXOBJ_LEN 128 /* Max catalog/schema/table name length */
#define MAX_CLV 64 /* Max command line variables (-V) */
#define QLABEL_LEN 16 /* Query Label Max Length */
#define WORDSZ sizeof(unsigned long int) /* CPU word size for memcmp memory aligned ops */
#define BYTESPWCHAR 4 /* Default Bytes per (Wide) Character */
#define BYTESPCHAR 1 /* Default Bytes per (non-Wide) Character */
#define NUMLOADERS 2 /* Default number of loaders per extractor */
#define EMPTY (-9999) /* used for EMPTYASEMPTY */
#define EX_OK 0 /* exit status: successfull termination */
#define EX_USAGE 64 /* exit status: invalid command line, bad or missing parameter */
#define EX_DATAERR 65 /* exit status: input data was incorrect in some way */
#define EX_NOINPUT 66 /* exit status: input does not exists or is unreadable */
#define EX_NOSRVC 67 /* exit status: service is not available */
#define EX_ODBCERR 68 /* exit status: unrecoverable ODBC API error */
#define EX_OSERR 69 /* exit status: OS error (cannot alloc, cannot create thread,... */
#define EX_NOUTPUT 70 /* exit status: cannot create/write output file */
#define EX_SIGNAL 71 /* exit status: timeout or user interrupt */
#define RWBUFF_LEN 262144 /* default read/write buffer length */
#include <zlib.h>
#include "JsonReader.h"
#define windowBits 15
#define GZIP_ENCODING 16
#define ENABLE_ZLIB_GZIP 32
#define GZBUFF_LEN 262144 /* gzip write buffer length */
#ifdef __CYGWIN__
#undef WORD
#include <windows.h>
#include <sql.h>
#include <sqltypes.h>
#include <sqlext.h>
#undef _WIN32
#endif
#ifdef _WIN32
#include <windows.h>
#include <process.h>
#include <sys/timeb.h>
#include <direct.h>
#include <float.h>
#define isnan(x) _isnan(x)
#define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) ? 1 : ((_fpclass(x) == _FPCLASS_NINF) ? -1 : 0))
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
#define SIZET_SPEC "%Iu"
#define FindFirstFile FindFirstFileA
int gettimeofday(struct timeval* tp, void* tzp) {
FILETIME ft;
unsigned __int64 tmpres = 0;
if (tp != NULL) {
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/* converting file time to unix epoch */
tmpres /= 10;
tmpres -= DELTA_EPOCH_IN_MICROSECS;
/* convert into microseconds*/
tp->tv_sec = (long)(tmpres / 1000000UL);
tp->tv_usec = (long)(tmpres % 1000000UL);
}
return (0);
}
#undef UNICODE
#define strcasecmp _stricmp
#define snprintf sprintf_s
#define chdir _chdir
#define getcwd _getcwd
#define Setenv(x,y) _putenv_s(x,y)
#define MutexLock(x) EnterCriticalSection(x)
#define MutexUnlock(x) LeaveCriticalSection(x)
#define MutexDestroy(x) DeleteCriticalSection(x)
#define CondWait(x,y) SleepConditionVariableCS(x, y, INFINITE)
#define CondWake(x) WakeConditionVariable(x)
#define CondWakeAll(x) WakeAllConditionVariable(x)
#define CondDestroy(x) ;
typedef CRITICAL_SECTION Mutex;
typedef CONDITION_VARIABLE CondVar;
static DWORD WINAPI Oruncmd(void *tid);
HANDLE h; /* used to expand file wildcards [-P, -S] */
HANDLE *thhn = 0; /* thread handle pointer - array will contain tn entries */
CRITICAL_SECTION dlbmutex; /* dlb mutex */
WIN32_FIND_DATA finfo; /* used to expand file wildcards [-P, -S] */
int st, en; /* used for round-robin replication [-P, -S] */
#else
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#define SIZET_SPEC "%zu"
#include <pthread.h>
#include <strings.h>
#ifdef __hpux
#define Sleep(x) \
if ( x > 1000 ) { \
(void) sleep ( x / 1000 ) ; \
(void) usleep ( (useconds_t)(1000 * x%1000) ); \
} else { \
(void) usleep ( (useconds_t)(1000 * x) ); \
}
#include <sys/pstat.h>
#ifndef ODBVER
#define ODBVER "odb"
#endif
union pstun pst;
#else
#define Sleep(x) (void) usleep ( (useconds_t)(1000 * x) ) /* sleep x ms */
#endif
#include <termios.h>
#include <math.h>
static void *Oruncmd(void *tid);
pthread_t *thid = 0; /* thread id pointer - array will contain tn entries */
int thres = 0; /* thread result */
pthread_mutex_t dlbmutex = PTHREAD_MUTEX_INITIALIZER; /* dlb mutex */
#define MutexLock(x) pthread_mutex_lock(x)
#define MutexUnlock(x) pthread_mutex_unlock(x)
#define MutexDestroy(x) pthread_mutex_destroy(x)
#define CondWait(x,y) pthread_cond_wait(x, y)
#define CondWake(x) pthread_cond_signal(x)
#define CondWakeAll(x) pthread_cond_broadcast(x)
#define CondDestroy(x) pthread_cond_destroy(x)
#define Setenv(x,y) setenv(x,y,1)
typedef pthread_mutex_t Mutex;
typedef pthread_cond_t CondVar;
extern int errno;
DIR *dp;
struct dirent *de;
struct stat flinfo; /* used to check if direntry are regular files */
#endif
#ifdef HDFS
#include <dlfcn.h> /* dlopen() include file */
#include <hdfs.h> /* libhdfs include file */
hdfsFS hfs = 0; /* Hadoop FS to connect to */
void *hdfs_handle = 0; /* dlopen() handle for libhdfs.so */
hdfsFS (*hdfsconn) (); /* pointer to hdfsConnectAsUser() */
hdfsFile (*hdfsopen) (); /* pointer to hdfsOpenFile() */
int (*hdfsclose) (); /* pointer to hdfsCloseFile() */
int (*hdfsseek) (); /* pointer to hdfsSeek() */
tSize (*hdfswrite) (); /* pointer to hdfsWrite() */
tSize (*hdfsread) (); /* pointer to hdfsRead() */
#endif
#ifdef XML
#ifndef HDFS
#include <dlfcn.h> /* dlopen() include file */
#endif
#include <libxml/xmlreader.h>
void *xml_handle = 0; /* dlopen() handle for libxml2.so */
xmlTextReaderPtr (*xmlfile) (); /* pointer to xmlNewTextReaderFilename() */
int (*xmlread) (); /* pointer to xmlTextReaderRead() */
int (*xmltype) (); /* pointer to xmlTextReaderNodeType() */
int (*xmldepth) (); /* pointer to xmlTextReaderDepth() */
xmlChar *(*xmllname) (); /* pointer to xmlTextReaderLocalName() */
int (*xmlnextattr) (); /* pointer to xmlTextReaderMoveToNextAttribute() */
xmlChar *(*xmlvalue) (); /* pointer to xmlTextReaderValue() */
int (*xmlfree) (); /* pointer to xmlFreeTextReader() */
#if defined __CYGWIN__
#define XML2LIBNAME "cygxml2-2.dll"
#elif defined __APPLE__ && defined __MACH__
#define XML2LIBNAME "libxml2.dylib"
#else
#define XML2LIBNAME "libxml2.so"
#endif
#endif
#define RAND(min,max) (min+rand()/(RAND_MAX+1.0)*(max-min+1))
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sql.h>
#include <sqlext.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
#if defined __APPLE__ && defined __MACH__
#include <sys/malloc.h>
#else
#include <malloc.h>
#endif
#define SQL_ATTR_ROWCOUNT64_PTR 5001 /* Attribute to get the 64bit rowcount when using the 32-bit ODBC driver */
#define SQL_ATTR_FETCHAHEAD 5003 /* Attribute to set fetchahead connection attribute */
#define SQL_ATTR_APPLNAME 5100 /* wms_mapping */
#define SQL_ATTR_CERTIFICATE_DIR 5200 /*Security */
#define SQL_ATTR_CERTIFICATE_FILE 5201
#define SQL_ATTR_CERTIFICATE_FILE_ACTIVE 5202
#define VTYPE_A 1 /* 0001 Variable Type = Alias */
#define VTYPE_U 2 /* 0010 Variable Type = User */
#define VTYPE_I 4 /* 0100 Variable Type = Internal */
#define VTYPE_UI 6 /* 0110 Variable Type = either User or Intenal */
#define MEMCPY(t,s,l) \
do { \
switch ( l ) {\
case 0:\
break;\
case 1:\
*(t) = *(s);\
break;\
case 2:\
*(t) = *(s);\
*((t)+1) = *((s)+1);\
break;\
case 3:\
*(t) = *(s);\
*((t)+1) = *((s)+1);\
*((t)+2) = *((s)+2);\
break;\
case 4:\
*(t) = *(s);\
*((t)+1) = *((s)+1);\
*((t)+2) = *((s)+2);\
*((t)+3) = *((s)+3);\
break;\
case 5:\
*(t) = *(s);\
*((t)+1) = *((s)+1);\
*((t)+2) = *((s)+2);\
*((t)+3) = *((s)+3);\
*((t)+4) = *((s)+4);\
break;\
case 6:\
*(t) = *(s);\
*((t)+1) = *((s)+1);\
*((t)+2) = *((s)+2);\
*((t)+3) = *((s)+3);\
*((t)+4) = *((s)+4);\
*((t)+5) = *((s)+5);\
break;\
case 7:\
*(t) = *(s);\
*((t)+1) = *((s)+1);\
*((t)+2) = *((s)+2);\
*((t)+3) = *((s)+3);\
*((t)+4) = *((s)+4);\
*((t)+5) = *((s)+5);\
*((t)+6) = *((s)+6);\
break;\
default:\
(void)memcpy( (void *)(t) , (void *)(s) , (size_t)(l) ) ;\
break;\
}\
} while ( 0 )
/* Struct and variables declaration */
struct info { /* ODBC/DBMS infor optional printed [-i] */
char *desc; /* Info description */
SQLUSMALLINT Oid; /* Info ID */
} Oinfo [] = { /* DBMS printed with -i option */
{"DBMS product name (SQL_DBMS_NAME)",SQL_DBMS_NAME},
{"DBMS product version (SQL_DBMS_VER)",SQL_DBMS_VER},
{"Database name (SQL_DATABASE_NAME)",SQL_DATABASE_NAME},
{"Server name (SQL_SERVER_NAME)",SQL_SERVER_NAME},
{"Data source name (SQL_DATA_SOURCE_NAME)",SQL_DATA_SOURCE_NAME},
{"Data source RO (SQL_DATA_SOURCE_READ_ONLY)",SQL_DATA_SOURCE_READ_ONLY},
{"ODBC Driver name (SQL_DRIVER_NAME)",SQL_DRIVER_NAME},
{"ODBC Driver version (SQL_DRIVER_VER)",SQL_DRIVER_VER},
{"ODBC Driver level (SQL_DRIVER_ODBC_VER)",SQL_DRIVER_ODBC_VER},
{"ODBC Driver Manager version (SQL_DM_VER)",SQL_DM_VER},
{"ODBC Driver Manager level (SQL_ODBC_VER)",SQL_ODBC_VER},
};
struct Otype { /* Database Object types */
char type; /* code */
SQLCHAR *Otype; /* extended name */
} Otypes[] = {
{'a', (SQLCHAR *)"ALIAS"}, /* alias */
{'t', (SQLCHAR *)"TABLE"}, /* table */
{'v', (SQLCHAR *)"VIEW"}, /* view */
{'y', (SQLCHAR *)"SYNONYM"}, /* synonym */
{'e', (SQLCHAR *)"SYSTEM TABLE"}, /* system table */
{'g', (SQLCHAR *)"GLOBAL TEMPORARY"}, /* global temporary */
{'l', (SQLCHAR *)"LOCAL TEMPORARY"}, /* local temporary */
{'m', (SQLCHAR *)"MV"}, /* materialized views */
{'M', (SQLCHAR *)"MVGROUPS"}, /* materialized view groups */
{'T', (SQLCHAR *)"TABLE"}, /* Olist print table desc */
{'D', (SQLCHAR *)"TABLE"}, /* Olist print table DDL */
{'U', (SQLCHAR *)"TABLE"}, /* Olist print table DDL multiplying CHAR/VARCHAR field by 4 */
{'A', (SQLCHAR *)""}, /* Olist uses SQL_ALL_TABLE_TYPES */
{'s', (SQLCHAR *)""}, /* Olist list schemas */
{'c', (SQLCHAR *)""}, /* Olist list catalogs */
};
struct rm {
char *cmd; /* initial command string */
char *mex; /* xx row(s) <mex> */
} rmess [] = {
{"other", "other"},
{"SELECT ", "selected"},
{"UPDATE ", "updated"},
{"DELETE ", "deleted"},
{"INSERT ", "inserted"}
};
char *olbmex[] = { "inserted", "copied", "piped" } ;
struct dbscmd { /* Database specific data */
char *dbname; /* database name */
char *trunc; /* fast delete template */
char *uncomm; /* uncommitted read syntax */
} dbscmds[] = {
{ "Generic", "DELETE FROM &tgt", "" } , /* dbt = 0 (GENERIC) */
{ "PostgreSQL", "TRUNCATE &tgt", "" } , /* dbt = 1 (POSTGRESQL) */
{ "Vertica Database", "TRUNCATE TABLE &tgt", "" } , /* dbt = 2 (VERTICA) */
{ "Microsoft SQL Server", "TRUNCATE TABLE &tgt", "WITH (NOLOCK)" } , /* dbt = 3 (SQLSERVER) */
{ "Teradata", "DELETE FROM &tgt", "" } , /* dbt = 4 (TERADATA) */
{ "NonStop SQL/MX", "PURGEDATA &tgt", "FOR READ UNCOMMITTED ACCESS" } , /* dbt = 5 (SQLMX) */
{ "Oracle", "TRUNCATE TABLE &tgt", "" } , /* dbt = 6 (ORACLE) */
{ "MySQL", "TRUNCATE TABLE &tgt", "" } , /* dbt = 7 (MYSQL) */
{ "Trafodion", "PURGEDATA &tgt", "FOR READ UNCOMMITTED ACCESS" } /* dbt = 8 (TRAFODION) */
};
enum DBs {
GENERIC = 0,
POSTGRESQL,
VERTICA,
SQLSERVER,
TERADATA,
SQLMX,
ORACLE,
MYSQL,
TRAFODION
};
struct tdesc { /* ODBC Table description for insert ops */
SQLSMALLINT Otype; /* ODBC Data Type */
SQLSMALLINT Octype; /* ODBC C data type */
SQLSMALLINT Odec; /* ODBC Decimal digits */
SQLSMALLINT Onull; /* ODBC Column nullability */
SQLSMALLINT OnameLen; /* ODBC column name length */
SQLCHAR *Oname; /* ODBC Column name */
SQLULEN Osize; /* ODBC Column Size */
SQLLEN OdisplaySize; /* ODBC column display size */
SQLLEN Ocdatabufl; /* ODBC C type buffer Size */
size_t dl; /* Default string length */
size_t start; /* Start address in rowset */
size_t pad; /* Used to memory align ODBC buffer elements on HP-UX */
};
struct execute {
char type; /* Possible types:
x = command specified through -x e=extracta(extract to file) D=diff (extract from tgt)
f = file specified through -f/-S/-P c=copy (extract from source) Z=diff (compares)
F = file specified with -S/-P under Win (free memory) C=copy (load to target) s=diff (sync writer - future use)
h = runsql file execution i=info p=pipe (extract from source)
l = loader I=interpreter P=pipe (load to target)
L = load buffer helper threads d=diff(extract from source) */
char *run; /* command to run or filename or query id, diff=output file */
char *pre; /* SQL script to be executed before load/copy/extract */
char *post; /* SQL script to be executed after load/copy/extract */
char *mpre; /* SQL script to be executed by each thread before extract */
char *cols; /* extract/copy: src table column list or original custom sql file name */
char *tgtsql; /* copy: target SQL to be executed instead of INSERT */
unsigned int *tgtp; /* copy: target SQL parameter position */
unsigned int dbt; /* Source/Target database type (see dbscmds[] structure */
int id; /* thread ID */
char tbe; /* to be executed flag */
unsigned char fs; /* field separator */
unsigned char rs; /* record separator */
unsigned char sq; /* string qualifier extract=type: 0+exit first diff, 1/2=print rows only in src/tgt */
unsigned char ec; /* escape character */
unsigned char pc; /* pad character , diff=number of key columns, copy/extract */
unsigned char em; /* embed file character, diff: key memory buffer to be freed */
size_t fsl; /* field separator length */
size_t r; /* initial rowset, 'Z' thread: key section length */
size_t ar; /* "actual" rowset (could be < r at the end) */
size_t rbs; /* rowset buffer size, Z thread: "actual" rowset for tgt threads */
size_t s; /* rowbuffer length (includes data & indicator) */
size_t sbl; /* allocated splitby buffer length (if !=0 grandfather should free etab[].sb) */
size_t iobuff; /* low level io buffer size. */
size_t buffsz; /* load: fread buffer size */
unsigned long mr; /* max number of records to insert/fetch; 0 = unlimited. Extract returns here tinit. Zthread:#records 'I" */
unsigned long TotalMaxRecords; /* total number of records */
int mer; /* max number of errors */
int roe; /* restart on error */
unsigned int roedel; /* delay before restarting on error */
unsigned long nr; /* number of records loaded/extracted by thread, Zthread: #records compared, general=no of output rows */
unsigned long nrt; /* total number of records inserted by "pool": Zthread: #records 'D' */
unsigned long nbs; /* total "base" number of records inserted. Extract returns here no. of extr bytes. Zthread: #records 'C' */
unsigned long nt; /* number of total load cycles */
unsigned long nw; /* number of wait load cycles */
unsigned int bpwc; /* copy: bytes per wide character (default=4) */
unsigned int bpc; /* copy: bytes per non-wide character (default=1) */
uint64_t seq; /* copy: sequence */
unsigned int seqp; /* copy: sequence position (tgt table field number (1=first field) */
unsigned int flg; /* etab bit mask flag (0=false 1=true):
0001 = FOR READ UNCOMMITTED ACCESS 010000 = quiet (no commands) 0100000000 = Oexec silent mode
0002 = truncate destination table 020000 = quiet (no results) 0200000000 = diff: tgt EOD
0004 = Stop On Error 040000 = print line mode 0400000000 = trim CHAR fields
0010 = Null Run 100000 = Interpreter Connected 1000000000 = cast non text fields
0020 = GZ Output (extract) 200000 = Save History & ifempty 2000000000 = diff: print 'I'
0040 = trunc decimal TIME/TS 400000 = desc result set 4000000000 = diff: print 'D'
0100 = binary data extraction 1000000 = complex load (Oload) 10000000000 = diff: print 'C'
0200 = print column names 2000000 = rtrim CHAR fields 20000000000 = diff: sync (future use)
0400 = print mark 4000000 = rtrim VARCHAR fields
1000 = multi-parted output 10000000 = ucs2toutf8 conversion
2000 = loader:EOF/copy-diff:src EOD 20000000 = NOT USED
4000 = loadbuff error 40000000 = Vertica's direct hint */
unsigned int flg2; /* etab bit mask flag (0=false 1=true):
0001 = Commit as a rowset multiplier 010000 = NOT USED 0100000000 = NOT USED
0002 = WITH NO ROLLBACK 020000 = print timeline 0200000000 = xml dump
0004 = quiet (no timing) 040000 = custom SQL extract/copy 0400000000 = user defined iobuff
0010 = diff "quick" mode 100000 = sql from file. free .sql 1000000000 = use libMapRClient.so instead of libhdfs
0020 = load "show" mode 200000 = force cdef binding 2000000000 = NOT USED
0040 = copy: cols to be excluded 400000 = NOT USED 4000000000 = NOT USED
0100 = src or tgt HDFS 1000000 = diff:odad (Or dt As dt) 10000000000 = NOT USED
0200 = flag used to print header line 2000000 = xml attr/text set/unset 20000000000 = NOT USED
0400 = diff gpar tgt checkdb/pre done 4000000 = xml export
1000 = tgt is set SQL_PARC_NO_BATCH 10000000 = xml fast (do not check element names)
2000 = diff pre SQL errors 20000000 = Oexec to create its own statement handle
4000 = copy: force SQL_C_CHAR bind 40000000 = trim VARCHAR cols other than CHAR */
int parent; /* Used to "group" executions */
int child; /* used by copy extractors to identify their loaders */
int cmt; /* commit: -1=end, 0=auto, >0 num rows, extract: fields in source table if analyzed */
int gzlev; /* extract: gzip compression level */
unsigned char lstat; /* load statuses (decimal):
0=write_avail
1=read_avail
2=write_busy
3=read_busy
4=pre_SQL_busy
5=pre-SQL-Error
10=write_job_completed
extract statuses (bit mask - octal):
0001=XML output to initialize
0002=pre-extract errors.
0004=pre-SQL to run.
0010=XML init errors
copy statuses (decimal):
0=write_avail
1=read_avail
2=write_busy
3=read_busy
4=pre_SQL_busy
5=pre-SQL-Error
diff statuses (octal bit mask):
0001 = src_write_avail
0002 = tgt_write_avail
0004 = src_data_ready
0010 = tgt_data_ready
0020 = src_write_busy
0040 = tgt_write_busy
0100 = src_read_busy
0200 = tgt_read_busy */
FILE *fso; /* Spool file pointer */
FILE *fo; /* Output file pointer (default stdout) */
FILE *fdmp; /* Copy: error dump file pointer */
#ifdef HDFS
hdfsFile fho; /* Hadoop File Handle */
char *hdfshost; /* hdfs host */
tPort hdfsport; /* hdfs port number */
char *hdfsuser; /* hdfs user */
tSize hblock; /* HDFS block size */
#endif
Mutex gmutex; /* Group mutex to synch pre-extract/copy/diff activities */
Mutex pmutex; /* Parallel load/extract/copy mutex */
CondVar pcvp; /* Parallel load/extract/copy condition variable - Producer */
CondVar pcvc; /* Parallel load/extract/copy condition variable - Consumer */
char *src; /* load/extract source */
char *tgt; /* load/extract target */
char *map; /* load=map file, extract/copy/diff=pwhere */
char *ns; /* load/extract Null string */
char *es; /* extract=emptystring, copy: tgtsql with positional parameters */
char *sql; /* extract custom SQL */
char *sb; /* extract split by column name */
char *bad; /* bad file */
char *key[MAX_PK_COLS]; /* diff key column names */
char loadcmd[3]; /* load/copy=command to use INSERT/UPSERT/UPSERT USING LOAD, IN/UP/UL */
#ifdef XML
char *xrt; /* load: xml row tag */
#endif
char *jsonKey; /* load: json key */
unsigned int nl; /* Null string length. */
unsigned int nloader; /* Copy: number of loaders per extraction thread */
unsigned int el; /* Empty String Length. */
unsigned int nltotal; /* Copy: total number of loaders per grandparent (ps * nl) */
unsigned int k; /* load lines to skip. extract: returned rsds. Copy: grandparent ID */
unsigned int sp; /* extract Start Partition load/copy: number of target table fields */
unsigned int ep; /* extract End Partition, copy: number of tgtsql parameters */
unsigned int ps; /* load/extract number of (still active) parallel streams, workload: number of agents */
unsigned int cr; /* Number of rows inserted since last commit. diff=1 first D-thread, =2 last D-thread */
unsigned int bucs2; /* bad UCS-2 character management: 0=skip, 1=force, 2=cpucs2 3=qmark */
long sbmin; /* Splitby min value */
long sbmax; /* Splitby max value */
char *gzp; /* extract gzip parameter */
SQLCHAR *Ocso[3]; /* load/extract ODBC array of catalog/schema/object pointers */
SQLCHAR *Orowsetl; /* ODBC pointer to the dynamically allocated rowset */
SQLCHAR *Orowsetl2; /* ODBC pointer to the dynamically allocated rowset used by 'Z' type threads (diff) */
SQLUSMALLINT *Ostatusl; /* ODBC pointer to the rowset status array */
SQLULEN Oresl; /* ODBC ulen to store SQLGetInfo results */
SQLULEN Omaxl; /* ODBC max char/varchar/binary column length to be loaded/retrieved */
struct tdesc *td; /* load target table definition structure */
char fldtr; /* Field truncation managenet during loads
0 truncates, warn and "load if text" (default)
1 truncates and "load if text"
2 warn and skip record
3 truncates, warn and load
4 truncates and load
5 do not check for field truncation
*/
char *mcfs; /* multi character field separator */
char *mcrs; /* multi character record separator */
} *etab = 0; /* Execution TABle: one entry per command or script */
struct ovar {
char type; /* Variable type: 'a'=alias, 'i'=interal, 'u'=user defined */
char name[MAX_VNLEN]; /* Variable name */
char *value; /* Variable value */
size_t nlen; /* Variable name length*/
size_t vlen; /* Variable value length*/
struct ovar *next; /* Pointer to the next element */
struct ovar *prev; /* Pointer to the previous element */
} **vars; /* pointer to array of "struct var" pointers */
struct thp { /* per thread properties */
int cr; /* Connection Redirection: >0 use this tid ODBC conn; -1 no connection */
int tid; /* Thread id */
SQLHDBC Oc; /* ODBC Connection handle pointer */
SQLHSTMT Os; /* ODBC Statement handle pointer */
struct ovar *tva; /* Chain of variables */
unsigned int cd; /* delay before starting the next command */
unsigned int cd2; /* (max) delay before starting the next command */
} *thps;
struct timeval tvi, /* init timeval struct */
tvs, /* start timeval struct */
tvn; /* now timeval struct */
SQLCHAR Odsn[64], Odsn2[64], /* ODBC data source name string */
Ouser[64], Ouser2[64], /* ODBC user name string */
Opwd[64], Opwd2[64], /* ODBC password string */
Oics[1024], /* ODBC SQLDriverConnect() Input Connection String */
Oocs[1024], /* ODBC SQLDriverConnect() Output Connection String */
Oaot[256], /* ODBC All Object Types list */
Obuf[3][1024]; /* ODBC buffer(s) */
SQLUINTEGER Onps=0, Onps2=0; /* ODBC Network Packet Size */
SQLHENV Oenv; /* ODBC environment handle */
SQLSMALLINT Osio[3]; /* ODBC smallint output */
SQLLEN Olen[7]; /* ODBC returned buff length */
SQLINTEGER Odps=0; /* ODBC display size for the given result column */
SQLRETURN Oret=0; /* ODBC return value */
SQLCHAR *Orowset = 0; /* ODBC pointer to the dynamically allocated rowset */
SQLHDBC Oc=0; /* ODBC Connection handle used by etabadd */
unsigned int f=000000000; /* global/command line flag mask:
Bit# Octal Meaning Bit# Octal Meaning
0 1 Data Source (-d) in cmd line 16 200000 Stop On Error (-soe / set soe)
1 2 User Name (-u) in cmd line 17 400000 Quiet Mode RES (-q res / set quiet res)
2 4 Password (-p) in cmd line 18 1000000 start time in CSV header (-b)
3 10 List Drivers (-lsdrvr) 19 2000000 Shuffle etab before execute (-Z)
4 20 List Data Sources (-lsdsn) 20 4000000 SQL_TXN_READ_UNCOMMITTED isolation level (-U)
5 40 Quiet Mode CMD (-q cmd / set quiet cmd) 21 10000000 Serial Run
6 100 Print Info (-i) 22 20000000 No Schema (-noschema / set noschema)
7 200 Exeute scripts/commands (-f/-x/-S/-P/-l/-e) 23 40000000 Print Line Mode (global)
8 400 Produce CSV output (-c) 24 100000000 No Catalog (-nocatalog /set nocatalog)
9 1000 Print Execution Table (-vv) 25 200000000 Print Column Names in command line
10 2000 Interpreter Mode (-I) in cmd line 26 400000000 UCS-2 to UTF-8 conversion in odb
11 4000 binary mode (copied to etab[eid].flg 0100) 27 1000000000 Dynamic Load Balancing
12 10000 Null Run (-N) 28 2000000000 Rolling DSN sequential (instead of RR)
13 20000 Verbose (-v) 29 4000000000 No Catalog as NULL
14 40000 do not connect (SQL Interpreter) startup 30 10000000000 Interpreter: case sensitive DB (set casesens)
15 100000 Desc result set (-drs) 31 20000000000 Quiet Mode TIMING (-q timing / set quiet timing) */
unsigned int f2 = 0000; /* second global f;ag mask:
Bit# Octal Meaning Bit# Octal Meaning
0 1 C-style comments as hint (not stripped) 16 200000 NOT USED
1 2 Global sequence during copy operations 17 400000 NOT USED
2 4 NOT USED 18 1000000 NOT USED
3 10 NOT USED 19 2000000 NOT USED
4 20 NOT USED 20 4000000 NOT USED
5 40 NOT USED 21 10000000 NOT USED
6 100 NOT USED 22 20000000 NOT USED
7 200 NOT USED 23 40000000 NOT USED
8 400 NOT USED 24 100000000 NOT USED
9 1000 NOT USED 25 200000000 NOT USED
10 2000 NOT USED 26 400000000 NOT USED
11 4000 NOT USED 27 1000000000 NOT USED
12 10000 NOT USED 28 2000000000 NOT USED
13 20000 NOT USED 29 4000000000 NOT USED
14 40000 NOT USED 30 10000000000 NOT USED
15 100000 NOT USED 31 20000000000 NOT USED */
unsigned int go = 1; /* General Flag: all threads to exit ASAP as soon as go = 0 */
unsigned int nloop=1; /* number of loops for command execution */
unsigned int ld=0; /* loop delay */
unsigned int ndsn=0; /* number of "rolling DSNs" */
unsigned int mf=0; /* Max number of rows to be fetched */
unsigned int mult = 4 ; /* char/varchar multiplier for -i U */
unsigned int wmult = 4 ; /* wide char/varchar multiplier for -i U */
uint64_t gseq = 0 ; /* global sequence value */
int exstat = EX_OK ; /* global exit status variable */
int cd=0, /* delay before executing the next command within a thread */
cd2=0, /* (max) delay before executing the same command within a thread */
nae=0, /* Number of Allocated etab[] chunks */
nc=1, /* number of output columns */
no=0, /* number of commands/scripts to run (entries in etab[]) */
cs=80, /* column size when multi-col output is used */
hist=200, /* Default History Size */
ssl=0, /* Set Schema Command length (before CAT.SCH) */
tn=0; /* number of threads */
unsigned char pad=0, /* Pad columns to their display size: 0=off, 1=full, 2=fit, 3=temporary full */
sq=0, /* String Qualifier */
fs=',', /* Field Separator */
ksep = ',', /* Default Thousand Separator */
dsep = '.', /* Default Decimal Separator */
rs='\n', /* Record Separator */
ec='\\'; /* Escape Character */
char *ns=0; /* Command line provided Null String */
extern unsigned int mrcol; /* screen columns defined in mreadline */
unsigned int w=0; /* general timeout in seconds */
size_t r=100; /* rowset for file loading */
SQLULEN Ores, /* ODBC ulen to store SQLGetInfo results */
Oqt = 0; /* ODBC Query Timeout (def = 0 unlimited) */
SQLSMALLINT Oocsl; /* ODBC output connection string length */
unsigned char clvn[MAX_CLV], /* command line arg numbers containing variable names */
clvv[MAX_CLV], /* command line arg numbers containing variable values */
clv = 0; /* Command line variables number */
char prompt[128], /* prompt string */
tprompt[128], /* prompt template */
*odbcmd=0, /* odb command (used to save av[0] */
*clca=0, /* command line connection attributes pointer */
*esrc=0, /* pointer to buffer containing src input file */
*tmpre=0; /* tmp pointer to buffer containing target mpre script name */
FILE *fp=0; /* File pointer */
FILE *fdmp=0; /* Dump file pointer */
struct tm *nowt; /* struct to print start date/time */
char nows[20], /* string with date/time in YYYY-MM-DD HH:MM:SS */
sfile[128], /* spool file name */
hfile[128], /* history file name */
cwd[64], /* current working directory */
chsch[64]; /* Set Schema command */
struct ovar *vv=0; /* struct var pointer for the Interpreter */
const char alnum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ; /* look-up tab to generate random strings */
void **globalPointers = NULL; /* save pointers to buffers which may shared by many thread and length determined at run time */
int nGlobalPointers = 0; /* number of pointers in globalPointers */
int naGlobalPointers = 0; /* number of globalPointers allocated chunk */
/* Functions prototypes */
static void Oerr(int eid, int tid, unsigned int line, SQLHANDLE Ohandle, SQLSMALLINT Otype);
static void Otinfo(SQLCHAR *Oca, SQLCHAR *Osc, SQLCHAR *Ota, char ddl);
static void Olist(SQLCHAR *Oca, SQLCHAR *Osc, SQLCHAR *Ota, char otype);
static void Odinfo();
static int Oexec(int tid, int eid, int ten, int scn, SQLCHAR *Ocmd, char *label); /* Execute a command */
static void usagexit(); /* Print usage info and exit */
static void sigcatch(int sig); /* signal catch routine */
static void cancel(int sig); /* executed in interactive mode when ^C */
static void etabadd(char type, char *run, int id); /* add entry(ies) to etab[] */
static void etabnew(int ix);
static void etabshuffle(); /* randomize etab[] entries */
static int etabout ( int eid , char *file );
static void Oload(int eid); /* load tables using etab entries #eid */
static void Oload2(int eid); /* fast load tables using etab entries #eid (only CSV with no map, no string qualifiers no escapes */
static void OloadX(int eid); /* load tables using etab entries #eid (only from XML) */
static void OloadJson(int eid); /* load tables using etab entries #eid (only from JSON) */
static void Oextract(int eid); /* extract tables using etab entries #eid */
static void strmcpy(char *tgt, char *src, size_t len);
static unsigned int strmcat(char *tgt, char *src, size_t len, char cas);
static unsigned int strmicmp(char *tgt, char *src, size_t len);
static char *strmprint(char *s);
static char *strmnum(double d, char *s, size_t l, unsigned int dd);
static char *strmtime(double secs, char *str);
static void splitcso(char *s, SQLCHAR **p, int a);
static int var_set(struct ovar **chain, char type, char *name, char *value);
static struct ovar *var_idx(struct ovar **chain, char type, char *name);
static int var_del(struct ovar **chain, char type, char *name);
static char *var_exp(char *str, size_t *lp, struct ovar **chain);
static unsigned int dconv ( char *fmt, char *str, char *out, size_t len, char t );
static int tokenize(char *s, int l, char *ss[]);
static void bcs(SQLCHAR *Oics, size_t csl, SQLCHAR *Oser, SQLCHAR *Opwd, SQLCHAR *Odsn, char *ca, int ctn);
static int mfprintf( FILE *fl1, FILE *fl2, char *fmt, ... );
static int mfputc( FILE *fl1, FILE *fl2, int ch );
static unsigned int checkdb(int eid, SQLHDBC *Oc, char *c, char *s);
static unsigned char mchar ( char *s );
static char* replace_escapes( char *s );
static void tclean(void *tid); /* Thread cleanup routine */
#ifndef _WIN32
static void tunlock(void *eid); /* Thread cleanup routine */
#endif
static void gclean(void); /* Global cleanup routine */
static int Oloadbuff(int eid);
static int Ocopy(int eid);
static void Odiff(int eid);
static void Ocompare(int eid);
static void cimutex(Mutex *mutexp);
static void cicondvar(CondVar *condvarp);
static void setan ( int eid, int tid, int nrag, char *rag[], char *ql );
static int Omexec(int tid, int eid, int ten, int scn, SQLCHAR *Ocmd, char *label, char *dcat, char *dsch);
static char *strup ( char *s );
static char *strlo ( char *s );
static char *strtrim(char *str);
int mrinit ( void );
void mrend ( void );
char *mreadline ( char *prompt, unsigned int *length );
int mrhadd(char *hrec, size_t hl);
int mrhinit(size_t hsize, char *hfile);
int mrhsave(char *hfile);
char *mresize(size_t size);
static char *expandtype( SQLSMALLINT Odt );
static void prec(char type, unsigned char *podbc, int eid);
static int runsql(int tid, int eid, int ten, char *script);
static unsigned int ifempty(int eid, char *table);
static int comp2asc(char *srcfld, char *tgtfld,unsigned int srclen, unsigned int maxlen);
static int comp32asc(char * srcfld, char *tgtfld, unsigned int srclen, unsigned int maxlen, int precision, int scale);
static int zoned2asc(char * srcfld, char *tgtfld, unsigned int srclen, unsigned int maxlen, int precision, int scale);
static int Otcol(int eid, SQLHDBC *Oc);
static unsigned int parseopt(char type, char *str, unsigned int len, unsigned int rp);
static void excol ( int eid, SQLHDBC *Ocn );
static void dumpbuff ( FILE *fd, int eid, unsigned char *buff, size_t buffl, unsigned int v );
#ifdef HDFS
static char *parsehdfs(int eid, char *hdfs);
#endif
#ifdef ODB_PROFILE
unsigned long tspdiff ( struct timespec *start, struct timespec *end ) ;
#endif
static int ucs2toutf8 ( SQLCHAR *str, SQLLEN *len, SQLULEN bs, int bu2 ) ;
static void addGlobalPointer(void *ptr);
static int is_valid_numeric(const char* str, size_t n);
int main(int ac, char *av[])
{
char *c=0, /* loop variable for command line parsing */
*line=0, /* pointer to input line - interactive mode */
*buff=0, /* pointer to the buffer to edit */
otype='d', /* object type:
'd': database info
'c': catalogs info
's': schema info
'A': all object types
't': table
'v': view
'a': alias
'y': synonym
'm': materialized view
'e': system table
'g': global temporary
'l': local temporary
'M': materialized view group
'T': desc tables
'D': desc tables (ddl output)
'U': desc tables (ddl output char/varchar x multiplier) */
*rag[MAX_ARGS], /* arguments in a row */
#ifdef __hpux
title[16], /* used by HP-UX pstat to rewrite ps command name */
#endif
odbcl[LINE_CHUNK], /* odb command line to rerun odb from the interpreter */
num[32], /* formatted numbers */
csn[MAXOBJ_LEN], /* current schema name */
ccn[MAXOBJ_LEN]; /* current catalog name */
int ch, /* char to read data file */
d=0, /* delay starting threads */
nrag=0, /* Number of rag[] arguments */
n=0, /* loop variable */
o=0, /* loop variable */
i=0, /* loop variable */
l=0, /* loop variable */
k=0, /* loop variable */
j=0; /* loop variable */
size_t rb = 0, /* rowset buffer */
rl = 0, /* to save rag[0] length */
bs = CMD_CHUNK; /* Ocmd buffer size */
unsigned int ll = 0, /* line length */
ol = 0; /* odb command line length */
time_t ptime; /* to register prompt time */
SQLUSMALLINT Odir; /* ODBC direction */
SQLCHAR *Ocso[3]; /* ODBC array of catalog/schema/object pointers */
SQLCHAR *Ocmd; /* ODBC command buffer */
SQLCHAR *O; /* ODBC Ctemp variable for memory realloc */
#ifdef _WIN32
HANDLE sts;
DWORD mode;
#else
struct termios sts, /* standard termios structure */
pts; /* password entry termios structure (no echo) */
#endif
char tim[15]; /* Formatted Time String */
double seconds = 0; /* seconds used for timings */
/* cleaning buffers */
memset(Odsn, 0, sizeof(Odsn));
memset(Ouser, 0, sizeof(Ouser));
memset(Opwd, 0, sizeof(Opwd));
memset(Odsn2, 0, sizeof(Odsn2));
memset(Ouser2, 0, sizeof(Ouser2));
memset(Opwd2, 0, sizeof(Opwd2));
memset(Oics, 0, sizeof(Oics));
memset(Oocs, 0, sizeof(Oocs));
memset(clvn, 0, sizeof(clvn));
memset(clvv, 0, sizeof(clvv));
for ( i = 0 ; i < 3 ; i++ )
memset (Obuf[i], 0, sizeof(Obuf[i]));
Ocso[0] = Ocso[1] = Ocso[2] = 0;
/* save odb command invocation */
odbcmd = av[0] ;
/* check ODB_USER, ODB_PWD, ODB_DSN env variables */
if ( ( c = getenv ("ODB_DSN") ) ) {
f |= 00001;
strmcpy((char *)Obuf[0], c, 2 * sizeof(Odsn));
for ( j = 0 ; Obuf[0][j] && Obuf[0][j] != ':' ; j++)
Odsn[j] = Obuf[0][j];
if ( Obuf[0][j] ) /* we found ':' */
strmcpy((char *)Odsn2, (char *)&Obuf[0][j+1], sizeof(Odsn2));
}
if ( ( c = getenv ("ODB_USER") ) ) {
f |= 00002;
strmcpy((char *)Obuf[0], c, 2 * sizeof(Ouser));
for ( j = 0 ; Obuf[0][j] && Obuf[0][j] != ':' ; j++)
Ouser[j] = Obuf[0][j];
if ( Obuf[0][j] ) /* we found ':' */
strmcpy((char *)Ouser2, (char *)&Obuf[0][j+1], sizeof(Ouser2));
}
if ( ( c = getenv ("ODB_PWD") ) ) {
f |= 00004;
strmcpy((char *)Obuf[0], c, 2 * sizeof(Opwd));
for ( j = 0 ; Obuf[0][j] && Obuf[0][j] != ':' ; j++)
Opwd[j] = Obuf[0][j];
if ( Obuf[0][j] ) /* we found ':' */
strmcpy((char *)Opwd2, (char *)&Obuf[0][j+1], sizeof(Opwd2));
}
memset(Obuf[0], 0, sizeof(Obuf[0]));
/* Allocate environment handle & set ODBC 3 */
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_ENV,
(SQLHANDLE)SQL_NULL_HANDLE, &Oenv))){
fprintf(stderr, "odb [main(%d)] - Error allocating Environment Handle\n", __LINE__);
exit(EX_ODBCERR);
}
if (!SQL_SUCCEEDED(Oret=SQLSetEnvAttr(Oenv, SQL_ATTR_ODBC_VERSION,
(void *) SQL_OV_ODBC3, 0))){
fprintf(stderr, "odb [main(%d)] - Error setting SQL_OV_ODBC3\n", __LINE__);
exit(EX_ODBCERR);
}
/* Register start time */
gettimeofday(&tvi, (void *)NULL);
/* handling command line options (no getopt under Win32) */
if ( ac == 1 ) {
fprintf(stderr, "odb [main(%d) - No command line options. Check \"-h\"\n", __LINE__);
exit( EX_USAGE );
}
for(i=1; i<ac; i++) {
if ( !strcmp(av[i], "-h") ) { /* help */
usagexit();
} else if ( !strcmp(av[i], "-version") ) { /* print odb version */
printf("%s - %s\nBuild [%s %s]: %s\n", odbid, odbauth, __DATE__, __TIME__, ODBBLD);
exit ( EX_OK );
} else if ( !strcmp(av[i], "-d") ) { /* data source name */
f |= 00001;
if ( ++i < ac ) {
for ( j = 0 ; av[i][j] && av[i][j] != ':' ; j++);
if ( av[i][j] ) /* we found ':' */
strmcpy((char *)Odsn2, &av[i][j+1], sizeof(Odsn2));
strmcpy((char *)Odsn, av[i], (size_t)j);
} else {
fprintf(stderr, "odb [main(%d)] - Missing data source name after -%s\n", __LINE__, av[i-1]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-ca") ) { /* Connection String */
f |= 00001;
if ( ++i < ac ) {
clca = av[i];
} else {
fprintf(stderr, "odb [main(%d)] - Missing connection string after -%s\n", __LINE__, av[i-1]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-u") ) { /* username */
f |= 00002;
if ( ++i < ac ) {
for ( j = 0 ; av[i][j] && av[i][j] != ':' ; j++);
if ( av[i][j] ) /* we found ':' */
strmcpy((char *)Ouser2, &av[i][j+1], sizeof(Ouser2));
strmcpy((char *)Ouser, av[i], (size_t)j);
} else {
fprintf(stderr, "odb [main(%d)] - Missing user name after %s\n", __LINE__, av[i-1]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-p") ) { /* password */
f |= 00004;
if ( ++i < ac ) {
for ( j = 0 ; av[i][j] && av[i][j] != ':' ; j++);
if ( av[i][j] ) /* we found ':' */
strmcpy((char *)Opwd2, &av[i][j+1], sizeof(Opwd2));
strmcpy((char *)Opwd, av[i], (size_t)j);
#ifdef __hpux
strmcpy(title, ODBVER, sizeof(title));
pst.pst_command = title ;
pstat(PSTAT_SETCMD, pst, strlen(title), 0, 0);
#else
av[i][0] = '?'; /* hide command line password */
#endif
for ( j = 1 ; av[i][j] ; j++ )
av[i][j] = '\0';
} else {
fprintf(stderr, "odb [main(%d)] - Missing password after %s\n", __LINE__, av[i-1]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-lsdrv") ) { /* list available Drivers */
f |= 00010;
} else if ( !strcmp(av[i], "-lsdsn") ) { /* list available Data Sources */
f |= 00020;
} else if ( !strcmp(av[i], "-q") ) { /* quiet (do not print cmd & output) */
if ( (i+1) < ac ) {
if ( !strcmp(av[i+1], "all") ) {
i++;
f |= 020000400040;
} else if ( !strcmp(av[i+1], "cmd") ) {
i++;
f |= 00040;
} else if ( !strcmp(av[i+1], "res") ) {
i++;
f |= 0400000;
} else if ( !strcmp(av[i+1], "timing") ) {
i++;
f |= 020000000000;
} else {
if ( av[i+1][0] == '-' ) {
f |= 0400040;
} else {
fprintf(stderr, "odb [main(%d)] - Wrong parameter >%s< after %s\n", __LINE__, av[i+1], av[i]);
exit( EX_USAGE );
}
}
} else {
f |= 0400040;
}
} else if ( !strcmp(av[i], "-i") ) { /* print database / schema / table info */
f |= 00100;
etabadd('i', "", 0);
if ( (i+1) < ac && av[i+1][0] != '-' ) { /* TYPE[multiplier]:CATALOG.SCHEMA.[TABLE] */
switch(av[++i][0]) {
case 'd': /* database info */
case 'c': /* catalogs info */
case 's': /* schema info */
case 'A': /* all object types */
case 't': /* table */
case 'v': /* view */
case 'a': /* alias */
case 'y': /* synonym */
case 'm': /* materialized view */
case 'e': /* system table */
case 'g': /* global temporary */
case 'l': /* local temporary */
case 'M': /* materialized view group */
case 'T': /* desc tables */
case 'D': /* desc tables (ddl output) */
otype = av[i][0] ;
rag[0] = &av[i][2] ;
break ;
case 'U': /* desc tables (ddl output char/varchar x multiplier) */
otype = av[i][0] ;
if ( av[i][1] >= '0' && av[i][1] <= '9' ) {
mult = (unsigned int)strtol(&av[i][1], (char **)NULL, 10);
for ( j = 0; av[i][j] && av[i][j] != ','; j++);
wmult = (unsigned int)strtol(&av[i][++j], (char **)NULL, 10);
} else {
fprintf(stderr, "odb [main(%d)] - Invalid multiplier \'%s\'\n", __LINE__, &av[i][1] );
exit ( EX_USAGE ) ;
}
break ;
default:
fprintf(stderr, "odb [main(%d)] - Invalid object type \'%c\'\n", __LINE__, av[i][0] );
exit ( EX_USAGE ) ;
break ;
}
for ( j = 0; av[i][j] && av[i][j] != ':'; j++);
if ( av[i][++j] ) {
rag[0] = &av[i][j] ;
} else {
fprintf(stderr, "odb [main(%d)] - Missing parameter. Use -i type[mult,wmult]:catalog.schema[.table]\n", __LINE__ );
exit ( EX_USAGE ) ;
}
}
} else if ( !strcmp(av[i], "-I") ) { /* Interpreter mode */
f |= 022000; /* interactive and verbose flags on */
if ( ( i + 1 ) < ac && av[i+1][0] != '-' )
etabadd('I', av[++i], 0);
else
etabadd('I', "", 0);
printf("%s - SQL Interpreter Mode\nBuild: %s [%s %s]\n", odbid, ODBBLD, __DATE__, __TIME__);
} else if ( !strcmp(av[i], "-noconnect") ) { /* do not connect on SQL int startup */
f |= 040000;
} else if ( !strcmp(av[i], "-x") || /* execute command(s) */
!strcmp(av[i], "-f") ) { /* run script file(s) */
f |= 00200;
if ( ++i < ac ) {
for ( j = 0; av[i][j] && isdigit((int)av[i][j]) ; j++);
for ( l = 0 ; l < (atoi(av[i])?atoi(av[i]):1) ; l++)
etabadd(av[i-1][1], (av[i][j]==':')?&av[i][j+1]:av[i], no);
} else {
fprintf(stderr, "odb [main(%d)] - Missing command/file after %s\n", __LINE__, av[i-1]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-l") || /* Load */
!strcmp(av[i], "-e") || /* Extract */
!strcmp(av[i], "-cp") || /* Copy */
!strcmp(av[i], "-pipe") || /* Pipe */
!strcmp(av[i], "-diff") ) { /* Table diff */
f |= 0200;
if ( ++i < ac ) {
etabadd(av[i-1][1], av[i], no);
} else {
fprintf(stderr, "odb [main(%d)] - Missing parameters after %s\n", __LINE__, av[i-1]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-c") ) { /* output in csv format */
f |= 00400;
} else if ( !strcmp(av[i], "-r") ) { /* rowset */
if ( (i+1) < ac ) {
if ( av[++i][0] == 'k' || av[i][0] =='K' ) {
r = 0 ;
rb = 1024 * atoi (&av[i][1]);
} else if ( av[i][0] == 'm' || av[i][0] =='M' ) {
r = 0 ;
rb = 1024 * 1024 * atoi (&av[i][1]);
} else {
r = (size_t) atoi(av[i]);
rb = 0;
}
} else {
fprintf(stderr, "odb [main(%d)] - Missing rowset after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-N") ) { /* NULL run */
f |= 010000;
} else if ( !strcmp(av[i], "-fs") ) { /* field separator */
if ( (i+1) < ac ) {
fs = mchar(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing field separator after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-rs") ) { /* record separator */
if ( (i+1) < ac ) {
rs = mchar(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing field separator after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-ec") ) { /* escape character */
if ( (i+1) < ac ) {
ec = mchar(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing escape character after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-ns") ) { /* null string */
if ( (i+1) < ac ) {
ns = av[++i];
} else {
fprintf(stderr, "odb [main(%d)] - Missing null string after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-ksep") ) { /* thousands separator */
if ( (i+1) < ac ) {
ksep = mchar(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing thousands separator after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-dsep") ) { /* decimal separator */
if ( (i+1) < ac ) {
dsep = mchar(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing decimal separator after %s\n", __LINE__, av[i]);
exit( EX_USAGE );
}
} else if ( !strcmp(av[i], "-delay") ) { /* Delay starting threads */
if ( (i+1) < ac ) {
d = atoi(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing delay (ms) after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-ldelay") ) { /* Delay starting threads */
if ( (i+1) < ac ) {
ld = (unsigned int)atoi(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing delay (ms) after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-ndsn") ) { /* Rolling DSN increment */
if ( (i+1) < ac ) {
if ( av[++i][0] == '+' ) {
f |= 02000000000; /* Sequential Rolling DSN (instead of RR) */
ndsn = atoi(&av[i][1]);
} else {
ndsn = atoi(av[i]);
}
} else {
fprintf(stderr, "odb [main(%d)] - Missing number of rollong DSN after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-soe") ) { /* Stop On Error */
f |= 0200000;
} else if ( !strcmp(av[i], "-pcn") ) { /* Print Column Names */
f |= 0200000000;
} else if ( !strcmp(av[i], "-pad") ) { /* Pad columns & Print Column Names */
pad = 1 ;
fs = '|';
f |= 0200000000;
} else if ( !strcmp(av[i], "-dlb") ) { /* Dynamic Load Balancing */
f |= 01000000000;
} else if ( !strcmp(av[i], "-plm") ) { /* Print Line Mode */
f |= 040000000;
} else if ( !strcmp(av[i], "-P") || /* run dir scripts in parallel */
!strcmp(av[i], "-S") ) { /* run dir scripts serially */
f |= 00200;
if ( av[i][1] == 'S' )
f |= 010000000;
if ( ( i + 1 ) < ac ) {
for ( j = 0; av[i+1][j] && av[i+1][j] != ':'; j++);
if ( av[i+1][j] ) {
n = atoi(av[i+1]);
i++;
} else {
n = 1;
}
} else {
fprintf(stderr, "odb [main(%d)] - Missing command/file after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
#ifdef _WIN32
for ( j = (int) strlen(av[++i]) -1 ;
j && av[i][j] != '\\' && av[i][j] != '/' ; j-- );
if( (h = FindFirstFile((LPCSTR)av[i], &finfo)) == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "odb [main(%d)] - Invalid file handle expanding %s: %d\n",
__LINE__, av[i], GetLastError());
exit(EX_NOINPUT);
} else {
st = no; /* save start point */
do {
l = (int) strlen((char *)finfo.cFileName) + j + 2;
if ( ( buff = (char *)calloc ( 1, l + 1 ) ) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating memory for %s: [%d] %s\n",
__LINE__, (char *) finfo.cFileName, errno, strerror(errno) );
exit(EX_OSERR);
}
if ( j )
strmcpy (buff, av[i], (size_t) j + 1 );
strmcpy (buff, av[i], (size_t) j + 1 );
(void) strmcat(buff, (char *)finfo.cFileName, l, 0);
etabadd('F', buff, ( f & 010000000 ) ? 0 : no);
if ( n > 0 ) /* serial replication. Ex: -P 3: ... */
for ( o = n - 1 ; o ; o-- )
etabadd('f', buff, ( f & 010000000 ) ? 0 : no);
} while(FindNextFile(h, &finfo) && GetLastError() != ERROR_NO_MORE_FILES);
FindClose(h);
en = no; /* save end point */
}
if ( n < 0 ) /* Round Robin replication. Ex: -P -3: ... */
for ( o = - ( n + 1 ) ; o ; o-- )
for ( j = st ; j < en ; j++ )
etabadd('f', etab[j].run, ( f & 010000000 ) ? 0 : no);
#else
l = (int) strlen(av[++i]);
if ( av[i][l-1] == '/' ) {
if ( ( dp = opendir (av[i]) ) == (DIR *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error opening input dir %s: [%d] %s\n",
__LINE__, av[i], errno, strerror(errno) );
exit(EX_NOINPUT);
}
if ( n > 0 ) {
while ( ( de = readdir ( dp ) ) != (struct dirent *)NULL ) {
l += (int) strlen ( de->d_name ) + 1;
if ( ( buff = calloc ( 1, l + 1 ) ) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating memory for %s: [%d] %s\n",
__LINE__, (char *) de->d_name, errno, strerror(errno) );
exit(EX_OSERR);
}
strmcpy ( buff, av[i], sizeof(sfile) );
(void) strmcat ( buff, de->d_name, sizeof(sfile), 0 );
if ( ! lstat(buff, &flinfo ) && S_ISREG(flinfo.st_mode) ) {
for ( o = n ; o ; o-- ) {
etabadd('F', buff, ( f & 010000000 ) ? 0 : no);
}
}
}
} else {
for ( o = n ; o ; o++ ) {
while ( ( de = readdir ( dp ) ) != (struct dirent *)NULL ) {
l += (int) strlen ( de->d_name ) + 1;
if ( ( buff = calloc ( 1, l + 1) ) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating memory for %s: [%d] %s\n",
__LINE__, (char *) de->d_name, errno, strerror(errno) );
exit(EX_OSERR);
}
strmcpy ( buff, av[i], sizeof(sfile) );
(void) strmcat ( buff, de->d_name, sizeof(sfile), 0 );
if ( ! lstat(buff, &flinfo ) && S_ISREG(flinfo.st_mode) ) {
etabadd('F', buff, ( f & 010000000 ) ? 0 : no);
}
}
rewinddir( dp );
}
}
closedir ( dp );
} else {
if ( n > 0 ) {
for ( ; ( i + 1 ) < ac && av[i][0] != '-' ; i++ ) {
for ( o = n ; o ; o-- )
etabadd('f', av[i], ( f & 010000000 ) ? 0 : no);
}
} else {
for ( l = i, o = n ; o ; o++, i = l ) {
for ( ; ( i + 1 ) < ac && av[i][0] != '-' ; i++ )
etabadd('f', av[i], ( f & 010000000 ) ? 0 : no);
}
}
i--;
}
#endif
} else if ( !strcmp(av[i], "-T") ) { /* Max number of parallel threads */
if ( (i+1) < ac ) {
tn = atoi(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing max threads after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-F") ) { /* Max number of rows to be fetched */
if ( (i+1) < ac ) {
mf = atoi(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing max rows to be fetched after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-ttime") ) {
if ( (i+1) < ac ) {
cd = (unsigned int)atoi(av[++i]);
for ( j = 0; av[i][j] && av[i][j] != ':'; j++);
cd2 = (unsigned int)atoi(&av[i][j+1]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing delay (ms) after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-L") ) { /* Number of loops */
if ( (i+1) < ac ) {
n = atoi(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing number of loops after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
if ( n < 0 ) { /* negative number of loops: reset to 1 */
fprintf(stderr, "odb [main(%d)] - Inconsistent #loops: %d. Forced to 1\n" , __LINE__, n);
nloop = 1;
} else { /* positive number of loops: ok */
nloop = (unsigned int)n;
}
} else if ( !strcmp(av[i], "-timeout") ) { /* General timeout */
#ifdef _WIN32
fprintf(stderr, "odb [main(%d)] - timeout not available under Win32\n", __LINE__);
exit ( EX_USAGE ) ;
#else
if ( (i+1) < ac ) {
if ( av[++i][0] == 'h' || av[i][0] == 'H' )
w = 3600*(unsigned int)atoi(&av[i][1]);
else if ( av[i][0] == 'm' || av[i][0] == 'M' )
w = 60*(unsigned int)atoi(&av[i][1]);
else
w = (unsigned int)atoi(av[i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing timeout after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
#endif
} else if ( !strcmp(av[i], "-U") ) { /* sets SQL_TXN_READ_UNCOMMITTED */
f |= 04000000;
} else if ( !strcmp(av[i], "-v") ) { /* be verbose */
f |= 020000;
} else if ( !strcmp(av[i], "-vv") ) { /* Print Execution Table */
f |= 001000;
} else if ( !strcmp(av[i], "-b") ) { /* add start time in the CSV header */
f |= 01000000;
} else if ( !strcmp(av[i], "-noschema") ) { /* no schema */
f |= 020000000;
} else if ( !strcmp(av[i], "-nocatalog") ) { /* no catalog */
f |= 0100000000;
} else if ( !strcmp(av[i], "-nocatnull") ) { /* no catalog: use NULL instead of empty strings */
f |= 04100000000;
} else if ( !strcmp(av[i], "-ucs2toutf8") ) { /* set UCS-2 to UTF-8 conversion in odb */
f |= 0400000000;
} else if ( !strcmp(av[i], "-Z") ) { /* shuffle etab[] */
f |= 02000000;
} else if ( !strcmp(av[i], "-var") ) { /* User defined variables */
if ( clv >= MAX_CLV) {
fprintf(stderr, "odb [main(%d)] - Max number of command line variables (%d) reached\n",
__LINE__, MAX_CLV);
} else if ( (i+2) < ac ) {
clvn[clv]=(unsigned char)++i;
clvv[clv++]=(unsigned char)++i;
} else {
fprintf(stderr, "odb [main(%d)] - Missing parameters after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-sq") ) { /* string qualifier */
if ( (i+1) < ac ) {
sq = mchar(av[++i]);
} else {
fprintf(stderr, "odb [main(%d)] - Missing parameters after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-drs") ) { /* describe the result set */
f |= 0100000;
} else if ( !strcmp(av[i], "-bin") ) { /* bind to SQL_C_BINARY */
f |= 04000;
} else if ( !strcmp(av[i], "-dump") ) { /* dump ODBC buffers to file */
if ( (i+1) < ac ) {
if ( !(strcmp(av[++i], "stdout") ) ) {
fdmp = stdout ;
} else {
if ( ( fdmp = fopen(av[i], "w") ) == (FILE *)NULL )
fprintf(stderr, "odb [main(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, av[i], errno, strerror(errno));
}
} else {
fprintf(stderr, "odb [main(%d)] - Missing dumpfile after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-hint") ) { /* C-Style comments as hints */
f2 |= 0001 ;
} else if ( !strcmp(av[i], "-nps") ) { /* Network Packet Size */
if ( (i+1) < ac ) {
Onps = (SQLUINTEGER)strtol(av[++i], (char **)NULL, 10);
for ( j = 0; av[i][j] && av[i][j] != ':'; j++);
Onps2 = (SQLUINTEGER)strtol(&av[i][j+1], (char **)NULL, 10);
} else {
fprintf(stderr, "odb [main(%d)] - Missing parameter after %s\n", __LINE__, av[i]);
exit ( EX_USAGE ) ;
}
} else if ( !strcmp(av[i], "-casesens") ) { /* Case sensitive DB */
f |= 010000000000 ;
} else {
fprintf(stderr, "odb [main(%d)] - Unknow option %s. Ignored\n", __LINE__, av[i]);
exstat = EX_USAGE ; /* This is the right exit status now... unless valid jobs overwrite it */
}
}
/* Close etabadd() connection (if any) */
if (Oc) {
if (!SQL_SUCCEEDED(Oret=SQLDisconnect(Oc)))
Oerr(-1, -1, __LINE__, Oc, SQL_HANDLE_DBC);
(void)SQLFreeHandle(SQL_HANDLE_DBC, Oc); /* free conn handle */
Oc = 0;
}
/* Exit if no etab entries and !-I !-lsdsn !-lsdrv */
if ( !no && !(f & 020030) )
exit( exstat );
if ( ! ( f & 02000 ) ) /* Not Interpreter mode */
(void)signal(SIGINT, SIG_IGN); /* Ignore signal interrupts for now */
#ifdef _WIN32
/* Initialize dlbmutex (Win32) */
InitializeCriticalSection(&dlbmutex);
#endif
if ( f & 00010 ) { /* [-lsdrv] list available driver */
for(Odir=SQL_FETCH_FIRST; SQL_SUCCEEDED(Oret=SQLDrivers(Oenv, Odir,
Obuf[0], (SQLSMALLINT)sizeof(Obuf[0]), &Osio[0],
Obuf[1], (SQLSMALLINT)sizeof(Obuf[1]), &Osio[1]));
Odir=SQL_FETCH_NEXT) {
printf("%s - %s\n", (char *)Obuf[0], (char *)Obuf[1]);
if (Oret == SQL_SUCCESS_WITH_INFO)
fprintf(stderr, "odb [main(%d)] - Warning data truncation\n", __LINE__);
}
(void)SQLFreeHandle(SQL_HANDLE_ENV, Oenv);
exit( EX_OK );
}
if ( f & 00020 ) { /* [-lsdsn] list available data sources */
for(Odir=SQL_FETCH_FIRST; SQL_SUCCEEDED(Oret=SQLDataSources(Oenv, Odir,
Obuf[0], (SQLSMALLINT)sizeof(Obuf[0]), &Osio[0],
Obuf[1], (SQLSMALLINT)sizeof(Obuf[1]), &Osio[1]));
Odir=SQL_FETCH_NEXT) {
printf("%s - %s\n", (char *)Obuf[0], (char *)Obuf[1]);
if (Oret == SQL_SUCCESS_WITH_INFO)
fprintf(stderr, "odb [main(%d)] - Warning data truncation\n", __LINE__);
}
(void)SQLFreeHandle(SQL_HANDLE_ENV, Oenv);
exit( EX_OK );
}
/* Ask the password if missing in interactive mode */
if ( ( f & 020003 ) && ! ( f & 00004 ) ) {
#ifdef _WIN32
sts = GetStdHandle(STD_INPUT_HANDLE); /* get STDIN Handle */
GetConsoleMode(sts, (LPDWORD)&mode); /* get current STDIN mode*/
SetConsoleMode(sts, mode & ~ENABLE_ECHO_INPUT); /* Set mode for STDIN with no ECHO */
do {
fputs("Password:", stdout); /* Password Prompt */
} while (!fgets((char *)Opwd, sizeof(Opwd), stdin)); /* Get Password */
SetConsoleMode(sts, mode); /* Reset Original console mode */
putc('\n', stdout);
#else
tcgetattr(0,&sts); /* get current settings for file descriptor 0 */
pts = sts; /* copy the current settings */
pts.c_lflag &= ~ECHO; /* change password settings to no echo */
pts.c_lflag |= ECHONL; /* ... but echo New Line */
tcsetattr(0,TCSANOW,&pts); /* use passord settings */
do {
fputs("Password:", stdout); /* Password prompt */
} while (!fgets((char *)Opwd, sizeof(Opwd), stdin));
tcsetattr(0,TCSANOW,&sts); /* reset previous settings */
#endif
Opwd[strlen((char *)Opwd) - 1] = '\0'; /* Clear ending new line */
f |= 00004;
}
/* From now on we need a connection unless -I */
if ( ( f & 040000 ) && ! ( f & 020000 ) ) {
fprintf(stderr, "odb [main(%d)] - Connection required\n", __LINE__);
exit ( EX_USAGE );
}
/* passwd (04), user (02) and DSN (01) needed if not interactive*/
if ( !(f & 02000 ) ) {
if ( ! ( f & 0001 ) ) {
fprintf(stderr, "odb [main(%d)] - Missing DSN\n", __LINE__ );
exit ( EX_USAGE ) ;
}
if ( ! ( f & 0002 ) ) {
fprintf(stderr, "odb [main(%d)] - Missing User\n", __LINE__ );
exit ( EX_USAGE ) ;
}
if ( ! ( f & 0004 ) ) {
fprintf(stderr, "odb [main(%d)] - Missing password\n", __LINE__ );
exit ( EX_USAGE ) ;
}
}
/* Check threads number vs Execution Table entries */
if ( tn > no ) {
fprintf(stderr, "odb [main(%d)] - Warning: won't be created more thread (%d) then needed (%d).\n",
__LINE__, tn, no);
tn = no;
}
/* Check threads number and Serial Execution */
if ( f & 010000000 && tn > 1 ) {
fprintf(stderr, "odb [main(%d)] - Warning: only one thread will be used for serial runs.\n",
__LINE__);
tn = 1;
}
/* Distribute work across threads */
if ( tn ) {
for ( i = j = 0; i < no; i++, j = ( j + 1 == tn ? 0 : j + 1 ) )
etab[i].id = j;
} else {
tn = ( f & 010000000 ) || !no ? 1 : no ;
}
/* Copy Global Flags into Single Execution flags */
for ( i = 0 ; i < no ; i++ ) {
if( f & 040 ) etab[i].flg |= 010000; /* Quiet cmd */
if( f & 04000 ) etab[i].flg |= 0100; /* Binary mode */
if( f & 010000 ) etab[i].flg |= 0010; /* Null Run */
if( f & 0100000 ) etab[i].flg |= 0400000; /* Describe Result Set */
if( f & 0200000 ) etab[i].flg |= 0004; /* Stop On Error */
if( f & 0400000 ) etab[i].flg |= 020000; /* Quiet results */
if( f & 040000000 ) etab[i].flg |= 040000; /* Print Line Mode */
if( f & 0200000000 ) etab[i].flg |= 0200; /* Print Column Names */
if( f & 0400000000 ) etab[i].flg |= 010000000; /* ucs2toutf8 */
if( f & 020000000000 ) etab[i].flg2 |= 0004; /* Quiet Timing */
if ( r && !etab[i].r )
etab[i].r = r;
if ( rb && !etab[i].rbs )
etab[i].rbs = rb;
if ( mf ) {
etab[i].mr = mf; /* max number of rows to fetch */
if ( etab[i].r > etab[i].mr ) /* if # records to fetch < rowset ... */
etab[i].r = etab[i].mr; /* make rowset = records to fetch */
}
if ( fs && !etab[i].fs )
etab[i].fs = fs;
if ( rs && !etab[i].rs )
etab[i].rs = rs;
if ( sq && !etab[i].sq )
etab[i].sq = sq;
if ( ec && !etab[i].ec )
etab[i].ec = ec;
if ( ns && !etab[i].ns ) {
etab[i].ns = ns; /* calculate nullstr length only once... */
etab[i].nl = i ? etab[0].nl : (unsigned int) strlen(ns);
}
}
/* Print Execution Table content */
if ( f & 001000 ) {
fprintf (stderr, "odb [main(%d)] - Execution Table content (%d items):\n", __LINE__, no);
for ( i = 0 ; i < no ; i++ ) {
fprintf(stderr, "etab[%d]:\n", i);
fprintf(stderr, "\tType (.type): %c\n", etab[i].type);
fprintf(stderr, "\tThread ID (.id): %d\n", etab[i].id);
fprintf(stderr, "\tParent EID (.parent): %d\n", etab[i].parent);
fprintf(stderr, "\tChild EID (.child): %d\n", etab[i].child);
fprintf(stderr, "\tTBE flag (.tbe): %d\n", etab[i].tbe);
fprintf(stderr, "\tField Separator (.fs): %d (decimal value)\n", etab[i].fs);
fprintf(stderr, "\tMulti Characters Field Separator (.mcfs): %s\n", etab[i].mcfs);
fprintf(stderr, "\tRecord Separator (.rs): %d (decimal value)\n", etab[i].rs);
fprintf(stderr, "\tMulti Characters Record Separator (.mcrs): %s\n", etab[i].mcrs);
fprintf(stderr, "\tString Qualifier (.sq): %d (decimal value)\n", etab[i].sq);
fprintf(stderr, "\tEscape Character (.ec): %d (decimal value)\n", etab[i].ec);
fprintf(stderr, "\tEmbed file Character (.em): %d (decimal value)\n", etab[i].em);
fprintf(stderr, "\tPad Character (.pc): %d (decimal value)\n", etab[i].pc);
fprintf(stderr, "\tRun (.run): %s\n", etab[i].run);
fprintf(stderr, "\tMax Rows (.mr): %lu\n", etab[i].mr);
fprintf(stderr, "\tMax Errors (.mer): %d\n", etab[i].mer);
fprintf(stderr, "\tEID flags (.flg/.flg2): %o/%o (octal values)\n", etab[i].flg, etab[i].flg2);
fprintf(stderr, "\tRowset (.r): " SIZET_SPEC " rows\n", etab[i].r);
fprintf(stderr, "\tRowset Buffer Size (.rbs): " SIZET_SPEC " bytes\n", etab[i].rbs);
fprintf(stderr, "\tRow Buffer Size (.s): " SIZET_SPEC " bytes\n", etab[i].s);
fprintf(stderr, "\tIO Buffer Size (.iobuff): %zu bytes\n", etab[i].iobuff);
fprintf(stderr, "\tRW Buffer Size (.buffsz): %zu bytes\n", etab[i].buffsz);
fprintf(stderr, "\tPre SQL (.pre): %s\n", etab[i].pre);
fprintf(stderr, "\tMPre SQL (.mpre): %s\n", etab[i].mpre);
fprintf(stderr, "\tPost SQL (.post): %s\n", etab[i].post);
fprintf(stderr, "\tDatabase Type (.dbt): %u\n", etab[i].dbt);
switch ( etab[i].type ) {
case 'l':
case 'L':
fprintf(stderr, "\tSource (.src): %s\n", etab[i].src);
fprintf(stderr, "\tTarget (.Ocso[0-2]): %s.%s.%s\n", etab[i].Ocso[0], etab[i].Ocso[1], etab[i].Ocso[2]);
fprintf(stderr, "\tMap File (.map): %s\n", etab[i].map);
fprintf(stderr, "\tLines to skip (.k): %u\n", etab[i].k);
fprintf(stderr, "\tParallel Streams (.ps): %u\n", etab[i].ps);
fprintf(stderr, "\tNull String (.ns): %s\n", etab[i].ns);
fprintf(stderr, "\tCommit (.cmt): %d (-1=end, 0=auto, >0 num rows)\n", etab[i].cmt);
fprintf(stderr, "\tField Truncation (.fldtr): %d\n", etab[i].cmt);
fprintf(stderr, "\tLoad Command (.loadcmd): %s\n", etab[i].loadcmd);
break;
case 'e':
fprintf(stderr, "\tSQL extract (.sql): %s\n", etab[i].sql);
fprintf(stderr, "\tSource (.src): %s\n", etab[i].src);
fprintf(stderr, "\tTarget (.tgt): %s\n", etab[i].tgt);
fprintf(stderr, "\tParallel Streams (.ps): %u\n", etab[i].ps);
fprintf(stderr, "\tPWhere Condition (.map): %s\n", etab[i].map);
fprintf(stderr, "\tSplit By (.sb): %s\n", etab[i].sb);
fprintf(stderr, "\tSplitby min/max (.sbmin/.sbmax): %ld/%ld\n", etab[i].sbmin, etab[i].sbmax);
fprintf(stderr, "\tStart/End (.sp/.ep): %u/%u\n", etab[i].sp, etab[i].ep);
fprintf(stderr, "\tNull String (.ns): %s\n", etab[i].ns);
fprintf(stderr, "\tEmpty String (.es): %s\n", etab[i].es);
fprintf(stderr, "\tGZIP Parameters (.gzp): %s\n", etab[i].gzp);
fprintf(stderr, "\tColumns (.cols): %s\n", etab[i].cols);
break;
case 'd':
case 'D':
case 'Z':
fprintf(stderr, "\tGrand Parent EID (.k): %u\n", etab[i].k);
fprintf(stderr, "\tSource (.src): %s\n", etab[i].src);
fprintf(stderr, "\tTarget (.tgt): %s\n", etab[i].tgt);
fprintf(stderr, "\tKey (.key): %s", etab[i].key[0]);
for ( j = 1 ; j < (int)etab[i].pc ; j++ )
fprintf(stderr, ",%s", etab[i].key[j]);
fprintf(stderr, "\n\tParallel Streams (.ps): %u\n", etab[i].ps);
fprintf(stderr, "\tPWhere Condition (.map): %s\n", etab[i].map);
fprintf(stderr, "\tStart/End (.sp/.ep): %u/%u\n", etab[i].sp, etab[i].ep);
fprintf(stderr, "\tSplitby min/max (.sbmin/.sbmax): %ld/%ld\n", etab[i].sbmin, etab[i].sbmax);
fprintf(stderr, "\tSplit By (.sb): %s\n", etab[i].sb);
fprintf(stderr, "\tOutput File (.map): %s\n", etab[i].map);
break;
case 'c':
case 'C':
fprintf(stderr, "\tSQL extract (.sql): %s\n", etab[i].sql);
fprintf(stderr, "\tGrand Parent EID (.k): %u\n", etab[i].k);
fprintf(stderr, "\tSource (.src): %s\n", etab[i].src);
fprintf(stderr, "\tTarget (.tgt): %s\n", etab[i].tgt);
fprintf(stderr, "\tParallel Streams (.ps): %u\n", etab[i].ps);
fprintf(stderr, "\tPWhere Condition (.map): %s\n", etab[i].map);
fprintf(stderr, "\tSplit By (.sb): %s\n", etab[i].sb);
fprintf(stderr, "\tStart/End (.sp/.ep): %u/%u\n", etab[i].sp, etab[i].ep);
fprintf(stderr, "\tSplitby min/max (.sbmin/.sbmax): %ld/%ld\n", etab[i].sbmin, etab[i].sbmax);
fprintf(stderr, "\tLoad Command (.loadcmd): %s\n", etab[i].loadcmd);
break;
case 'p':
case 'P':
fprintf(stderr, "\tSQL from source (.sql): %s\n", etab[i].sql);
fprintf(stderr, "\tSQL to target (.tgtsql): %s\n", etab[i].tgtsql);
fprintf(stderr, "\tGrand Parent EID (.k): %u\n", etab[i].k);
fprintf(stderr, "\tParallel Streams (.ps): %u\n", etab[i].ps);
fprintf(stderr, "\tSplit By (.sb): %s\n", etab[i].sb);
fprintf(stderr, "\tStart/End (.sp/.ep): %u/%u\n", etab[i].sp, etab[i].ep);
fprintf(stderr, "\tSplitby min/max (.sbmin/.sbmax): %ld/%ld\n", etab[i].sbmin, etab[i].sbmax);
fprintf(stderr, "\tLoad Command (.loadcmd): %s\n", etab[i].loadcmd);
break;
}
}
if ( f & 0400040 ) /* exit if -q all ( not documented hack ) */
exit ( EX_OK );
}
/* Allocate memory for the thread structures */
if ( !( f & 010000000 ) ) {
#ifdef _WIN32
if ( (thhn = (HANDLE *)calloc ((size_t)tn, sizeof(HANDLE))) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating thhn memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exit( EX_OSERR );
}
#else
if ( (thid = calloc ((size_t)tn, sizeof(pthread_t))) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating thid memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exit( EX_OSERR );
}
#endif
}
if ( (thps = (struct thp *)calloc ((size_t)tn, sizeof(struct thp))) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating thps memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exit( EX_OSERR );
}
/* Initialize thps structure with default/command line options */
for( i = 0; i < tn ; i++ ) {
thps[i].tid = i;
thps[i].cd = cd;
thps[i].cd2 = cd2;
for ( j = 0 ; j < clv ; j++ ) /* initialize variables */
var_set ( &thps[i].tva, VTYPE_U, av[clvn[j]], av[clvv[j]] );
}
/* Identify threads not requiring connection (.cr>0) or using 2nd user/dsn (.cr<0) */
for ( j = 0 ; j < tn ; j++ ) {
for ( i = 0 ; i < no ; i++ ) {
if ( etab[i].id == j && etab[i].type == 'l' && etab[i].ps &&
!etab[i].pre && !etab[i].post &&
etab[i+1].type == 'L' && etab[i+1].parent == i && i < no )
thps[j].cr = etab[i+1].id;
if ( etab[i].id == j && ( etab[i].type == 'C' || etab[i].type == 'D' || etab[i].type == 'P' ) ) {
thps[j].cr = -1;
if ( !Ouser2[0] )
strmcpy ( (char *)Ouser2, (char *)Ouser, sizeof(Ouser2) );
if ( !Odsn2[0] )
strmcpy ( (char *)Odsn2, (char *)Odsn, sizeof(Odsn2) );
if ( !Opwd2[0] )
strmcpy ( (char *)Opwd2, (char *)Opwd, sizeof(Opwd2) );
} else if ( etab[i].id == j && ( etab[i].type == 'Z' ) ) {
thps[j].cr = -2;
}
}
}
/* Create ODBC connections (if thps[].cr <=0). We do need:
(1) username, (2) password and (3) either DSN or Connection Attributes */
if ( ! ( f & 00040 ) ) {
ptime = (time_t)tvi.tv_sec; /* WIN localtime() requires time_t input */
nowt = localtime(&ptime);
strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt);
fprintf(stderr, "odb [%s]: starting", nows);
}
if ( ( f & 00006 ) && ( Odsn[0] || clca ) && !( f & 040000 ) ) {
if ( ! ( f & 00040 ) )
fprintf(stderr, " ODBC connection(s)...");
if ( Ouser2[0] && Opwd2[0] && Odsn2[0] )
(void) bcs ( Obuf[0], sizeof(Obuf[0]), Ouser2, Opwd2, Odsn2, NULL, 0 );
for( i = 0; i < tn ; i++ ) {
if ( thps[i].cr > 0 ) {
fprintf(stderr, " (%d)", thps[i].cr);
continue;
} else if ( thps[i].cr == ( -2 ) ) {
fprintf(stderr, " [%d]", i);
continue;
}
if ( ! ( f & 00040) )
fprintf(stderr, thps[i].cr ? " >%d" : " %d", i);
fflush(stderr);
(void) bcs ( Oics, sizeof(Oics), Ouser, Opwd, Odsn, clca, i+1 );
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_DBC, Oenv, &thps[i].Oc))){
Oerr(-1, -1, __LINE__, Oenv, SQL_HANDLE_ENV);
exit( EX_ODBCERR );
}
if ( f & 0400000000 ) { /* uc2toutf8 is set. change ODBCINI to ${ODBCINI}0 */
if (Oc) {
if (!SQL_SUCCEEDED(Oret=SQLDisconnect(Oc)))
Oerr(-1, -1, __LINE__, Oc, SQL_HANDLE_DBC);
(void)SQLFreeHandle(SQL_HANDLE_DBC, Oc); /* free conn handle */
Oc = 0;
}
if ( Setenv ( "ODBCINI", getenv("ODBCINI0") ) ) {
fprintf(stderr, "odb [main(%d)] - Error allocating setting %s: [%d] %s\n",
__LINE__, sfile, errno, strerror(errno));
exit( EX_OSERR );
}
}
if ( i == 0 && Oc ) { /* re-use etabadd Oc connection as thps[0].Oc if uc2toutf8 is NOT set */
thps[0].Oc = Oc ;
(void)SQLFreeHandle(SQL_HANDLE_DBC, Oc);
Oc = 0;
} else {
if ( thps[i].cr && Onps2 && !SQL_SUCCEEDED(Oret=SQLSetConnectAttr(thps[i].Oc,
(SQLINTEGER)SQL_ATTR_PACKET_SIZE, (SQLPOINTER)&Onps2, SQL_IS_UINTEGER ) ) )
Oerr(-1, -1, __LINE__, Oenv, SQL_HANDLE_ENV);
if ( !thps[i].cr && Onps && !SQL_SUCCEEDED(Oret=SQLSetConnectAttr(thps[i].Oc,
(SQLINTEGER)SQL_ATTR_PACKET_SIZE, (SQLPOINTER)&Onps, SQL_IS_UINTEGER ) ) )
Oerr(-1, -1, __LINE__, Oenv, SQL_HANDLE_ENV);
if ((Oret=SQLDriverConnect(thps[i].Oc, (SQLHWND) NULL,
thps[i].cr ? Obuf[0] : Oics, SQL_NTS, Oocs, (SQLSMALLINT) sizeof(Oocs),
&Oocsl, (SQLUSMALLINT)SQL_DRIVER_NOPROMPT)) != SQL_SUCCESS) {
Oerr(-1, -1, __LINE__, thps[i].Oc, SQL_HANDLE_DBC);
}
}
if ( ! SQL_SUCCEEDED(Oret) )
exit( EX_ODBCERR );
if ( ( f & 04000000 ) &&
!SQL_SUCCEEDED(Oret=SQLSetConnectAttr(thps[i].Oc,
(SQLINTEGER)SQL_ATTR_TXN_ISOLATION,
(SQLPOINTER)SQL_TXN_READ_UNCOMMITTED, 0))){
Oerr(-1, -1, __LINE__, Oenv, SQL_HANDLE_ENV);
exit( EX_ODBCERR );
}
if ( !SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT, thps[i].Oc, &thps[i].Os))){
Oerr(-1, -1, __LINE__, thps[i].Oc, SQL_HANDLE_DBC);
exit( EX_ODBCERR );
}
/* Driver not to scan SQL strings for ODBC Escapes (improve perf) */
if (!SQL_SUCCEEDED(Oret=SQLSetStmtAttr(thps[i].Os, SQL_ATTR_NOSCAN,
(SQLPOINTER)SQL_NOSCAN_ON, 0))) {
Oerr(-1, -1, __LINE__, thps[i].Os, SQL_HANDLE_STMT);
exit( EX_ODBCERR );
}
}
etab[0].flg |= 0100000;
}
if ( ! ( f & 00040 ) )
fputc('\n', stderr);
/* shuffle etab[] */
if ( f & 02000000 )
etabshuffle();
/* initialize random number generator */
srand((unsigned int)time(NULL));
if ( f & 00100) { /* [-i] print database/schema/table info */
switch ( otype ) {
case 'd': /* database info */
Odinfo();
break;
case 'c': /* catalogs info */
if ( !(f & 0100000000) ) /* if no nocatalog */
Olist((SQLCHAR *)SQL_ALL_CATALOGS, (SQLCHAR *)"", (SQLCHAR *)"", 'c');
break;
case 's': /* schema info */
Olist((SQLCHAR *)"", (SQLCHAR *)SQL_ALL_SCHEMAS, (SQLCHAR *)"", 's');
break;
case 'A': /* all object types */
case 't': /* table */
case 'v': /* view */
case 'a': /* alias */
case 'y': /* synonym */
case 'm': /* materialized view */
case 'e': /* system table */
case 'g': /* global temporary */
case 'l': /* local temporary */
case 'M': /* materialized view group */
case 'T': /* desc tables */
case 'D': /* desc tables (ddl output) */
case 'U': /* desc tables (ddl output char/varchar x4) */
etab[0].dbt = checkdb(0, &thps[0].Oc, NULL, NULL);
splitcso(rag[0], Ocso, 0);
Olist((f & 0100000000)?(SQLCHAR *)"":Ocso[0], (f & 020000000)?(SQLCHAR *)"":Ocso[1], Ocso[2], otype);
break;
default:
fprintf(stderr, "odb [main(%d)] - Invalid object type >%c<\n", __LINE__, otype);
break;
}
}
/* Interpreter: */
if ( f & 02000 ) { /* [-I] Interpreter mode */
/* Initializing variables */
csn[0] = ccn[0] = '\0';
strmcpy(prompt, Oocs[0] ? "SQL> " : "NDC> ", sizeof(prompt));
strmcpy(tprompt, "%M> ", sizeof(tprompt));
strmcpy(chsch, "set schema", sizeof(chsch)); /* default set schema cmd */
ol = (unsigned int) snprintf(odbcl, sizeof(odbcl), "%s -u %s -p %s %s%s %s%s",
odbcmd, (char *)Ouser, (char *)Opwd, Odsn[0] ? "-d " : "", Odsn[0] ? (char *)Odsn : "",
clca ? "-ca" : "", clca ? clca : "" );
if ( ol == (unsigned int)sizeof(odbcl) )
fprintf(stderr, "odb [main(%d)] - Warning odb command truncation\n", __LINE__);
ssl = 10; /* default "set schema" length */
f |= 010000000; /* assume serial run for scripts */
j = 0; /* #char in Ocmd */
k = 0; /* current arg# */
l = 1; /* l>0 new cmd; l=0 continuation */
i = 1; /* loop variable */
/* Set database type, csn and ccn */
if ( thps[0].Oc ) {
etab[0].dbt = checkdb(0, &thps[0].Oc, ccn, csn);
var_set ( &thps[0].tva, VTYPE_I, "cs", (char *)Oocs );
var_set ( &thps[0].tva, VTYPE_I, "catalog", ccn[0] ? (char *)ccn : "unknown" );
var_set ( &thps[0].tva, VTYPE_I, "schema", csn[0] ? (char *)csn : "unknown" );
}
if ( etab[0].dbt != VERTICA )
(void)signal(SIGINT, cancel); /* Keyboard Ctrl-C (interactive) */
/* Opening ODB_INI file & reading initial section */
if ( etab[0].run ) {
strmcpy(sfile, getenv("ODB_INI"), sizeof(sfile));
if ( !sfile[0] ) { /* ODB_INI is empty */
#ifdef _WIN32
strmcpy(sfile, getenv("HOMEDRIVE"), sizeof(sfile));
strmcat(sfile, getenv("HOMEPATH"), sizeof(sfile), 0);
strmcat(sfile, "\\_odbrc", sizeof(sfile), 0);
#else
strmcpy(sfile, getenv("HOME"), sizeof(sfile));
strmcat(sfile, "/.odbrc", sizeof(sfile), 0);
#endif
}
if ((fp=fopen(sfile, "r"))==(FILE *)NULL) {
fprintf(stderr,"odb [main(%d)] - Warning. Cannot open ODB_INI file (%s): [%d] %s\n",
__LINE__, sfile, errno, strerror(errno) );
} else {
if ( ( line = (char *)malloc ( LINE_CHUNK ) ) == (void *) NULL ) {
fprintf(stderr, "odb [main(%d)] - Error Allocating LINE_CHUNK memory\n", __LINE__);
exit( EX_OSERR );
}
l = (int)strlen(etab[0].run);
while ( fgets(line, LINE_CHUNK, fp ) ) { /* skip to av[i] section */
if ( line[0] == '[' && !strncmp(&line[1], etab[0].run, l) && line[l+1] == ']' ) {
i = 0;
break;
}
}
if ( i )
fprintf(stderr, "odb [main(%d)] - Warning: Section [%s] not found in %s\n", __LINE__, etab[0].run, sfile);
}
}
/* allocating intial memory for Ocmd */
if ( (Ocmd = (SQLCHAR *)malloc ( bs ) ) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error allocating Ocmd memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
free(line);
exit( EX_OSERR );
}
/* Setting history file */
strmcpy(hfile, getenv("ODB_HIST"), sizeof(hfile));
if ( !hfile[0] ) { /* ODB_HIST is empty */
#ifdef _WIN32
strmcpy(hfile, getenv("HOMEDRIVE"), sizeof(hfile));
strmcat(hfile, getenv("HOMEPATH"), sizeof(hfile), 0);
strmcat(hfile, "\\_odbhist", sizeof(hfile), 0);
#else
strmcpy(hfile, getenv("HOME"), sizeof(hfile));
strmcat(hfile, "/.odbhist", sizeof(hfile), 0);
#endif
}
/* mreadline initialization */
if ( mrinit() ) { /* Initialize mreadline */
fprintf(stderr, "odb [main(%d)] - Error during mreadline initialization\n", __LINE__);
goto oint_exit;
}
if ( mrhinit(hist, hfile) ) { /* Initialize mrhistory */
fprintf(stderr, "odb [main(%d)] - Error during mrhistory initialization\n", __LINE__);
goto oint_exit;
}
/* Clean sfile & Ocmd */
sfile[0] = Ocmd[0] = '\0';
/* Interpreter loop */
while( (fp) || (line=mreadline(prompt, &ll)) != (char *)EOF ){
if ( fp ) { /* line read from the ODB_INI file */
if ((!fgets(line, LINE_CHUNK, fp)) || (line[0]=='[')) {
fclose(fp);
free(line);
fp = 0;
continue;
} else if ( line[0] == '#' ) {
continue; /* skip comments */
} else {
ll = (unsigned int) strlen ( line ) - 1;
line[ll] = '\0'; /* remove ending new line */
if (line[0])
printf("%s\n", line);
}
etab[0].flg &= ~0200000; /* don't save history */
} else {
if ( etab[0].fso )
fprintf(etab[0].fso, "%s%s\n", strmprint(prompt), line);
etab[0].flg |= 0200000; /* save history */
}
while ( ( j + ll + 1 ) > bs ) { /* we need more memory for Ocmd */
bs += CMD_CHUNK;
O = Ocmd ;
if ( ( O = (SQLCHAR *)realloc ( O, bs ) ) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
j = 0;
break;
}
Ocmd = O;
}
if ( line == (char *) (-2) ) { /* CTRL-X */
etab[0].flg &= ~0200000; /* don't save history */
nrag = j = 0;
rl = 0 ;
} else {
j += strmcat ( (char *)Ocmd, line, bs, 0);
nrag = tokenize ( line, (int)ll, rag );
rl = strlen ( rag[0] );
ll = (unsigned int) j; /* save current command length */
}
/* alias substitution */
if ( l && nrag ) { /* don't check aliases on continuation lines */
for ( vv = thps[0].tva; vv ; vv = vv->next ) {
if ( vv->type == VTYPE_A && /* Is an ALIAS */
vv->nlen == rl && /* Alias name length = first command line word */
!strmicmp(rag[0], vv->name, rl)) { /* Alias name == first command line word */
mrhadd((char *)Ocmd, ll); /* save command to history now... */
etab[0].flg &= ~0200000; /* ... and not after alias expansion */
memset(Ocmd, 0, bs);
for ( j = k = 0; vv->value[k]; k++ ) { /* build new Ocmd */
if ( vv->value[k] == '&' && vv->value[k+1] && vv->value[k+1] > 48 && vv->value[k+1] < 58 ) {
j += (int)strlen(rag[vv->value[++k]-48]);
(void) strmcat((char *)Ocmd, rag[vv->value[k]-48], bs, 0);
} else {
Ocmd[j++] = (SQLCHAR)vv->value[k];
}
}
if ( ( line = mresize ( (size_t)(j + 1) ) ) == (char *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error re-allocating memory for line: [%d] %s\n",
__LINE__, errno, strerror(errno));
} else {
ll = (unsigned int)j;
MEMCPY ( line, Ocmd , j + 1); /* Copy Ocmd and the terminating NULL */
nrag = tokenize ( line, (int)ll, rag );
}
break; /* we've found our alias */
}
}
}
/* Commands for odb... */
if ( nrag && l ) {
if ( nrag == 1 && ( !strmicmp(rag[0], "h", 0) || !strmicmp(rag[0], "help", 0) ) ) {
printf ("All the following are case insensitive:\n"
" h | help : print this help\n"
" i | info : print database info\n"
" q | quit : exit SQL Interpreter\n"
" c | connect { no | [user[/pswd][;opts;...] (re/dis)connect using previous or new user\n"
" odb odb_command : will run an odb instance using the same DSN/credentials\n"
" ls -[type] [pattern] : list objects. Type=(t)ables, (v)iews, s(y)nonyns, (s)chemas\n"
" : (c)atalogs, syst(e)m tables, (l)ocal temp, (g)lobal temp\n"
" : (m)at views, (M)mat view groups, (a)lias, (A)ll object types\n"
" : (D)table DDL, (T)table desc\n"
" print <string> : print <string>\n"
" !cmd : execute the operating system cmd\n"
" @file [&0]... [&9] : execute the sql script in file\n"
" set : show all settings\n"
" set alias [name] [cmd|-]: show/set/change/delete aliases\n"
" set chsch [cmd] : show/set change schema command\n"
" set cols [#cols] : show/set ls number of columns\n"
" set cwd [<directory>] : show/set current working directory\n"
" set drs [on|off|\"SQL\"] : show/enable/disable/run SQL describe result set mode\n"
" set fs [<char>] : show/set file field separator\n"
" set hist [#lines] : show/set lines saved in the history file\n"
" set maxfetch [#rows] : show/set max lines to be fetched (-1 = unlimited)\n"
" set nocatalog [on|off] : show/enable/disable \"no catalog\" database mode)\n"
" set nocatnull [on|off] : show/enable/disable \"no catalog as null\" database mode)\n"
" set noschema [on|off] : show/enable/disable \"no schema\" database mode)\n"
" set nullstr [<string>|-]: show/set/delete nullstring\n"
" set pad [fit|full|off] : show/set column padding\n"
" set param name [value|-]: show/set/change/delete a parameter\n"
" set pcn [on|off] : show/enable/disable printing column names\n"
" set plm [on|off|\"SQL\"] : show/enable/disable/run SQL print list mode (one col/row)\n"
" set prepare [on|off] : show/enable/disable 'prepare only' mode\n"
" set prompt [string] : show/set prompt string\n"
" set query_timeout [s] : show/set query timeout in seconds (def = 0 no timeout)\n"
" set quiet [cmd|res|timing|all|off] : show/enable/disable quiet mode\n"
" set rowset [#] : show/set rowset used to fetch rows\n"
" set soe [on|off] : show/enable/disable Stop On Error mode\n"
" set spool [<file>|off] : show/enable/disable spooling output on <file>\n"
" set ucs2toutf8 [<file>|off] : show/enable/disable UCS-2 to UTF-8 conversion\n"
" <SQL statement>; : everything ending with ';' is sent to the database\n");
#ifndef _WIN32
printf("\nmreadline keys:\n"
" Control-A : move to beginning of line Control-P : history Previous\n"
" Control-E : move to end of line Up Arrow : history Previous\n"
" Control-B : move cursor Back Control-N : history Next\n"
" Left Arrow : move cursor Back Down Arrow : history Next\n"
" Control-F : move cursor Forward Control-W ; history List\n"
" Right Arrow: move cursor Forward Control-R : Redraw\n"
" Control-D : input end (exit) - DEL right Control-V : Edit current line\n"
" Control-L : Lowercase Line Control-X : Kill line\n"
" Control-U : Uppercase Line #Control-G : load history entry #\n");
#endif
j = 0;
} else if ( !strmicmp((char *)Ocmd, chsch, ssl) ) {
if ( rag[nrag-1][0] == ';' )
k = nrag - 2;
else
k = nrag - 1;
i = (int) strlen(rag[k]);
if ( rag[k][i - 1] == ';' || rag[k][i-1] == ',')
rag[k][--i] = '\0';
if ( f & 020000000 ) {
strmcpy(ccn, rag[k], sizeof(ccn));
} else {
for ( ; i && rag[k][i] != '.' ; i--);
strmcpy(csn, &rag[k][i+(i?1:0)], sizeof(csn));
if ( i )
strmcpy(ccn, rag[k], i);
}
var_set ( &thps[0].tva, VTYPE_I, "catalog", ccn[0] ? (char *)ccn : "none" );
var_set ( &thps[0].tva, VTYPE_I, "schema", csn[0] ? (char *)csn : "none" );
} else if ( !strmicmp(rag[0], "ls", 0) ) {
if ( etab[0].flg & 0100000 ) {
otype = 'A';
if ( nrag > 1 ) {
if ( rag[1][0] == '-' )
otype = rag[1][1];
if ( rag[nrag-1][0] == '.' && rag[nrag-1][1] == '\0' )
Ocso[1] = (SQLCHAR *)csn;
else if ( rag[nrag-1][0] != '-' )
splitcso(rag[nrag-1], Ocso, 1);
}
if ( otype == 's' ) { /* list schemas */
Olist((SQLCHAR *)"", (SQLCHAR *)SQL_ALL_SCHEMAS, (SQLCHAR *)"", 's');
} else if ( otype == 'c' ) { /* list catalogs */
Olist((SQLCHAR *)SQL_ALL_CATALOGS, (SQLCHAR *)"", (SQLCHAR *)"", 'c');
} else { /* all other objects */
Olist(( f & 0100000000) ? (SQLCHAR *)"":(Ocso[0]?Ocso[0]:(SQLCHAR *)ccn),
( f & 020000000) ? (SQLCHAR *)"":(Ocso[1]?Ocso[1]:(SQLCHAR *)csn), Ocso[2], otype);
}
} else {
fprintf(stderr, "odb [main(%d)] - No Database Connection\n", __LINE__);
}
Ocso[0] = Ocso[1] = Ocso[2] = 0;
j = 0;
} else if ( !strmicmp(rag[0], "odb", 0) ) {
mrhadd((char *)Ocmd, ll); /* save command to history now... */
etab[0].flg &= ~0200000; /* ... and not after alias expansion */
while ( ol + ll > bs ) {
bs += CMD_CHUNK;
if ( ( Ocmd = (SQLCHAR *)realloc ( Ocmd, bs ) ) == (void *)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
}
}
strmcpy((char *)Ocmd, odbcl, bs);
for ( i = 1 ; i < nrag ; i++ ) {
strmcat((char *)Ocmd, " ", bs, 0);
strmcat((char *)Ocmd, rag[i], bs, 0);
}
#ifdef _WIN32
_spawnlp(_P_WAIT, "cmd.exe", "cmd.exe", "/c", Ocmd, NULL);
#else
if ( system((const char *)Ocmd) < 0 )
fprintf(stderr, "odb [main(%d)] - Error running %s\n", __LINE__, &Ocmd[1]);
#endif
j = 0;
} else if ( !strmicmp(rag[0], "set", 0) ) {
setan ( 0, 0, nrag, rag, 0 );
j = 0;
} else if ( !strmicmp(rag[0], "i", 0) || !strmicmp(rag[0], "info", 0) ) {
if ( etab[0].flg & 0100000 ) {
Odinfo();
mfprintf(stdout, etab[0].fso, "\t%-45s: %s\n", "Current Catalog", ccn);
mfprintf(stdout, etab[0].fso, "\t%-45s: %s\n", "Current Schema", csn);
} else {
fprintf(stderr, "odb [main(%d)] - No Database Connection\n", __LINE__);
}
j = 0;
} else if ( !strmicmp(rag[0], "c", 0) || !strmicmp(rag[0], "connect", 0) ) {
if ( nrag == 2 && !strmicmp(rag[1], "NO", 2) ) {
if (!SQL_SUCCEEDED(Oret=SQLDisconnect(thps[0].Oc)))
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
etab[0].flg &= ~0100000;
thps[0].Os = 0;
thps[0].Oc = 0;
} else {
if ( nrag > 1 ) {
if ( rag[1][0] == ';' ) {
clca = &rag[1][1];
} else {
for ( i = 0, o = 0; rag[1][i] && rag[1][i] != '/' ; i++, o++ )
Ouser[o] = (SQLCHAR) rag[1][i];
Ouser[i] = '\0';
for ( i++, o = 0; rag[1][i] && rag[1][i] != ';' ; i++, o++ )
Opwd[o] = (SQLCHAR) rag[1][i];
Opwd[i] = '\0';
if ( rag[1][i] == ';' )
clca = &rag[1][i+1];
}
(void) bcs ( Oics, sizeof(Oics), Ouser, Opwd, Odsn, clca, 0 );
}
if ( !thps[0].Oc )
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_DBC, Oenv, &thps[0].Oc)))
Oerr(0, 0, __LINE__, Oenv, SQL_HANDLE_ENV);
if (!SQL_SUCCEEDED(Oret=SQLDriverConnect(thps[0].Oc, (SQLHWND) NULL,
Oics, SQL_NTS, Oocs, (SQLSMALLINT) sizeof(Oocs),
&Oocsl, (SQLUSMALLINT)SQL_DRIVER_NOPROMPT))) {
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
} else {
etab[0].flg |= 0100000;
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT,
thps[0].Oc, &thps[0].Os))){
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
}
var_set ( &thps[0].tva, VTYPE_I, "cs", (char *)Oocs );
}
etab[0].dbt = checkdb(0, &thps[0].Oc, ccn, csn);
if ( ( vv = var_idx ( &thps[0].tva, VTYPE_I, "schema" ) ) ) {
strmcpy(csn, vv->value, sizeof(csn));
vv = var_idx ( &thps[0].tva, VTYPE_I, "catalog" );
if ( vv && strmicmp(ccn, vv->value, 0) ) {
strmcpy(ccn, vv->value, sizeof(ccn));
snprintf((char *)Obuf[0], sizeof(Obuf[0]), "%s %s.%s", chsch, ccn, csn);
} else {
snprintf((char *)Obuf[0], sizeof(Obuf[0]), "%s %s", chsch, csn);
}
printf("Resetting schema: \"%s\"\n", (char *)Obuf[0]);
Oexec( 0, 0, -1, 0, Obuf[0], "");
}
var_set ( &thps[0].tva, VTYPE_I, "catalog", ccn[0] ? (char *)ccn : "unknown" );
var_set ( &thps[0].tva, VTYPE_I, "schema", ccn[0] ? (char *)csn : "unknown" );
}
j = 0;
} else if ( !strmicmp(rag[0], "q", 0) || !strmicmp(rag[0], "quit", 0) ||
!strmicmp(rag[0], "exit", 0) ) {
break;
} else if ( !strmicmp(rag[0], "print", 0 ) ) {
switch ( nrag ) {
case 1:
fprintf(stderr, "odb [main(%d)] - Not enough arguments (print what?)\n", __LINE__);
break;
default:
var_exp (rag[1], 0, &thps[0].tva);
}
j = 0;
} else if ( rag[0][0] == '@' ) {
if ( etab[0].flg & 0100000 ) {
i = pad; /* save pad setting */
pad = 0;
(void)runsql(0, 0, -1, &rag[0][1]);
pad = (char)i; /* reset pad setting */
} else {
fprintf(stderr, "odb [main(%d)] - No Database Connection\n", __LINE__);
}
j = 0;
} else if ( rag[0][0] == '!' ) {
#ifdef _WIN32
_spawnlp(_P_WAIT, "cmd.exe", "cmd.exe", "/c", &Ocmd[1], NULL);
#else
if ( system((const char *)&Ocmd[1]) < 0 )
fprintf(stderr, "odb [main(%d)] - Error running %s\n", __LINE__, &Ocmd[1]);
#endif
j = 0;
}
}
/* Commands ending with ';' are sent to the database */
if ( j ) {
for ( i = j - 1; i && isspace(Ocmd[i]) ; i--); /* skip trailing blanks */
if ( Ocmd[i] == ';' ) {
Ocmd = (SQLCHAR *)var_exp ( (char *)Ocmd, &bs, &thps[0].tva);
if ( !(etab[0].flg & 0100000) ) /* No Connection */
fprintf(stderr, "odb [main(%d)] - No Database Connection\n", __LINE__);
else
Omexec( 0, 0, -1, 0, Ocmd, "", ccn, csn);
j = 0;
} else {
(void) strmcat((char *)Ocmd, "\n", bs, 0);
j++;
}
}
if ( !j ) {
l = 1;
if ( etab[0].flg & 0200000 )
mrhadd((char *)Ocmd, ll);
Ocmd[0] = '\0';
}
/* Setting prompt string */
memset(prompt, 0, sizeof(prompt));
c = tprompt;
i = 0;
while ( (ch = *c++) ) {
if ( ch == '%' ) {
switch ( ( ch = *c++ ) ) {
case 'D':
i += strmcat(prompt, (char *)Odsn, sizeof(prompt), 0);
break;
case 'U':
i += strmcat(prompt, (char *)Ouser, sizeof(prompt), 0);
break;
case 'S':
i += strmcat(prompt, csn, sizeof(prompt), 0);
break;
case 'C':
i += strmcat(prompt, ccn, sizeof(prompt), 0);
break;
case 'M':
i += 3;
if ( j ) { /* command continuing on the next line */
l = 0;
(void) strmcat(prompt, "...", sizeof(prompt), 0);
} else if ( !(etab[0].flg & 0100000) ) {
(void) strmcat(prompt, "NDC", sizeof(prompt), 0);
} else if ( etab[0].fso ) {
(void) strmcat(prompt, "SPO", sizeof(prompt), 0);
} else if ( etab[0].flg & 0010 ) {
(void) strmcat(prompt, "PRE", sizeof(prompt), 0);
} else if ( f & 0400000 ) {
(void) strmcat(prompt, "QUI", sizeof(prompt), 0);
} else {
(void) strmcat(prompt, "SQL", sizeof(prompt), 0);
}
break;
case 'T':
if ( time(&ptime) < 0 ) {
strmcpy(nows, "Time N.A.", sizeof(nows));
} else {
nowt = localtime(&ptime);
if (!strftime(nows, sizeof(nows), "%H:%M:%S", nowt))
strmcpy(nows, "Time N.A.", sizeof(nows));
}
i += strmcat(prompt, nows, sizeof(prompt), 0);
break;
}
} else {
prompt[i++] = (char)ch;
}
}
}
mrhsave(hfile); /* save history file */
oint_exit:
free(Ocmd); /* Free Ocmd */
if (etab[0].fso) /* spool not closed */
fclose(etab[0].fso);
mrend(); /* End mreadline */
printf("Bye\n");
}
if ( f & 00200 ) { /* [-x / -f / -P / -S / -l / -e ] */
if ( f & 00400 ) { /* csv output */
gettimeofday(&tvs, (void *)NULL); /* register start time */
ptime = (time_t) tvs.tv_sec; /* WIN localtime requires time_t */
if ( f & 01000000 ) {
nowt = localtime(&ptime);
if (strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt))
printf("Thread id [%s]%c", nows, fs);
else
printf("Thread id [start time N.A.]%c", fs);
} else {
nowt = localtime(&ptime);
strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt);
fprintf(stderr, "odb [%s]: starting (%d) threads...\n", nows, tn);
printf("Thread id%c", fs);
}
printf("Proc id%cThread Exec#%cScript Cmd#%c", fs, fs, fs);
printf("File%cLabel%cCommand%cRows%cRsds%cPrepare(s)%c", fs, fs, fs, fs, fs, fs);
printf("Exec(s)%c1st Fetch(s)%cFetch(s)%cTotal(s)%cSTimeline%cETimeline\n",
fs, fs, fs, fs, fs);
fflush(stdout);
}
if ( tn == 1 ) { /* just one command OR serial execution: no threads */
(void)signal(SIGINT, sigcatch); /* Keyboard Ctrl-C */
(void)signal(SIGTERM, sigcatch); /* Software termination (kill) */
#ifndef _WIN32
(void)signal(SIGALRM, sigcatch); /* timeout alarm */
if ( w )
alarm ( w );
#endif
f |= 010000000;
(void)Oruncmd((void *)&etab[0].id);
} else {
#ifdef _WIN32
for ( i = 0; i < tn; i++ ){
if ( i && d )
Sleep (d);
if( ( thhn[i] = CreateThread(NULL, 0, Oruncmd, (void *)&thps[i].tid,
0, NULL)) == (HANDLE)NULL ) {
fprintf(stderr, "odb [main(%d)] - Error starting cmd thread %d: [%d] %s\n",
__LINE__, i, errno, strerror(errno));
exit( EX_OSERR );
}
}
(void)signal(SIGINT, sigcatch); /* Keyboard Ctrl-C */
(void)signal(SIGTERM, sigcatch); /* Software termination (kill) */
WaitForMultipleObjects(i, thhn, TRUE, INFINITE); /* wait threads */
for ( i = 0 ; i < tn ; i++) /* close thread handles */
CloseHandle(thhn[i]);
#else
for ( i = 0 ; i < tn ; i++ ) {
if ( i && d )
Sleep (d);
if( ( k = pthread_create(&thid[i], NULL,
Oruncmd, (void *) &thps[i].tid) ) ) {
fprintf(stderr, "odb [main(%d)] - Error starting thread %d: [%d] %s\n",
__LINE__, i, errno, strerror(errno));
exit( EX_OSERR );
}
}
(void)signal(SIGALRM, sigcatch); /* timeout alarm */
(void)signal(SIGINT, sigcatch); /* Keyboard Ctrl-C */
(void)signal(SIGTERM, sigcatch); /* Software termination (kill) */
if ( w )
alarm ( w );
for ( i = 0 ; i < tn ; i++) { /* wait threads */
if ( ( k = pthread_join(thid[i], (void *)&thres) ) ) {
fprintf(stderr, "odb [main(%d)] - Error joining thread %d: [%d] %s\n",
__LINE__, i, errno, strerror(errno));
exit( EX_OSERR );
}
}
#endif
}
if ( !(f & 01000000) && ( f & 00400 ) ) { /* -c (csv output) and not -b */
gettimeofday(&tvn, (void *)NULL); /* register end time */
printf("%s statistics:\n", odbid);
ptime = (time_t)tvi.tv_sec; /* WIN localtime() requires time_t input */
nowt = localtime(&ptime);
if (strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt))
printf("\tInit timestamp: %s\n", nows);
else
printf("\tInit timestamp N.A.\n");
ptime = (time_t)tvs.tv_sec; /* WIN localtime() requires time_t input */
nowt = localtime(&ptime);
if (strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt))
printf("\tStart timestamp: %s\n", nows);
else
printf("\tStart timestamp N.A.\n");
ptime = (time_t)tvn.tv_sec; /* WIN localtime() requires time_t input */
nowt = localtime(&ptime);
if (strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt))
printf("\tEnd timestamp: %s\n", nows);
else
printf("\tEnd timestamp N.A.\n");
printf("\tElapsed [Start->End] (s): %.3f\n",
(double)(tvn.tv_sec-tvs.tv_sec)+(double)(tvn.tv_usec-tvs.tv_usec)/1000000.0);
fflush(stdout);
}
}
if ( thps[0].Oc && !thps[0].cr ) {
if (!SQL_SUCCEEDED(Oret=SQLDisconnect(thps[0].Oc)))
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
}
gettimeofday(&tvn, (void *)NULL); /* register end time */
ptime = (time_t)tvn.tv_sec; /* WIN localtime() requires time_t input */
nowt = localtime(&ptime);
strftime(nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", nowt);
seconds = (double)(tvn.tv_sec-tvi.tv_sec+(tvn.tv_usec-tvi.tv_usec)/1000000.0);
fprintf(stderr, "odb [%s]: exiting. Session Elapsed time %s seconds (%s)\n", nows,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
gclean();
exit( exstat );
}
/* setan: change/print settings
*
* eid(I): etab[] index
* tid(I): thps[] index
* nrag(I): number of arguments
* rag(I): arguments array pointer
* ql(I): qlabel pointer
*
* return: void
*/
static void setan ( int eid, int tid, int nrag, char *rag[], char *ql )
{
int i = 0; /* loop variable */
unsigned int ul = 0; /* used to park new nullstr length */
if ( nrag == 1 || !strmicmp(rag[1], "alias", 0) ) {
switch ( nrag ) {
case 1:
case 2:
for ( vv = thps[tid].tva; vv ; vv = vv->next )
if ( vv->type == VTYPE_A )
fprintf(stderr, "set alias \"%s\" is \"%s\"\n", vv->name, vv->value);
break;
case 3:
vv = var_idx ( &thps[tid].tva, VTYPE_A, rag[2] );
fprintf(stderr, "set alias \"%s\" is \"%s\"\n", rag[2], vv ? vv->value : "not set");
break;
case 4:
if ( rag[3][0] == '-' )
var_del ( &thps[tid].tva, VTYPE_A, rag[2] );
else
var_set ( &thps[tid].tva, VTYPE_A, rag[2], rag[3] );
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "chsch", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set chsch (change schema cmd) is %s\n", chsch);
break;
case 3:
strmcpy(chsch, rag[2], sizeof(chsch));
ssl = (int) strlen(chsch);
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "cwd", 0) ) {
switch ( nrag ) {
case 1:
case 2:
if ( !getcwd(cwd, sizeof(cwd)) )
fprintf(stderr, "odb [main(%d)] - Cannot get cwd: [%d] %s\n",
__LINE__, errno, strerror(errno));
else
fprintf(stderr, "set cwd is %s\n", cwd );
break;
case 3:
if ( ( i = chdir(rag[2]) ) == -1 )
fprintf(stderr, "odb [main(%d)] - Cannot chdir: [%d] %s\n",
__LINE__, errno, strerror(errno));
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "drs", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set drs (describe result set) is %s\n", ( etab[eid].flg & 0400000 ) ? "on" : "off" );
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) ) {
etab[eid].flg |= 0400000;
} else if ( !strmicmp(rag[2], "off", 0) ) {
etab[eid].flg &= ~0400000;
} else {
etab[eid].flg |= 0400000;
(void)Oexec( tid, eid, -1, 0, (SQLCHAR *)rag[2], "");
etab[eid].flg &= ~0400000;
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "fs", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set fs (field separator) is \"%c\"\n", etab[eid].fs);
break;
case 3:
etab[eid].fs = rag[2][0];
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "maxfetch", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set maxfetch (max rows fetched) is %lu%s\n",
etab[eid].mr, etab[eid].mr ? "" : " (unlimited)");
break;
case 3:
etab[eid].mr = atoi(rag[2]);
if ( etab[eid].r > (size_t) etab[eid].mr )
etab[eid].r = (size_t) etab[eid].mr;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "nocatalog", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set nocatalog (\"no catalog\" mode) is %s\n",
( f & 0100000000 ) ? "on" : "off");
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
f |= 0100000000;
else if ( !strmicmp(rag[2], "off", 0) )
f &= ~0100000000;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "nocatnull", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set nocatnull (\"no catalog as null\" mode) is %s\n",
( f & 04100000000 ) ? "on" : "off");
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
f |= 04100000000;
else if ( !strmicmp(rag[2], "off", 0) )
f &= ~04100000000;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "noschema", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set noschema (\"no schema\" mode) is %s\n",
( f & 020000000 ) ? "on" : "off");
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
f |= 020000000;
else if ( !strmicmp(rag[2], "off", 0) )
f &= ~020000000;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "ucs2toutf8", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set ucs2toutf8 is %s\n",
( f & 010000000 ) ? "on" : "off");
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
f |= 010000000;
else if ( !strmicmp(rag[2], "off", 0) )
f &= ~010000000;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "plm", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set plm (print one column/row) is %s\n", ( etab[eid].flg & 040000 ) ? "on" : "off" );
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) ) {
etab[eid].flg |= 040000;
} else if ( !strmicmp(rag[2], "off", 0) ) {
etab[eid].flg &= ~040000;
} else { /*run cmd in plm mode */
etab[eid].flg |= 040000;
(void)Oexec( tid, eid, -1, 0, (SQLCHAR *)rag[2], "");
etab[eid].flg &= ~040000;
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "param", 3) ) {
switch ( nrag ) {
case 1:
case 2:
for ( vv = thps[tid].tva; vv ; vv = vv->next ) {
if ( vv->type == VTYPE_U )
fprintf(stderr, "set (user) param \"%s\" is \"%s\"\n", vv->name, vv->value);
else if ( vv->type == VTYPE_I )
fprintf(stderr, "set (internal) param \"%s\" is \"%s\"\n", vv->name, vv->value);
}
break;
case 3:
vv = var_idx ( &thps[tid].tva, VTYPE_U, rag[2] );
fprintf(stderr, "set (user) param \"%s\" is \"%s\"\n", rag[2], vv ? vv->value : "not set");
if ( ( vv = var_idx ( &thps[tid].tva, VTYPE_I, rag[2] ) ) )
fprintf(stderr, "set (internal) param \"%s\" is \"%s\"\n",
rag[2], vv ? vv->value : "not set");
break;
case 4:
if ( rag[3][0] == '-' )
var_del ( &thps[tid].tva, VTYPE_U, rag[2] );
else
var_set ( &thps[tid].tva, VTYPE_U, rag[2], rag[3] );
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "pcn", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set pcn (print column names) is %s\n", etab[eid].flg & 0200 ? "on" : "off" );
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) ) {
etab[eid].flg |= 0200;
} else if ( !strmicmp(rag[2], "off", 0) ) {
etab[eid].flg &= ~0200;
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "prepare", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set prepare is %s\n", etab[eid].flg & 0010 ? "on" : "off" );
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
etab[eid].flg |= 0010;
else if ( !strmicmp(rag[2], "off", 0) )
etab[eid].flg &= ~0010;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "query_timeout", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set query_timeout is \"%ld\"\n", (long)Oqt);
break;
case 3:
Oqt = (SQLULEN) atol(rag[2]);
if (!SQL_SUCCEEDED(Oret=SQLSetStmtAttr(thps[tid].Os, SQL_ATTR_QUERY_TIMEOUT,
&Oqt, SQL_IS_UINTEGER))) {
Oerr(0, 0, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "quiet", 0) ) {
switch ( nrag ) {
case 1:
case 2:
if ( etab[eid].flg & 030000 && etab[eid].flg2 & 0004 ) {
fprintf(stderr, "set quiet is all\n");
} else if ( !(etab[eid].flg & 030000) && !(etab[eid].flg2 & 0004) ) {
fprintf(stderr, "set quiet is off\n");
} else {
if ( etab[eid].flg & 010000 )
fprintf(stderr, "set quiet is cmd\n");
if ( etab[eid].flg & 020000 )
fprintf(stderr, "set quiet is res\n");
if ( etab[eid].flg2 & 0004 )
fprintf(stderr, "set quiet is timing\n");
}
break;
case 3:
if ( !strmicmp(rag[2], "off", 0) ) {
etab[eid].flg &= ~030000; /* switch all off */
etab[eid].flg2 &= ~0004; /* switch all off */
} else if ( !strmicmp(rag[2], "cmd", 0) ) {
etab[eid].flg |= 010000; /* switch cmd on */
} else if ( !strmicmp(rag[2], "res", 0) ) {
etab[eid].flg |= 020000; /* switch res on */
} else if ( !strmicmp(rag[2], "timing", 0) ) {
etab[eid].flg2 |= 0004; /* switch timing on */
} else if ( !strmicmp(rag[2], "all", 0) ) {
etab[eid].flg |= 030000; /* switch all on */
etab[eid].flg2 |= 0004; /* switch all on */
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "rowset", 0) ) {
switch ( nrag ) {
case 1:
case 2:
if ( etab[eid].r )
fprintf(stderr, "set rowset (insert/fetch rowset) is %ld\n",(long int) etab[eid].r);
else
fprintf(stderr, "set rowset (insert/fetch buffer size) is %ld\n",(long int) etab[eid].rbs);
break;
case 3:
switch ( rag[2][0] ) {
case 'k':
case 'K':
etab[eid].r = 0 ;
etab[eid].rbs = 1024 * atoi (&rag[2][1]);
break;
case 'm':
case 'M':
etab[eid].r = 0 ;
etab[eid].rbs = 1024 * 1024 * atoi (&rag[2][1]);
break;
default:
etab[eid].r = (size_t) atoi(rag[2]);
etab[eid].rbs = 0;
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "soe", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set soe (Stop On Error) is %s\n", etab[eid].flg & 0004 ? "on" : "off" );
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
etab[eid].flg |= 0004 ;
else if ( !strmicmp(rag[2], "off", 0) )
etab[eid].flg &= ~0004 ;
break;
}
}
if ( nrag == 1|| !strmicmp(rag[1], "spool", 0) ) {
switch ( nrag ) {
case 1:
case 2:
if ( etab[eid].fso )
fprintf(stderr, "set spool is on %s\n", sfile );
else
fprintf(stderr, "set spool is off\n");
break;
case 3:
if ( !strmicmp(rag[2], "off", 0) ) {
fclose(etab[eid].fso);
etab[eid].fso = 0;
} else {
strmcpy(sfile, rag[2], sizeof(sfile));
if ( ( etab[eid].fso = fopen(sfile, "a") ) == (FILE *)NULL )
fprintf(stderr, "odb [main(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, sfile, errno, strerror(errno));
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "nullstr", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set nullstr is \'%s\'\n", etab[eid].ns ? etab[eid].ns : "not set");
break;
case 3:
ul = etab[eid].nl; /* save current nullstring length */
etab[eid].nl = (unsigned int)strlen(rag[2]);
if ( etab[eid].nl == 1 && rag[2][0] == '-' ) { /* clean nullstr */
etab[eid].nl = 0;
free ( etab[eid].ns );
etab[eid].ns = '\0';
} else {
if ( ul < etab[eid].nl ) { /* we need more memory for our nullstring */
if ( ( etab[eid].ns = realloc ( etab[eid].ns, etab[eid].nl + 1 ) ) == (void *)NULL ) {
fprintf(stderr, "[%d] odb [setan(%d)] - Error re-allocating nullstr memory: [%d] %s\n",
tid, __LINE__, errno, strerror(errno));
return;
}
}
strmcpy(etab[eid].ns, rag[2], (size_t)etab[eid].nl);
}
break;
}
}
if ( f & 02000 ) { /* [-I] Interpreter only flags */
if ( nrag == 1 || !strmicmp(rag[1], "casesens", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set casesens (Case Sensitive DB) is %s\n", f & 010000000000 ? "on" : "off" );
break;
case 3:
if ( !strmicmp(rag[2], "on", 0) )
f |= 010000000000;
else if ( !strmicmp(rag[2], "off", 0) )
f &= ~010000000000;
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "cols", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set cols is %d\n", nc);
break;
case 3:
nc = atoi(rag[2]);
break;
}
cs = (int)(mrcol / nc);
}
if ( nrag == 1 || !strmicmp(rag[1], "hist", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set hist (lines saved in the history file) is %d\n", hist);
break;
case 3:
hist = atoi(rag[2]);
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "pad", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set pad (pad columns to display size) is %s\n",
pad == 0 ? "off" : ( pad == 1 ? "full" : "fit" ) );
break;
case 3:
case 4:
if ( !strmicmp(rag[2], "fit", 0) ) {
pad = 2;
} else if ( !strmicmp(rag[2], "full", 0) ) {
pad = 1;
} else if ( !strmicmp(rag[2], "off", 0) ) {
ul = pad ; /* save old pad setting */
pad = 0;
if ( nrag == 4 ) {
(void)Oexec( tid, eid, -1, 0, (SQLCHAR *)rag[3], "");
pad = ul ; /* reset pad */
}
}
break;
}
}
if ( nrag == 1 || !strmicmp(rag[1], "prompt", 0) ) {
switch ( nrag ) {
case 1:
case 2:
fprintf(stderr, "set prompt is \"%s\"\n", tprompt);
break;
case 3:
strmcpy(tprompt, rag[2], sizeof(tprompt));
break;
}
}
} else { /* script only flags */
if ( !strmicmp(rag[1], "ttime", 0) ) {
thps[tid].cd = (unsigned int)strtol(rag[2], (char **)NULL, 10);
for ( i = 0; rag[2][i] && rag[2][i] != ':'; i++);
thps[tid].cd2 = (unsigned int)strtol(&rag[2][i+1], (char **)NULL, 10);
} else if ( !strmicmp(rag[1], "qlabel", 0) ) {
strmcpy ( ql, rag[2], QLABEL_LEN );
}
}
}
/* Oerr:
* print ODBC error stack related to a given HANDLE
*
* eid: etab[] entry index
* tid: thread id
* line: line number where the Oerr() call occurred
* Ohandle: ODBC handle
* Otype: ODBC handle type
*
* return: void
*/
static void Oerr(int eid, int tid, unsigned int line, SQLHANDLE Ohandle, SQLSMALLINT Otype)
{
size_t bs=ERR_MSG_LEN; /* Memory needed for the error message */
SQLSMALLINT Oi=1,
Oln=0;
SQLINTEGER Onat=0;
SQLCHAR Ostate[6],
*O,
*Otxt;
SQLRETURN Orv;
time_t errtime = 0; /* error time */
struct timeval tverr; /* error timeval struct */
struct tm *errt; /* date/time struct */
char errts[20]; /* string with date/time in YYYY-MM-DD HH:MM:SS format*/
/* Initial memory allocation for the error message buffer */
if ( ( Otxt = malloc(bs) ) == (void *)NULL ) {
fprintf(stderr, "[%d] odb [Oerr(%d)] - Error allocating err msg buffers: [%d] %s\n",
tid, __LINE__, errno, strerror(errno));
return;
}
/* Get current time */
gettimeofday(&tverr, (void *)NULL);
errtime = (time_t)tverr.tv_sec;
errt = localtime(&errtime);
strftime(errts, sizeof(errts), "%Y-%m-%d %H:%M:%S", errt);
/* Loop through ODBC error stack */
while ( ( Orv = SQLGetDiagRec(Otype, Ohandle, Oi++, Ostate, &Onat, Otxt,
(SQLSMALLINT)bs, &Oln) ) != SQL_NO_DATA ) {
if ( (size_t)Oln > bs ) { /* error message buffer was too small */
bs = (size_t) ( Oln + 1 );
O = Otxt;
if ( ( O = realloc ( O, bs ) ) == (void *)NULL ) {
fprintf(stderr, "[%d] odb [Oerr(%d)] - Error re-allocating memory for err msg buff: [%d] %s\n",
tid, __LINE__, errno, strerror(errno));
continue;
}
Otxt = O;
if ( ( Orv = SQLGetDiagRec(Otype, Ohandle, (Oi - 1) , Ostate, &Onat, Otxt,
(SQLSMALLINT)bs, &Oln) ) != SQL_SUCCESS ) {
fprintf(stderr, "[%d] odb [Oerr(%d)] - Error %d getting errmsg with bigger buff of size %d\n",
tid, __LINE__, Orv, (int)bs);
break;
}
}
mfprintf(stderr, eid < 0 ? (FILE *)NULL : etab[eid].fso, "[%d] odb(%d) [%s] - %s (State: %s Native Err: %ld)\n",
tid, line, errts, (char *)Otxt, (char *)Ostate, (long) Onat);
if ( Orv < 0 ) {
fprintf(stderr, "[%d] odb [Oerr(%d)] - Error %d getting the error message\n", tid, __LINE__, Orv);
break;
}
}
fflush(stderr);
free(Otxt);
}
/* Omexec:
* Expand command looking for multi-objects and call Oexec for each object
* tid: Thread id;
* eid: Execution Table Process id (idx in the etab structure array)
* ten: Thread Execution no (progr # of cmd/script executed for a specific thread);
* (SQL Intepreter uses -1)
* scn: Script Command no (progr # of cmd executed in a given script);
* Ocmd: command to execute
* label: Query label
* dcat: default catalog
* dsch: default schema
*
* return: number of executed commands, (-1) if soe and errors
*/
static int Omexec(int tid, int eid, int ten, int scn, SQLCHAR *Ocmd, char *label, char *dcat, char *dsch)
{
unsigned int i=0, /* loop variable */
k=0, /* loop variable */
j=0; /* loop variable */
int ret=0; /* return value */
size_t ll, bl; /* Ocmd length */
SQLCHAR *Otype = 0, /* ODBC Object Type: see struct Otypes[] definition */
*Oncmd = 0, /* New (expanded) command */
Oname[MAXOBJ_LEN], /* ODBC object name */
Ostr[MAXOBJ_LEN], /* ODBC string to expand */
*Ocso[3]; /* ODBC array of catalog/schema/object pointers */
SQLLEN Onamel; /* ODBC object name length */
SQLRETURN Or=0; /* ODBC return value */
SQLHSTMT Ostmt = 0; /* Statement Handle */
/* Clean Ostr */
Ostr[0] = '\0';
/* look for expansion string */
bl = strlen((char *)Ocmd);
for ( i = 1; i < ( bl - 2 ); i++) {
if ( Ocmd[i] == '&' && Ocmd[i+2] == ':' && isspace(Ocmd[i-1]) ) {
for ( k = 0 ; Ocmd[i+k] != ';' && Ocmd[i+k] && !isspace(Ocmd[i+k]) ; k++ )
Ostr[k] = Ocmd[i+k];
Ostr[k] = '\0';
break;
}
}
/* if nothing to expand */
if ( !Ostr[0] )
return ( Oexec(tid, eid, ten, scn, Ocmd, label) );
/* Object type expansion */
for ( j = 0 ; j < sizeof(Otypes)/sizeof(struct Otype) ; j++ )
if ( Ostr[1] == Otypes[j].type )
Otype = Otypes[j].Otype;
if ( !Otype ) { /* type not found */
fprintf(stderr, "odb [Omexec(%d)] - Invalid object type >%c<\n",
__LINE__, (char)Ostr[1]);
ret = -1;
exstat = EX_USAGE ;
goto omexec_exit;
}
/* Allocate statement handle */
if (!SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT,
thps[tid].Oc, &Ostmt))){
Oerr(0, 0, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
ret = -1;
exstat = EX_ODBCERR ;
goto omexec_exit;
}
/* Allocate memory for the expanded command */
if ( ( Oncmd = malloc ( ( ll = bl + MAXOBJ_LEN ) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Omexec(%d)] - Error allocating memory for Oncmd\n", __LINE__);
ret = -1;
exstat = EX_OSERR ;
goto omexec_exit;
}
/* look for catalogs/schema/object */
splitcso ( (char *)&Ostr[3], Ocso, 1);
/* list objects */
if (!SQL_SUCCEEDED(Or=SQLTables(Ostmt,
( f & 0100000000) ? (SQLCHAR *)"": ( Ocso[0] ? Ocso[0] : (SQLCHAR *)dcat ), SQL_NTS,
( f & 020000000) ? (SQLCHAR *)"": ( Ocso[1] ? Ocso[1] : (SQLCHAR *)dsch), SQL_NTS,
Ocso[2], SQL_NTS, Otype , SQL_NTS ))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
ret = -1;
exstat = EX_ODBCERR ;
goto omexec_exit;
}
/* Bind object name */
if (!SQL_SUCCEEDED(Or = SQLBindCol(Ostmt, (SQLUSMALLINT) 3,
SQL_C_CHAR, Oname, (SQLLEN)sizeof(Oname), &Onamel))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
ret = -1;
exstat = EX_ODBCERR ;
goto omexec_exit;
}
/* Fetch object name and run */
for ( j = 0; SQL_SUCCEEDED(Or=SQLFetch(Ostmt)) ; j++ ) {
MEMCPY ( Oncmd, Ocmd, i);
Oncmd[i] = '\0';
if ( Ocso[0] && !(f & 0100000000) ) {
(void) strmcat((char *)Oncmd, (char *)Ocso[0], ll, 0);
(void) strmcat((char *)Oncmd, ".", ll, 0);
}
if ( Ocso[1] && !(f & 020000000) ) {
(void) strmcat((char *)Oncmd, (char *)Ocso[1], ll, 0);
(void) strmcat((char *)Oncmd, ".", ll, 0);
}
(void) strmcat((char *)Oncmd, (char *)Oname, ll, 0);
(void) strmcat((char *)Oncmd, (char *)(Ocmd+i+k), ll, 0);
if ( ! ( etab[eid].flg & 010000 ) ) /* if not quiet cmd on */
mfprintf(stderr, etab[eid].fso, "[%d.%d] Command expanded to: \'%s\'\n",
tid, j, Oncmd);
ret = Oexec(tid, eid, ten, scn, Oncmd, label);
if ( etab[eid].flg & 0004 && ret < 1 ) {
ret = -1;
break;
}
}
omexec_exit:
if ( Ostmt )
(void)SQLFreeHandle(SQL_HANDLE_STMT, Ostmt);
if ( Oncmd )
free ( Oncmd );
return ( ret );
}
/* Oexec:
* prepare and eventually execute a command printing the results.
* tid: Thread id;
* eid: Execution Table Process id (idx in the etab structure array)
* ten: Thread Execution no (progr # of cmd/script executed for a specific thread);
* (SQL Intepreter uses -1)
* scn: Script Command no (progr # of cmd executed in a given script);
* Ocmd: command to execute
* label: Query label
*
* return: 0 if no errors, (-1) otherwise
*/
static int Oexec(int tid, int eid, int ten, int scn, SQLCHAR *Ocmd, char *label)
{
int i=0, /* loop variable */
t=0, /* command type: 1=select, 2=update, 3=delete, 4=insert */
par = etab[eid].parent, /* Parent shortcut */
gzret = 0, /* zlib function return values */
ret = -1; /* function return value */
char *ch, /* used to browse the field to print looking for char to escape */
*os = 0, /* output string pointer */
*gzbuff = 0, /* GZIP IO buffer */
*obuff=0; /* output buffer pointer */
char *p = 0 ; /* loop variable */
char q = 0, /* quote flag */
buff[50], /* to build command string for csv output */
num[32]; /* Formatted Number String */
unsigned long l=0, /* loop variable */
b=0, /* Number of bytes written */
sline=0, /* Qs start timeline in ms */
eline=0; /* Qs end timeline in ms */
unsigned int mchl=0, /* Max Column Header Length used in print line mode */
osl = 0, /* Output string length */
cfl=0, /* CHAR/VARCHAR field length */
ncds=0, /* Non CHAR/VARCHAR display size */
cfields=0, /* number of CHAR/VARCHAR fields */
mrsds=0, /* minimal result set disp size when pad=fit */
tfl=0, /* total fields length */
d = 0, /* loop variable */
j = 0 , /* loop variable */
k = 0 , /* loop variable */
otype = 1, /* output type: 0=no print, 1=normal, 2=xml, 3=plm, 4=NOT USED, 5=pad */
otflg = 0017, /* output type flag: 0001=results 0002=timing 0004=headers 0010=print rs(no binary mode) */
omark = 0, /* output mark: 0=no mark, 1=normal mark, 2=mark+timeline */
*fl=0; /* Field length array: max between col name or field length */
unsigned char *fa=0, /* Field array mask:
0000 = align field left, 0010 = NOT USED 0200 = NOT USED
0001 = align field right, 0020 = NOT USED
0002 = use string qualifier 0040 = NOT USED
0004 = char/varchar field 0100 = nullable field */
lfs = etab[eid].fs, /* local field separator */
lrs = etab[eid].rs, /* local record separator */
lsq = etab[eid].sq, /* local string qualifier */
lec = etab[eid].ec; /* local escape character */
size_t pos=0, /* position in the output buffer */
nby=0, /* number of bytes returned by fwrite */
rsds=0, /* Result Set Display Size */
gzb = 0 , /* Number of gizipped bytes in the deflate output buffer */
obl = 0; /* output buffer (obuff) length */
long tinit=0, /* init time in ms */
tprep=0, /* prepare time in ms */
t1fetch=0, /* first fetch time in ms */
tfetch=0, /* total fetch time in ms */
telaps=0, /* elapsed time mark */
texec=0; /* exec time in ms */
SQLCHAR *Ocnames=0; /* ODBC column name returned by select */
SQLLEN Onrows=0; /* ODBC no of affected rows */
SQLRETURN Or; /* ODBC return value */
SQLHSTMT Ostmt=0; /* ODBC Statement Handle */
SQLSMALLINT Onamel, /* ODBC column name length returned by SQLDescribeCol */
*Odt=0, /* ODBC data type returned by SQLDescribeCol */
*Odd=0, /* ODBC decimal digit returned by SQLDescribeCol */
Onull; /* ODBC nullable returned by SQLDescribeCol */
SQLUSMALLINT Oncol; /* ODBC number of columns in the result set */
SQLPOINTER **Oresp = 0; /* ODBC Result Array Pointer */
SQLLEN **Olength = 0; /* ODBC Length indicator array pointer */
SQLLEN Oll = 0 ; /* ODBC Local Length Indicator value loop variable */
SQLULEN *Ors = 0; /* ODBC Record Display Size array pointer */
SQLULEN Orespl = 0; /* ODBC ulen to store SQLGetInfo results */
struct timeval tve; /* timeval struct to define elapesd/timelines */
unsigned int ffetch = 1 ; /* First Fetch marker */
Mutex *parmutex = &etab[par].pmutex ; /* Local copy of parent mutex address */
z_stream gzstream = { 0 }; /* Local gzstream structure */
unsigned int ucs = 0; /* Local ucs2toutf8 conversion flag */
size_t mcfsl = etab[eid].mcfs ? strlen(etab[eid].mcfs) : 0;
size_t mcrsl = etab[eid].mcrs ? strlen(etab[eid].mcrs) : 0;
/* register start time */
gettimeofday(&tve, (void *)NULL);
sline=1000*(tve.tv_sec-tvs.tv_sec)+(tve.tv_usec-tvs.tv_usec)/1000;
tinit = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
if ( etab[eid].flg2 & 020000000 ) { /* Oexec to allocate its own handle */
if ( !SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT, thps[tid].Oc, &Ostmt))){
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
return(-1);
}
} else {
Ostmt = thps[tid].Os;
}
/* Initialize otype, otflg, omark and ucs flags */
if ( etab[eid].flg & 0100000000 ) { /* Oexec in silent mode */
otype = 0;
} else if ( etab[eid].flg2 & 04000000 ) { /* xlm on */
otype = 2;
otflg &= ~0004 ; /* no headers */
} else if ( etab[eid].flg & 040000 ) { /* plm on */
otype = 3;
otflg &= ~0004 ; /* no headers */
} else if ( pad ) { /* pad */
otype = 5;
}
if ( !(etab[eid].flg & 0200) ) /* pcn is off */
otflg &= ~0004;
if ( etab[eid].flg & 020000 ) { /* -q res */
otflg &= ~0005;
otype = 0 ;
}
if ( etab[eid].flg2 & 0004 ) /* -q timing */
otflg &= ~0002;
if ( etab[eid].type == 'e' ) {
if ( etab[eid].flg & 0400 ) /* print simple extract mark */
omark = 1 ;
if ( etab[eid].flg2 & 020000 )
omark = 2 ;
}
if ( etab[eid].flg & 0100 ) /* nimary mode: no RS */
otflg &= ~0010 ;
if ( etab[eid].flg & 010000000 ) { /* Convert ucs2toutf8 */
ucs = 1 ;
}
/* Initialize deflate & allocate gzip io buffer */
if ( etab[eid].flg & 0020 ) { /* gzip output: allocate gzstream structure */
gzstream.zalloc = Z_NULL;
gzstream.zfree = Z_NULL;
gzstream.opaque = Z_NULL;
/* Initalize deflate */
if ( ( gzret = deflateInit2 (&gzstream, etab[eid].gzlev, Z_DEFLATED, windowBits + GZIP_ENCODING, 8, Z_DEFAULT_STRATEGY ) ) != Z_OK ) {
fprintf(stderr, "odb [Oexec(%d)] - Error initializing zlib: [%d]\n",
__LINE__, gzret);
return(-1);
}
if ( ( gzbuff = malloc((size_t)GZBUFF_LEN) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating gzip IO buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
return(-1);
}
}
/* Determine command type
The "right" solution here would have been:
SQLGetDiagField(SQL_HANDLE_STMT, Ostmt, 0, SQL_DIAG_DYNAMIC_FUNCTION_CODE, &Ostype,
SQL_IS_INTEGER, (SQLSMALLINT *)NULL)
and then switch based on Ostype. However this doesn't work with Vertica
Please note the fixed length '6' in strmicmp: SELECT/UPDATET/DELETE/INSERT have the same length */
for ( t = (int)((sizeof(rmess)/sizeof(struct rm)) - 1);
t > 0 && strmicmp(rmess[t].cmd, (char *)Ocmd, 6); t--);
/* Prepare command */
tprep -= 1000*tve.tv_sec + tve.tv_usec/1000;
if ((Or=SQLPrepare(Ostmt, Ocmd, SQL_NTS)) != SQL_SUCCESS) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if ( Or != SQL_SUCCESS_WITH_INFO )
return(-1);
}
gettimeofday(&tve, (void *)NULL); /* register end time */
tprep += 1000*tve.tv_sec + tve.tv_usec/1000;
if ( etab[eid].flg & 0010 ) { /* Null run: just check syntax preparing the command */
if ( ten >= 0 )
fprintf(stderr, "[%d.%d.%d]", tid, ten, scn);
mfprintf (stderr, etab[eid].fso, "--- prepared in %.3fs\n", tprep/1000.0);
return (0);
}
/* Execute command */
texec -= 1000*tve.tv_sec + tve.tv_usec/1000;
if (!SQL_SUCCEEDED(Or=SQLExecute(Ostmt)) && Or != SQL_NO_DATA) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
return(-1);
}
gettimeofday(&tve, (void *)NULL); /* register end time */
texec += 1000*tve.tv_sec + tve.tv_usec/1000;
/* If SQLExecute executes a searched update, insert, or delete statement that does... */
/* not affect any rows at the data source, the call to SQLExecute returns SQL_NO_DATA */
if ( Or == SQL_NO_DATA )
goto oexec_nocols ;
/* Find number of returned columns */
if (!SQL_SUCCEEDED(Or=SQLNumResultCols(Ostmt, (SQLSMALLINT *)&Oncol))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
return(-1);
}
if ( Oncol == 0 ) /* No resulting Columns... goto oexec_nocols: */
goto oexec_nocols;
tfetch -= 1000*tve.tv_sec + tve.tv_usec/1000;
/* Allocate memory for the record size array */
if ( (Ors = (SQLULEN *)calloc ((size_t)Oncol, sizeof(SQLULEN))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating record size array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
return(-1);
}
/* Allocate memory for the data type size array */
if ( (Odt = (SQLSMALLINT *)calloc ((size_t)Oncol, sizeof(SQLSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating data types array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Allocate memory for decimals array */
if ( (Odd = (SQLSMALLINT *)calloc ((size_t)Oncol, sizeof(SQLSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating decimals array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Allocate memory for the result array */
if ( (Oresp = (SQLPOINTER **)calloc ((size_t)Oncol, sizeof(SQLPOINTER *))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating result array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Allocate memory for the length indicator array */
if ( (Olength = (SQLLEN **)calloc ((size_t)Oncol, sizeof(SQLLEN *))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating length indicator array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Allocate memory for the field length array */
if ( (fl = (unsigned int *)calloc ((size_t)Oncol, sizeof(unsigned int))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating field length array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
if ( (fa = (unsigned char *)calloc ((size_t)Oncol, 1)) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating field align array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Allocate header's buffer memory */
if ( (Ocnames = (SQLCHAR *)calloc ((size_t)Oncol, MAXCOL_LEN)) == (void *)NULL){
fprintf(stderr, "odb [Oexec(%d)] - Error allocating header buffer memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Get Result Set columns descriptions */
for (j = 0; j < Oncol; j++) {
if ( !SQL_SUCCEEDED(Or=SQLDescribeCol(Ostmt, (SQLUSMALLINT)(j+1),
&Ocnames[j*MAXCOL_LEN], (SQLSMALLINT) MAXCOL_LEN, &Onamel,
&Odt[j], &Ors[j], &Odd[j], &Onull))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto oexec_exit;
}
if ( etab[eid].flg & 0400000 && t == 1 ) { /* Describe result set */
mfprintf(stderr, etab[eid].fso, "%s%s %s",
j ? "\t," : "CREATE TABLE ODBC.DATA.TYPES (\n\t ",
(char *)&Ocnames[j*MAXCOL_LEN], expandtype(Odt[j]));
switch(Odt[j]) {
case SQL_CHAR:
case SQL_WCHAR:
case SQL_VARCHAR:
case SQL_WVARCHAR:
case SQL_LONGVARCHAR:
case SQL_WLONGVARCHAR:
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
mfprintf(stderr, etab[eid].fso, "(%u) ", (unsigned int)Ors[j]);
break;
case SQL_NUMERIC:
case SQL_DECIMAL:
mfprintf(stderr, etab[eid].fso, "(%u,%u) ", (unsigned int)Ors[j], (unsigned int)Odd[j]);
break;
case SQL_TIME:
case SQL_TIMESTAMP:
case SQL_TYPE_TIME:
case SQL_TYPE_TIMESTAMP:
if ( Odd[j] )
mfprintf(stderr, etab[eid].fso, "(%u) ", (unsigned int)Odd[j]);
break;
default:
mfprintf(stderr, etab[eid].fso, " ");
break;
}
mfprintf(stderr, etab[eid].fso, "%s\n", Onull ? "" : "NOT NULL");
}
if ( (unsigned int) Onamel > mchl ) /* Calculate max column-name length */
mchl = (unsigned int)Onamel;
if(!SQL_SUCCEEDED(Or=SQLColAttribute(Ostmt, (SQLUSMALLINT)(j+1),
SQL_DESC_DISPLAY_SIZE, (SQLPOINTER) NULL, (SQLSMALLINT) 0,
(SQLSMALLINT *) NULL, (SQLPOINTER) &Ors[j]))) {
Oerr(eid, tid, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto oexec_exit;
}
if ( Onull )
fa[j] |= 0100;
if ( pad && (fa[j] & 0100 ) && ( Ors[j] < etab[eid].nl ) )
Ors[j] = etab[eid].nl; /* space to write nullstring */
if ( ( etab[eid].flg & 0200 ) || pad )
fl[j] = Ors[j] > (SQLULEN)Onamel ? (unsigned int)Ors[j] : (unsigned int)Onamel;
switch ( Odt[j] ) {
case SQL_REAL:
case SQL_DOUBLE:
case SQL_FLOAT:
case SQL_NUMERIC:
case SQL_DECIMAL:
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_TINYINT:
case SQL_BIGINT:
fa[j] |= 0001; /* numeric alignment right */
ncds += (unsigned int)Ors[j];
break;
case SQL_WCHAR:
if ( etab[eid].dbt != VERTICA ) /* Vertica's CHAR field length is in bytes (not chars) */
Ors[j] *= etab[eid].bpwc;
/* FALLTHRU */
case SQL_CHAR:
if ( etab[eid].dbt != VERTICA ) /* Vertica's CHAR field length is in bytes (not chars) */
Ors[j] *= etab[eid].bpc;
fa[j] |= 0004; /* This is a CHAR/VARCHAR field */
if ( etab[eid].sq ) /* if string qualifier char is defined */
fa[j] |= 0002; /* enclose CHAR/VARCHAR fields in sq */
if ( etab[eid].Omaxl && etab[eid].Omaxl < Ors[j] ) {
fl[j] = (unsigned int)etab[eid].Omaxl;
Ors[j] = etab[eid].Omaxl;
}
cfl += fl[j];
cfields++;
break;
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
if ( etab[eid].dbt != VERTICA ) /* Vertica's CHAR field length is in bytes (not chars) */
Ors[j] *= etab[eid].bpwc;
/* FALLTHRU */
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
if ( etab[eid].dbt != VERTICA ) /* Vertica's CHAR field length is in bytes (not chars) */
Ors[j] *= etab[eid].bpc;
fa[j] |= 0004; /* This is a CHAR/VARCHAR field */
if ( etab[eid].sq ) /* if string qualifier char is defined */
fa[j] |= 0002; /* enclose CHAR/VARCHAR fields in sq */
if ( etab[eid].Omaxl && etab[eid].Omaxl < Ors[j] ) {
fl[j] = (unsigned int)etab[eid].Omaxl;
Ors[j] = etab[eid].Omaxl;
}
cfl += fl[j];
cfields++;
break;
case SQL_TIME:
case SQL_TYPE_TIME:
case SQL_TIMESTAMP:
case SQL_TYPE_TIMESTAMP:
if ( etab[eid].flg & 0040 ) /* truncate decimals */
fa[j] |= 0200;
ncds += (unsigned int)Ors[j];
break;
default:
ncds += (unsigned int)Ors[j];
}
Ors[j]++; /* add space for NULL */
tfl += fl[j];
if ( fdmp && eid == par ) /* Parent to initialize ODBC dump file */
fprintf(fdmp, "[%d] Column %d: name=%s, type=%d (%s), size=%lu, decimals=%d, nullable=%s\n",
tid, j, &Ocnames[j*MAXCOL_LEN], (int)Odt[j], expandtype(Odt[j]),
(unsigned long)Ors[j], (int)Odd[j], Onull ? "yes" : "no" ) ;
rsds += (size_t) Ors[j];
}
/* Close Describe Result set */
if ( etab[eid].flg & 0400000 && t == 1 ) { /* Describe result set */
mfprintf(stderr, etab[eid].fso, ");\n");
mfprintf(stderr, etab[eid].fso, "Result Set Display Size: %zu bytes (%zu bytes including terminating NULLs)\n", rsds - j, rsds);
}
/* reduce CHAR/VARCHAR field length to fit display size */
if ( !(etab[eid].flg & 040000) && pad == 2 && mrcol < tfl ) {
if ( ncds + (unsigned int)Oncol + cfields * 2 > mrcol ) {
fprintf(stderr, "odb [Oexec(%d)] - Warning cannot fit record in %u cols\n",
__LINE__, mrcol);
pad = 3; /* temporary pad. behaves like full, reset to pad=2 at the end */
} else {
mrsds = tfl - cfl + Oncol - 1 ; /* space needed for non CHAR/VCHAR fields + field seps */
for (j = 0; j < Oncol; j++) { /* shrink fields longer than content (because of col titles) */
if ( !(fa[j] & 0004) ) { /* this is NOT a CHAR/VARCHAR field */
if ( fl[j] > (unsigned int)Ors[j] ) {
d = fl[j] - (unsigned int)Ors[j] + 1;
fl[j] -= d;
mrsds -= d;
cfl += d;
}
}
}
for (j = 0, d = 0; j < Oncol; j++) { /* shrink CHAR/VCHAR fields */
if ( fa[j] & 0004 ) { /* this is a CHAR/VARCHAR field */
fl[j] = (unsigned int) ( fl[j] * ( mrcol - mrsds ) * 1.0 / cfl - 1.0 ); /* assign space proportionally */
if ( fl[j] < 2 ) /* for short fields... */
fl[j] = 2; /* ... use a min length of 2 chars */
Ors[j] = fl[j] + 1; /* update Ors with new field length + NULL */
}
d += fl[j];
}
/* Finally: assign remaining chars due to division roundings */
for (j = 0, d = mrcol - d - Oncol + 1 ; d && j < Oncol ; j++) {
if ( fa[j] & 0004 ) { /* this is a CHAR/VARCHAR field */
fl[j]++;
Ors[j]++;
d--;
}
}
}
}
/* Calculate rowset if buffer size is set */
if ( etab[eid].rbs ) {
etab[eid].r = etab[eid].rbs / rsds;
if ( etab[eid].mr && etab[eid].r > etab[eid].mr ) /* if # records to fetch < rowset ... */
etab[eid].r = etab[eid].mr; /* make rowset = records to fetch */
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; /* at least one record at a time */
}
/* Set output buffer length */
obl = ( rsds + 1 ) * ( etab[eid].r + 1 ) ;
/* Allocate memory for the result set based on display size */
for (j = 0; j < Oncol; j++) {
if ( (Oresp[j] = (SQLPOINTER *)calloc (etab[eid].r, (size_t)Ors[j])) == (void *)NULL ||
(Olength[j] = (SQLLEN *)calloc (etab[eid].r, sizeof(SQLLEN))) == (void *)NULL ) {
fprintf(stderr, "odb [Oexec(%d)] - Error allocating memory (%d bytes) for %s: [%d] %s\n",
__LINE__, (int) Ors[j], (char *)&Ocnames[j*MAXCOL_LEN], errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
/* Binding columns */
if (!SQL_SUCCEEDED(Or = SQLBindCol(Ostmt, (SQLUSMALLINT)(j+1),
etab[eid].flg & 0100 ? SQL_C_BINARY : SQL_C_CHAR,
Oresp[j], Ors[j], Olength[j]))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_OSERR ;
goto oexec_exit;
}
}
/* Set Statement attributes for Column-wise binding */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_BIND_TYPE,
(SQLPOINTER)SQL_BIND_BY_COLUMN, 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto oexec_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_ARRAY_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto oexec_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_STATUS_PTR,
NULL, 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto oexec_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROWS_FETCHED_PTR,
&Orespl, 0))) {
exstat = EX_ODBCERR ;
goto oexec_exit;
}
/* Fetch data */
while ( go &&
SQL_SUCCEEDED(Or = SQLFetchScroll(Ostmt, SQL_FETCH_NEXT, 0))) {
if ( ffetch ) { /* first fetch */
ffetch = 0 ;
gettimeofday(&tve, (void *)NULL); /* register time */
t1fetch = tfetch + 1000*tve.tv_sec + tve.tv_usec/1000;
if ( (obuff = malloc ( obl )) == (void *)NULL ) { /* Allocate memory for the output buffer */
fprintf(stderr, "odb [Oexec(%d)] - Error allocating output buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
exstat = EX_OSERR ;
goto oexec_exit;
}
if ( otflg & 0004 ) { /* print column header */
if ( pad ) {
for ( i = 0 ; i < Oncol ; i++ ) {
for ( l = 0 ; Ocnames[i*MAXCOL_LEN+l] ; l++ ) {
if ( pad == 2 && l == fl[i] ) {
Ocnames[i*MAXCOL_LEN+l] = '\0';
Ocnames[i*MAXCOL_LEN+l-1] = '>';
break;
}
}
pos += snprintf(&obuff[pos], obl - pos,
( fa[i] & 0001 ) ? "%*s" : "%-*s",
(int)( fl[i] > MAXCOL_LEN ? MAXCOL_LEN : fl[i] ),
(char *)&Ocnames[i*MAXCOL_LEN]);
if ( (i+1) < (int)Oncol)
obuff[pos++] = lfs;
}
obuff[pos++] = etab[eid].rs;
for (i = 0; i < (int) Oncol; i++) {
for ( l = 0; l < fl[i] && l < MAXCOL_LEN ; l++)
obuff[pos++] = '-';
if ( (i+1) < (int) Oncol)
obuff[pos++] = '+';
}
} else {
for ( i = 0 ; i < Oncol ; i++ ) {
p = (char *)&Ocnames[i*MAXCOL_LEN] ;
while ( *p )
obuff[pos++] = *p++ ;
if ( (i+1) < (int)Oncol)
obuff[pos++] = lfs;
}
}
obuff[pos++] = lrs;
}
}
Onrows += Orespl;
if ( fdmp ) { /* Dump ODBC buffers */
MutexLock( parmutex );
for ( i = 0 ; i < (int)Oncol ; i++ ) {
fprintf(fdmp, "[%d] Column %d: %s (%lu values, %lu bytes/value)\n",
tid, i, (char *)&Ocnames[i*MAXCOL_LEN], (unsigned long) Orespl, (unsigned long)Ors[i] ) ;
dumpbuff (fdmp, eid, (unsigned char *)Oresp[i], (size_t)(Orespl * Ors[i]) , 1 );
}
MutexUnlock( parmutex );
}
if ( ucs ) { /* UCS-2 to UTF-8 conversion */
for ( l = 0 ; l < (unsigned long)Orespl ; l++ ) {
for ( i = 0 ; i < (int)Oncol ; i++ ) {
switch ( etab[eid].td[i].Otype ) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
if ( ucs2toutf8 ( (SQLCHAR *)Oresp[i] + (Ors[i] * l ) , &Olength[i][l], Ors[i], etab[eid].bucs2 ) ) {
fprintf(stderr, "odb [Oexec(%d)] - Error converting UCS-2 column %s\n",
__LINE__, (char *)etab[eid].td[i].Oname);
}
break;
default:
break; /* do nothing */
}
}
}
}
/* Fill output buffer */
switch ( otype ) {
case 0: /* no print */
break;
case 1: /* normal print (csv like) */
for ( l = 0 ; l < (unsigned long)Orespl ; l++) {
for (i = 0; i < (int) Oncol; i++) {
if ( i ) {
if (!etab[eid].mcfs)
obuff[pos++] = lfs;
else {
strcpy(&obuff[pos], etab[eid].mcfs);
pos += mcfsl;
}
}
switch ( Oll = Olength[i][l] ) {
case 0:
os = etab[eid].es ;
osl = etab[eid].el ;
break;
case SQL_NULL_DATA:
os = etab[eid].ns ;
osl = etab[eid].nl ;
break;
default:
os = (char *)Oresp[i] + (Ors[i] * l) ;
osl = (unsigned int) Oll;
q = fa[i] & 0002 ; /* quote? */
}
if ( q ) { /* Print qualified strings with escapes */
obuff[pos++] = lsq;
for ( ch = os, j = 0; j < osl ; j++ ) {
if ( *ch == lsq || *ch == lec )
obuff[pos++] = lec;
obuff[pos++] = *ch++;
}
obuff[pos++] = lsq;
} else { /* Print based on field length (long fields) */
MEMCPY ( &obuff[pos], os, osl ) ;
pos += (size_t)osl;
}
}
if ( otflg & 0010 ) {
if (!etab[eid].mcrs)
obuff[pos++] = lrs;
else {
strcpy(&obuff[pos], etab[eid].mcrs);
pos += mcrsl;
}
}
}
break;
case 2: /* XML print */
for ( l = 0 ; l < (unsigned long)Orespl ; l++) {
pos += snprintf(&obuff[pos], obl - pos, "\n <row>");
for (i = 0; i < (int) Oncol; i++) {
pos += snprintf(&obuff[pos], obl - pos, "\n <%s>", (char *)&Ocnames[i*MAXCOL_LEN]);
switch ( Oll = Olength[i][l] ) {
case 0:
os = etab[eid].es ;
break;
case SQL_NULL_DATA:
os = etab[eid].ns ;
break;
default:
os = (char *)Oresp[i] + (Ors[i] * l) ;
}
pos += snprintf(&obuff[pos], obl - pos, "%s</%s>", os, (char *)&Ocnames[i*MAXCOL_LEN]);
}
pos += snprintf(&obuff[pos], obl - pos, "\n </row>");
}
break;
case 3: /* Print Line Mode */
for ( l = 0 ; l < (unsigned long)Orespl ; l++) {
k = (unsigned int)snprintf(&obuff[pos], obl - pos, "[ROW %lu] ", l + 1 ) ;
pos += (size_t) k ;
while ( k++ < mrcol )
obuff[pos++] = k == mchl + 1 ? '+' : '-' ;
obuff[pos++] = '\n';
for (i = 0; i < (int) Oncol; i++) {
switch ( Oll = Olength[i][l] ) {
case 0:
os = etab[eid].es ;
break;
case SQL_NULL_DATA:
os = etab[eid].ns ;
break;
default:
os = (char *)Oresp[i] + (Ors[i] * l) ;
}
pos += snprintf(&obuff[pos], obl - pos, "%-*s|%s\n", mchl,
(char *)&Ocnames[i*MAXCOL_LEN], os);
}
}
break;
case 5: /* pad printing */
for ( l = 0 ; l < (unsigned long)Orespl ; l++) {
for (i = 0; i < (int) Oncol; i++) {
if ( i )
obuff[pos++] = lfs;
switch ( Oll = Olength[i][l] ) {
case 0:
os = etab[eid].es ;
osl = etab[eid].el ;
break;
case SQL_NULL_DATA:
os = etab[eid].ns ;
osl = etab[eid].nl ;
break;
default:
os = (char *)Oresp[i] + (Ors[i] * l) ;
osl = (unsigned int) Oll ;
}
if ( fl[i] < osl ) { /* field has been truncated */
os[fl[i]-1] = '>';
os[fl[i]] = '\0';
}
pos += snprintf(&obuff[pos], obl - pos, (fa[i]&0001)?"%*s":"%-*s",
(int)fl[i], os);
}
obuff[pos++] = lrs;
}
break;
}
/* Write output buffer */
if ( etab[eid].flg & 0020 ) { /* gzip output */
gzstream.next_in = (unsigned char *)obuff ;
gzstream.avail_in = (unsigned int) pos ;
MutexLock( parmutex );
do {
gzstream.avail_out = GZBUFF_LEN ;
gzstream.next_out = (unsigned char *)gzbuff ;
if ( ( gzret = deflate (&gzstream, Z_NO_FLUSH) ) != Z_OK ) {
fprintf(stderr, "odb [Oexec(%d)] - Error during deflate: [%d]\n",
__LINE__, gzret);
MutexUnlock( parmutex );
goto oexec_exit ;
}
gzb = GZBUFF_LEN - gzstream.avail_out ;
#ifdef HDFS
if ( etab[eid].fho ) { /* write gzipped file to normal output */
nby = ( size_t) (*hdfswrite)(hfs, etab[eid].fho, (void *)gzbuff, gzb);
} else { /* HDFS output */
if ( ( nby = fwrite ( gzbuff, 1, gzb, etab[eid].fo ) ) != gzb )
fprintf(stderr, "odb [Oexec(%d)] - Warning fwrite wrote " SIZET_SPEC " out of " SIZET_SPEC " bytes: %s",
__LINE__, nby, gzb, strerror(errno));
}
#else
if ( ( nby = fwrite ( gzbuff, 1, gzb, etab[eid].fo ) ) != gzb )
fprintf(stderr, "odb [Oexec(%d)] - Warning fwrite wrote " SIZET_SPEC " out of " SIZET_SPEC " bytes: %s",
__LINE__, nby, gzb, strerror(errno));
#endif
b += (unsigned long)nby;
} while ( gzstream.avail_out == 0 ) ;
gzstream.next_in = (unsigned char *)obuff ;
gzstream.next_out = (unsigned char *)gzbuff ;
gzstream.avail_in = 0 ;
gzstream.avail_out = GZBUFF_LEN ;
if ( ( gzret = deflate (&gzstream, Z_FINISH) ) != Z_STREAM_END ) {
fprintf(stderr, "odb [Oexec(%d)] - Error during deflate: [%d]\n",
__LINE__, gzret);
goto oexec_exit ;
}
gzb = GZBUFF_LEN - gzstream.avail_out ;
#ifdef HDFS
if ( etab[eid].fho ) { /* write gzipped file to normal output */
nby = ( size_t) (*hdfswrite)(hfs, etab[eid].fho, (void *)gzbuff, gzb);
} else { /* HDFS output */
if ( ( nby = fwrite ( gzbuff, 1, gzb, etab[eid].fo ) ) != gzb )
fprintf(stderr, "odb [Oexec(%d)] - Warning fwrite wrote " SIZET_SPEC " out of " SIZET_SPEC " bytes: %s",
__LINE__, nby, gzb, strerror(errno));
}
#else
if ( ( nby = fwrite ( gzbuff, 1, gzb, etab[eid].fo ) ) != gzb )
fprintf(stderr, "odb [Oexec(%d)] - Warning fwrite wrote " SIZET_SPEC " out of " SIZET_SPEC " bytes: %s",
__LINE__, nby, gzb, strerror(errno));
#endif
b += (unsigned long)nby;
if ( ( gzret = deflateReset (&gzstream) ) != Z_OK ) {
fprintf(stderr, "odb [Oexec(%d)] - Error during deflateReset: [%d]\n",
__LINE__, gzret);
}
MutexUnlock( parmutex );
#ifdef HDFS
} else if ( etab[eid].fho ) { /* non-HDFS output */
MutexLock( parmutex );
nby = ( size_t) (*hdfswrite)(hfs, etab[eid].fho, (void *)obuff, pos);
MutexUnlock( parmutex );
b += (unsigned long)nby;
#endif
} else { /* HDFS output */
if ( ( nby = fwrite ( obuff, 1, pos, etab[eid].fo ) ) != pos )
fprintf(stderr, "odb [Oexec(%d)] - Warning fwrite wrote " SIZET_SPEC " out of " SIZET_SPEC " bytes: %s",
__LINE__, nby, pos, strerror(errno));
b += (unsigned long)nby;
}
if ( etab[eid].fso )
if ( ( nby = fwrite ( obuff, 1, pos, etab[eid].fso ) ) != pos )
fprintf(stderr, "odb [Oexec(%d)] - Warning fwrite sppoled " SIZET_SPEC " out of " SIZET_SPEC " bytes: %s",
__LINE__, nby, pos, strerror(errno));
pos = 0;
/* Print mark for extracts */
switch ( omark ) {
case 1: /* simple mark */
fprintf(stderr,"[%d] %s records extracted\n", tid, strmnum((double)Onrows,num,sizeof(num),0));
break;
case 2: /* mark + timeline */
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
fprintf(stderr,"[%d] %s records extracted (%ld ms)\n", tid, strmnum((double)Onrows,num,sizeof(num),0), telaps);
break;
}
/* Check Max fetches */
if ( etab[eid].mr && (unsigned long)Onrows >= etab[eid].mr ) /* max fetches */
break;
}
if ( Onrows && otype == 3 ) { /* Print Line Mode: end line */
for ( k = 0 ; k < mrcol ; k++ )
obuff[pos++] = k == mchl ? '+' : '-' ;
obuff[pos++] = '\n';
}
/* deflateEnd gzip */
if ( etab[eid].flg & 0020 ) { /* gzip output */
if ( ( gzret = deflateEnd (&gzstream) ) != Z_OK ) {
fprintf(stderr, "odb [Oexec(%d)] - Error during deflateEnd: [%d]\n",
__LINE__, gzret);
}
}
/* Check fetch loop return value */
if (Or != SQL_NO_DATA && Or != SQL_SUCCESS) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto oexec_exit;
}
/* Register end fetch time */
gettimeofday(&tve, (void *)NULL); /* register end fetch time */
tfetch += 1000*tve.tv_sec + tve.tv_usec/1000;
eline=1000*(tve.tv_sec-tvs.tv_sec)+(tve.tv_usec-tvs.tv_usec)/1000;
oexec_nocols: /* no columns found during exec */
/* Find number of affected rows */
if ( t > 1 && !SQL_SUCCEEDED(Or=SQLRowCount(Ostmt, &Onrows))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto oexec_exit;
}
if ( f & 00400 ) { /* csv timing output */
for ( i = j = 0 ; Ocmd[j] && i < 49 ; j++ )
if ( !isspace(Ocmd[j]) )
buff[i++] = (char)Ocmd[j];
else if ( i && buff[i - 1] != ' ' )
buff[i++] = ' ';
if ( Ocmd[j] )
buff[i++] = '>';
buff[i] = '\0';
printf("%d%c%d%c%d%c%d%c%s%c%s%c\"%s\"%c%ld%c" SIZET_SPEC "%c%.3f%c%.3f%c%.3f%c%.3f%c%.3f%c%lu%c%lu\n",
tid, fs, eid, fs, ten, fs, scn, fs,
(etab[eid].type != 'x' ? etab[eid].run : "(none)"), fs,
(label ? label : "(none)"), fs,
buff, fs, (long int) Onrows, fs, rsds, fs,
tprep/1000.0,fs, texec/1000.0, fs, t1fetch/1000.0, fs, tfetch/1000.0, fs,
(tprep+texec+tfetch)/1000.0,fs, sline, fs, eline);
fflush(stdout);
} else if (etab[eid].type == 'e' ) { /* extract timing */
etab[eid].k = (unsigned int)rsds ; /* pass back record display size */
etab[eid].mr = (unsigned long)tinit ; /* pass back tinit */
etab[eid].nrt = (unsigned long)(tprep+texec+tfetch) ; /* pass back timig info */
etab[eid].nbs = b ; /* pass back no. of read bytes */
} else if ( otflg & 0002 ) { /* print timing info */
if ( ten >= 0 ) /* not in SQL Interpreter */
mfprintf(stderr, etab[eid].fso, "[%d.%d.%d]", tid, ten, scn);
if ( t )
mfprintf(stderr, etab[eid].fso, "--- %s row(s) %s", strmnum((double)Onrows,num,sizeof(num),0), rmess[t].mex);
else
mfprintf(stderr, etab[eid].fso, "--- command executed");
mfprintf (stderr, etab[eid].fso, " in %ss ", strmnum((double)(tprep+texec+tfetch)/1000.0,num,sizeof(num),3));
mfprintf (stderr, etab[eid].fso, "(prep %ss, ", strmnum((double)tprep/1000.0,num,sizeof(num),3));
mfprintf (stderr, etab[eid].fso, "exec %ss, ", strmnum((double)texec/1000.0,num,sizeof(num),3));
mfprintf (stderr, etab[eid].fso, "fetch %ss", strmnum((double)tfetch/1000.0,num,sizeof(num),3));
mfprintf (stderr, etab[eid].fso, "/%ss)\n", strmnum((double)t1fetch/1000.0,num,sizeof(num),3));
}
etab[eid].nr = (unsigned long)Onrows ; /* pass back no. of resulting rows */
ret = t; /* set return value */
oexec_exit:
if ( pad == 3 )
pad = 2;
/* Close & Free statement handle & Memory */
if ( obuff )
free ( obuff );
if ( Oresp ) {
for ( i = 0; i < Oncol ; i++) {
if ( Oresp[i] )
free (Oresp[i]);
}
free(Oresp);
}
if ( Olength ) {
for ( i = 0; i < Oncol ; i++)
free (Olength[i]);
free(Olength);
}
if ( etab[eid].flg2 & 020000000 ) {
(void)SQLFreeHandle(SQL_HANDLE_STMT, Ostmt);
} else {
(void)SQLFreeStmt(Ostmt, SQL_CLOSE);
(void)SQLFreeStmt(Ostmt, SQL_UNBIND);
}
if ( Ors )
free(Ors);
if ( Odt )
free(Odt);
if ( Odd )
free(Odd);
if ( Ocnames )
free(Ocnames);
if ( fl )
free(fl);
if ( fa )
free(fa);
return(ret); /* return type of executed command */
}
/* Oruncmd:
* executes commands/scripts allocated for a given thread.
*
* tid: thread id
*
* return: void
*/
#ifdef _WIN32
static DWORD WINAPI Oruncmd(void *tid)
#else
static void *Oruncmd(void *tid)
#endif
{
register unsigned int i = 0; /* loop variable */
int eid = 0, /* etab idx loop variable */
n = 0, /* command/script no for this thread */
ret = 0, /* return value */
id = 0; /* thread id */
unsigned int lcd = 0; /* delay to start next command within a thread */
id = *(int *)tid;
#ifndef _WIN32
pthread_cleanup_push(tunlock, (void *)&eid);
pthread_cleanup_push(tclean, tid);
#endif
for ( i = 0; go && ( ( nloop == 0 ) || ( i < nloop ) ) ; i++ ) {
if ( ( f & 010000000 ) && i && ( f & 02000000 ) )
etabshuffle(); /* re-shuffle etab[] for serial runs */
if ( i && ld ) /* loop delay */
Sleep (ld);
for ( eid = 0, n = 0; go && eid < no ; eid++ ) {
if ( etab[eid].tbe != 1 )
continue; /* skip completed or started jobs */
if ( f & 01000000000 ) { /* if Dynamic Load Balancing */
MutexLock(&dlbmutex);
if ( etab[eid].tbe != 1 ) { /* re-check eid is not assigned already */
MutexUnlock(&dlbmutex);
continue; /* skip completed or started jobs */
}
switch ( etab[eid].type ) {
case 'C':
case 'P':
case 'D':
case 'Z':
if ( thps[id].cr == 0 ) { /* This thread is not connected with target */
MutexUnlock(&dlbmutex);
continue ;
} else {
etab[eid].id = id; /* dynamically assign thread id */
etab[eid].tbe = 2; /* job started */
}
break ;
case 'c':
case 'p':
case 'd':
if ( thps[id].cr ) { /* This thread is not connected with source */
MutexUnlock(&dlbmutex);
continue ;
}
/* FALLTHRU */
default:
if ( etab[eid].tbe != 1 ) {
MutexUnlock(&dlbmutex);
continue ;
}
etab[eid].tbe = 2; /* job started */
etab[eid].id = id; /* dynamically assign thread id */
break ;
}
MutexUnlock(&dlbmutex);
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [Oruncmd(%d)] - thread %d associated with eid=%d\n", __LINE__, id, eid);
} else if ( etab[eid].id != id ) { /* Static Load Balancing: skip jobs assigned to other threads */
continue;
} else {
etab[eid].tbe = 2; /* job started */
}
if ( thps[id].cd2 ) /* random delay between cd and cd2 */
lcd = (unsigned int) ( thps[id].cd + rand() / ( RAND_MAX + 1.0 ) * ( thps[id].cd2 - thps[id].cd + 1 ) );
else
lcd = thps[id].cd;
if ( n && lcd )
Sleep (lcd);
switch ( etab[eid].type ) {
case 'x': /* run commands */
if ( tn > 1 && ! ( etab[eid].flg & 010000 ) ) /* if not quiet cmd on */
mfprintf(stderr, etab[eid].fso, "[%d.%d]%s: \'%s\'\n",
id, n, ( etab[eid].flg & 0010 ? "Preparing" : "Executing" ), etab[eid].run);
ret = Omexec ( id, eid, n++, 0, (SQLCHAR *)etab[eid].run, "", (char *)NULL, (char *)NULL);
break;
case 'l': /* load (read file) thread */
#ifdef XML
if ( etab[eid].xrt )
OloadX ( eid ) ; /* XML Load */
else
#endif
if ( etab[eid].flg & 01000000 )
Oload ( eid ) ; /* complex load */
else if (etab[eid].jsonKey)
OloadJson ( eid ) ;
else
Oload2 ( eid ); /* simple (fast) CSV load */
break;
case 'L': /* load (insert to target) thread */
(void)Oloadbuff( eid );
break ;
case 'C': /* copy (insert to target) thread */
case 'P': /* pipe (insert to target) thread */
do {
ret = Oloadbuff( eid );
if ( ret ) {
if ( f & 020000 )
fprintf(stderr, "odb [Oruncmd(%d)] - Oloadbuff[%d] returned %d\n", __LINE__, eid, ret);
fflush(stdout);
/* Disconnect */
if (!SQL_SUCCEEDED(Oret=SQLDisconnect(thps[etab[eid].id].Oc)))
Oerr(0, 0, __LINE__, thps[etab[eid].id].Oc, SQL_HANDLE_DBC);
etab[eid].mpre = 0 ; /* do not re-run target mpre a second time */
/* Reconnect: Obuf[0] still contains the connection string built during the initial connect */
if (!SQL_SUCCEEDED(Oret=SQLDriverConnect(thps[etab[eid].id].Oc, (SQLHWND) NULL,
Obuf[0], SQL_NTS, Oocs, (SQLSMALLINT) sizeof(Oocs),
&Oocsl, (SQLUSMALLINT)SQL_DRIVER_NOPROMPT)))
Oerr(0, 0, __LINE__, thps[etab[eid].id].Oc, SQL_HANDLE_DBC);
/* Reallocate statement handle */
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT,
thps[etab[eid].id].Oc, &thps[etab[eid].id].Os)))
Oerr(0, 0, __LINE__, thps[etab[eid].id].Oc, SQL_HANDLE_DBC);
if ( f & 020000 )
fprintf(stderr, "odb [Oruncmd(%d)] - Oloadbuff[%d] reconnected\n", __LINE__, ret);
} else {
break ;
}
} while ( etab[eid].roe-- ) ;
break;
case 'c': /* copy (read from source) thread */
case 'p': /* pipe (read from source) thread */
do {
ret = Ocopy( eid );
if ( ret && etab[eid].roe ) {
if ( f & 020000 )
fprintf(stderr, "odb [Oruncmd(%d)] - OCopy returned %d\n", __LINE__, ret);
fflush(stdout);
/* Disconnect */
if (!SQL_SUCCEEDED(Oret=SQLDisconnect(thps[etab[eid].id].Oc)))
Oerr(0, 0, __LINE__, thps[etab[eid].id].Oc, SQL_HANDLE_DBC);
/* Reset Parameters */
etab[eid].pre = 0 ; /* do not re-run target mpre a second time */
etab[eid].mpre = 0; /* do not re-run mpre on source */
/* Wait */
Sleep ( etab[eid].roedel ) ;
etab[eid].roedel *= 2 ;
/* Reconnect: Oics still contains the connection string built during the initial connect */
if (!SQL_SUCCEEDED(Oret=SQLDriverConnect(thps[etab[eid].id].Oc, (SQLHWND) NULL,
Oics, SQL_NTS, Oocs, (SQLSMALLINT) sizeof(Oocs),
&Oocsl, (SQLUSMALLINT)SQL_DRIVER_NOPROMPT)))
Oerr(0, 0, __LINE__, thps[etab[eid].id].Oc, SQL_HANDLE_DBC);
/* Reallocate statement handle */
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT,
thps[etab[eid].id].Oc, &thps[etab[eid].id].Os)))
Oerr(0, 0, __LINE__, thps[etab[eid].id].Oc, SQL_HANDLE_DBC);
if ( f & 020000 )
fprintf(stderr, "odb [Oruncmd(%d)] - OCopy restarted\n", __LINE__);
} else {
break ;
}
} while ( etab[eid].roe-- ) ;
break;
case 'e': /* extract thread */
Oextract( eid );
break;
case 'd': /* diff (read from source) thread */
case 'D': /* diff (read from target) thread */
Odiff( eid );
break;
case 'Z': /* diff (compare) thread */
Ocompare( eid );
break;
case 'f': /* run SQL script thread */
case 'F': /* run SQL script thread (Win -S/-P) */
ret = runsql ( id, eid, n++, etab[eid].run );
break;
}
if ( f & 01000000000 ) { /* if Dynamic Load Balancing */
MutexLock(&dlbmutex);
etab[eid].tbe = ( i < nloop -1 ) ? 1 : 0 ;
MutexUnlock(&dlbmutex);
} else {
etab[eid].tbe = ( i < nloop -1 ) ? 1 : 0 ;
}
if ( ret && etab[eid].flg & 0004 ) {
i = nloop; /* stop loops */
break;
}
}
}
#ifdef _WIN32
tclean(tid);
return((DWORD)0);
#else
pthread_cleanup_pop(1);
pthread_cleanup_pop(2);
if(!(f & 02000) && !( f & 010000000 ))
pthread_exit((void *)NULL);
return((void *)NULL);
#endif
}
/* sigcatch:
* print a message with the catched signal and exit
*
* sig: integer used as exit status (received signal)
*
* return: void
*/
static void sigcatch(int sig)
{
int i = 0;
switch ( sig ) {
#ifndef _WIN32
case SIGALRM:
fprintf(stderr, "odb [sigcatch(%d)] - Received SIGALRM (timeout) after %us. Exiting\n",
__LINE__, w);
break;
#endif
case SIGINT:
fprintf(stderr, "odb [sigcatch(%d)] - Received SIGINT. Exiting\n", __LINE__);
go = 0;
break;
case SIGTERM:
fprintf(stderr, "odb [sigcatch(%d)] - Received SIGTERM. Exiting\n", __LINE__);
go = 0;
break;
}
#ifdef _WIN32
gclean();
exit ( EX_SIGNAL );
#else
if ( tn == 1 ) { /* single threaded */
gclean();
} else {
for ( i = 0 ; i < tn ; i++ ) {
if ( !pthread_kill(thid[i], 0) ) { /* If this thread is alive... */
if ( pthread_cancel(thid[i]) ) /* ... cancel it */
fprintf(stderr, "odb [sigcatch(%d)] - Error canceling thread %d: [%d] %s\n",
__LINE__, i, errno, strerror(errno) );
}
}
}
exit(EX_SIGNAL);
#endif
}
/* tclean: Thread cleanup routine
*
* tid: thread identifier
* return: void
*/
static void tclean(void *tid)
{
int id = tid ? *(int *)tid : 0 ;
if ( ! ( f & 02000 ) ) { /* if not the Interpreter ... */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: thread %d closing connection...\n", id);
if ( thps[id].cr <= 0 ) { /* if cr > 0 connection handle belongs to another thread */
if (thps[id].Oc) { /* if connected... */
if (!SQL_SUCCEEDED(SQLDisconnect(thps[id].Oc)))
Oerr(-1, id, __LINE__, thps[id].Oc, SQL_HANDLE_DBC);
(void)SQLFreeHandle(SQL_HANDLE_DBC, thps[id].Oc); /* free conn handle */
thps[id].Oc = 0;
}
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: thread %d is ending...\n", id);
}
}
#ifndef _WIN32
/* tunlock: Thread Mutexes & Condition variables unlock. This function is actived
* when a multithreaded program using condition varables and mutexes is
* interrupted via SIGINT or SIGQUIT. It unlock mutexes and wakeup waiting
* threads
*
* eid: execution table identifier
* return: void
*/
static void tunlock(void *eid)
{
int id = *(int *)eid ;
if ( go == 0 ) { /* MT instance interrupted via SIGINT/SIGQUIT */
switch(etab[id].type) {
case 'e':
MutexUnlock(&etab[etab[id].parent].pmutex);
break;
case 'c':
case 'd':
MutexUnlock(&etab[id].pmutex);
CondWakeAll(&etab[id].pcvc);
break;
case 'C':
case 'D':
MutexUnlock(&etab[etab[id].parent].pmutex);
CondWakeAll(&etab[etab[id].parent].pcvc);
break;
case 'Z':
MutexUnlock(&etab[etab[id].child].pmutex);
CondWakeAll(&etab[etab[id].child].pcvc);
break;
}
}
}
#endif
/* gclean: Global clean routine
*
* return: void
*/
static void gclean(void)
{
int i = 0; /* loop variable */
int j = 0; /* loop variable */
struct ovar *p = 0;
if ( fdmp && fdmp != stdout ) /* close dump file */
fclose ( fdmp ) ;
MutexDestroy(&dlbmutex);
#ifndef _WIN32
if ( thid ) /* Free thread ID array */
free (thid);
#endif
#ifdef HDFS
if ( hdfs_handle )
dlclose ( hdfs_handle );
#endif
for ( i = 0 ; i < no ; i++ ) /* Free memory */
switch ( etab[i].type ) {
case 'e': /* ...(e) allocated in etabadd */
if ( i == etab[i].parent ) { /* the "parent" thread ... */
free( etab[i].src ); /* ... free .src */
if ( etab[i].ps ) /* ps but not multi */
MutexDestroy(&etab[i].pmutex);
if (i == 0) /* fix jira-2029, the groupt mutex is only set in etab[0] */
MutexDestroy(&etab[i].gmutex);
}
break;
case 'l': /* ...(l) allocated in etabadd */
if ( etab[i].ps ) {
MutexDestroy(&etab[i].pmutex);
CondDestroy ( &etab[i].pcvp );
CondDestroy ( &etab[i].pcvc );
}
break;
case 'd': /* ...(d) allocated in etabadd */
if ( etab[etab[i].k].em ) { /* only grand parent frees .key */
for ( j = 0 ; j < etab[etab[i].k].pc ; j++ )
free(etab[etab[i].k].key[j]);
etab[etab[i].k].em = 0 ;
if (i == 0) /* fix jira-2029, the groupt mutex is only set in etab[0] */
MutexDestroy(&etab[i].gmutex);
}
if ( etab[etab[i].k].src ) { /* only grand parent frees .src */
free(etab[etab[i].k].src);
etab[etab[i].k].src = 0 ;
if (i == 0) /* fix jira-2029, the groupt mutex is only set in etab[0] */
MutexDestroy(&etab[i].gmutex);
}
break;
case 'D': /* ...(D) allocated in etabadd */
MutexDestroy( &etab[i].pmutex );
CondDestroy ( &etab[i].pcvp );
CondDestroy ( &etab[i].pcvc );
break;
case 'c': /* ...(c) allocated in etabadd */
if ( i == (int)etab[i].k ) { /* grand parent */
if ( etab[i].sbl )
free ( etab[i].sb );
free ( etab[i].src );
MutexDestroy( &etab[i].pmutex );
if (i == 0) /* fix jira-2029, the groupt mutex is only set in etab[0] */
MutexDestroy(&etab[i].gmutex);
CondDestroy ( &etab[i].pcvp );
CondDestroy ( &etab[i].pcvc );
}
break;
case 'F': /* ...(F) allocated during -S/-P */
free(etab[i].run);
/* FALLTHRU */
case 'f':
case 'I':
if ( etab[i].ns != ns ) /* nullstr allocated */
free ( etab[i].ns );
break;
}
if ( etab ) /* Free etab array */
free ( etab );
for ( i = 0 ; i < tn ; i++ ) { /* Free vars chains */
for ( p = thps[i].tva ; p && p->next ; p = p->next ) ;
while ( p ) {
free ( p->value );
p = p->prev;
if ( p )
free ( p->next );
}
}
(void)SQLFreeHandle(SQL_HANDLE_ENV, Oenv);
for (int i = 0; i < nGlobalPointers; ++i) /* free globalPointers buffer */
{
free(globalPointers[i]);
}
free(globalPointers);
}
/* cancel: executed in interactive mode when ^C is pressed:
* - send a SQLCancel to the statement handle thps[0].Os
*
* sig: received signal
*
* return: void
*/
static void cancel(int sig)
{
printf("odb canceling statement... ");
fflush(stdout);
if ( thps[0].Oc )
if (!SQL_SUCCEEDED(Oret=SQLCancel(thps[0].Os)))
Oerr(0, 0, __LINE__, thps[0].Os, SQL_HANDLE_STMT);
printf("%s", prompt);
fflush(stdout);
(void)signal(sig, cancel); /* reset SIGINT handling to cancel() */
}
/* Otinfo:
* print table DDL
*
* Oca: Catalog name
* Osc: Schema name
* Ota: Object name
* ddl: 0 = column output, 1 = ddl output, 2 = ddl output with CHAR/VARCHAR x 4
*
* return: void
*/
static void Otinfo(SQLCHAR *Oca, SQLCHAR *Osc, SQLCHAR *Ota, char ddl)
{
int mc=6, /* Max Column Name size */
mt=4, /* Max Type_Name column size */
md=7, /* Max Default Value column size */
mi=5, /* Max Index column size */
i=0, /* loop variable */
j=0; /* loop variable */
struct tstruct {
char cname[MAXCOL_LEN]; /* Column name */
char ctype[64]; /* Column type */
char cdef[64]; /* Default Value */
char cnull[4]; /* Nullable (YES/NO) */
char kseq[4]; /* Index type (PRI/UNI/MUL)*/
SQLSMALLINT Otype; /* ODBC data type */
struct tstruct *prev; /* pointer to the prev structure */
struct tstruct *next; /* pointer to the next structure */
} *tdef=0, *p=0, *pp=0; /* Table Structure First/Current/Previous Element pointers */
char head[128]; /* table header/footer */
SQLHSTMT Ostmt; /* Statement Handle. Otinfo may be called from Olist() within
a loop based on the thps[0].Os statement handle. So we need
"our own" statement handle in Otinfo() */
/* Allocate statement handle */
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT,
thps[0].Oc, &Ostmt))){
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
return;
}
/* Execute SQLColumns to get object list */
if (!SQL_SUCCEEDED(Oret=SQLColumns(Ostmt,
Oca[0] ? Oca : f & 04000000000 ? NULL : (SQLCHAR *)"",
Oca[0] ? SQL_NTS : 0,
Osc[0] ? Osc : (SQLCHAR *)"", SQL_NTS,
Ota[0] ? Ota : (SQLCHAR *)"", SQL_NTS,
NULL, 0))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
/* Bind result set:
field 4: COLUMN_NAME VARCHAR -> Obuf[0]
field 5: DATA_TYPE SMALLINT -> Osio[0]
field 6: TYPE_NAME VARCHAR -> Obuf[1]
field 7: COLUMN_SIZE INTEGER -> Odps
field 9: DECIMAL_DIGITS SMALLINT -> Osio[1]
field 11: NULLABLE SMALLINT -> Osio[2]
field 13: COLUMN_DEF VARCHAR -> Obuf[2]
*/
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 4, SQL_C_CHAR,
Obuf[0], (SQLLEN)sizeof(Obuf[0]), &Olen[0]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 5, SQL_C_SSHORT,
&Osio[0], (SQLLEN)sizeof(Osio[0]), &Olen[1]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 6, SQL_C_CHAR,
Obuf[1], (SQLLEN)sizeof(Obuf[1]), &Olen[2]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 7, SQL_C_SLONG,
&Odps, (SQLLEN)sizeof(Odps), &Olen[3]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 9, SQL_C_SSHORT,
&Osio[1], (SQLLEN)sizeof(Osio[1]), &Olen[4]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 11, SQL_C_SSHORT,
&Osio[2], (SQLLEN)sizeof(Osio[2]), &Olen[5]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 13, SQL_C_CHAR,
Obuf[2], (SQLLEN)sizeof(Obuf[2]), &Olen[6]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
exstat = EX_ODBCERR ;
goto otinfo_exit;
}
/* Fill tdef structure with basic table info */
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))) {
if ( ( pp = malloc ( sizeof(struct tstruct) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Otinfo(%d)] - Error allocating tstruct element\n", __LINE__);
goto otinfo_exit;
}
if ( i++ ) {
pp->prev = p;
p->next = pp;
} else {
tdef = pp;
tdef->prev = (struct tstruct *)NULL;
}
p = pp;
p->kseq[0] = '\0';
p->Otype = Osio[0] ; /* save ODBC data type */
strmcpy(p->cname, (char *)Obuf[0], sizeof(p->cname));
if ( (int)Olen[0] > mc )
mc = (int)Olen[0];
switch (Osio[0]) {
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
if ( ddl == 2 )
Odps *= mult ;
snprintf(p->ctype, sizeof(p->ctype), "%s(%d)",
(char *)Obuf[1], (int)Odps);
break ;
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
if ( ddl == 2 )
Odps *= wmult ;
snprintf(p->ctype, sizeof(p->ctype), "%s(%d)",
(char *)Obuf[1], (int)Odps);
break ;
case SQL_FLOAT:
snprintf(p->ctype, sizeof(p->ctype), "%s(%d)",
(char *)Obuf[1], (int)Odps);
break;
case SQL_NUMERIC:
case SQL_DECIMAL:
snprintf(p->ctype, sizeof(p->ctype), "%s(%d,%d)",
(char *)Obuf[1], (int)Odps, (int)Osio[1]);
break;
case SQL_TIME:
case SQL_TIMESTAMP:
case SQL_TYPE_TIME:
case SQL_TYPE_TIMESTAMP:
snprintf(p->ctype, sizeof(p->ctype), "%s(%d)",
(char *)Obuf[1], (int)Osio[1]);
break;
default:
strmcpy(p->ctype, (char *)Obuf[1], sizeof(p->ctype));
break;
}
j = (int)strlen(p->ctype);
if ( j > mt )
mt = j;
strmcpy(p->cdef, (char *)Obuf[2], sizeof(p->cdef));
Obuf[2][0] = '\0'; /* clean Default buffer */
if ( (int)Olen[6] > md )
md = (int)Olen[6];
strmcpy(p->cnull, Osio[2] ? "YES" : "NO", sizeof(p->cnull));
}
if ( !i ) {
fprintf(stderr, "odb [Otinfo(%d)] - Cannot access %s.%s.%s\n", __LINE__, Oca, Osc, Ota);
goto otinfo_exit;
}
p->next = (struct tstruct *)NULL;
/* Clean statement handle */
(void)SQLFreeStmt(Ostmt, SQL_CLOSE);
(void)SQLFreeStmt(Ostmt, SQL_UNBIND);
/* Execute SQLPrimaryKeys */
if (!SQL_SUCCEEDED(Oret=SQLPrimaryKeys(Ostmt,
Oca[0] ? Oca : (SQLCHAR *)NULL, SQL_NTS,
Osc[0] ? Osc : (SQLCHAR *)NULL, SQL_NTS,
Ota[0] ? Ota : (SQLCHAR *)NULL, SQL_NTS))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto otinfo_exit;
}
/* Bind result set:
field 4: COLUMN_NAME VARCHAR -> Obuf[0]
field 5: KEY_SEQ SMALLINT -> Osio[0]
*/
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 4, SQL_C_CHAR,
Obuf[0], sizeof(Obuf[0]), &Olen[0]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto otinfo_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 5, SQL_C_SSHORT,
&Osio[0], sizeof(Osio[0]), &Olen[1]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto otinfo_exit;
}
/* Fill tdef structure with PK info */
Obuf[1][0] = '\0';
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))) {
for ( p = tdef; p ; p = p->next ) {
if ( !strmicmp(p->cname, (char *)Obuf[0], 0) ) {
snprintf(p->kseq, sizeof(p->kseq),"%d", (int)Osio[0]);
if ( Obuf[1][0] )
strmcat ( (char *)Obuf[1], ",", sizeof(Obuf[1]), 0);
strmcat ( (char *)Obuf[1], (char *)Obuf[0], sizeof(Obuf[1]), 0);
break;
}
}
}
/* Print CREATE TABLE stmt if ddl = 1 */
if ( ddl ) {
mfprintf(etab[0].fo, etab[0].fso, "%s %s%s%s%s\"%s\" (\n",
islower(Ota[0]) ? "create table" : "CREATE TABLE",
Oca[0] ? (char *)Oca : "", Oca[0] ? "." : "",
Osc[0] ? (char *)Osc : "", Osc[0] ? "." : "", (char *)Ota);
} else { /* Print Table Structure Header*/
for ( i = 1, head[0] = '+' ; i <= mc ; head[i++]='-');
for ( j = 0, head[i++] = '+' ; j < mt ; j++, head[i++]='-');
for ( j = 0, head[i++] = '+' ; j < 29 ; j++, head[i++]='-');
for ( j = 0, head[i++] = '+' ; j < 4 ; j++, head[i++]='-');
for ( j = 0, head[i++] = '+' ; j < md ; j++, head[i++]='-');
for ( j = 0, head[i++] = '+' ; j < mi ; j++, head[i++]='-');
head[i++] = '+';
head[i] = '\0';
mfprintf(etab[0].fo, etab[0].fso, "Describing: ");
if ( Oca[0] )
mfprintf(etab[0].fo, etab[0].fso, "%s.", Oca);
if ( Osc[0] )
mfprintf(etab[0].fo, etab[0].fso, "%s.", Osc);
mfprintf(etab[0].fo, etab[0].fso, "%s\n", Ota);
}
for ( i = 0, p = tdef; p; p = p->next ) {
if ( ddl ) {
mfprintf(etab[0].fo, etab[0].fso, "\t%c%s %s%s%s%s\n",
(i++) ? ',' : ' ', p->cname, p->ctype,
p->cnull[0] == 'N' ? ( islower ( Ota[0] ) ? " not null" : " NOT NULL" ) : "",
p->cdef[0] ? ( islower ( Ota[0] ) ? " default " : " DEFAULT " ) : "",
p->cdef[0] ? p->cdef : "");
} else {
if ( i++ == 0 ) {
mfprintf(etab[0].fo, etab[0].fso, "%s\n|%-*s|%-*s|%-29s|NULL|%-*s|%-*s|\n%s\n",
head, mc,"COLUMN", mt, "TYPE","ODBC DATA TYPE", md, "DEFAULT", mi, "PKEY", head);
}
mfprintf(etab[0].fo, etab[0].fso, "|%-*s|%-*s|%-29s|%-4s|%-*s|%-*s|\n",
mc, p->cname, mt, p->ctype, expandtype(p->Otype),p->cnull, md, p->cdef, mi, p->kseq);
}
}
if ( ddl ) {
if ( Obuf[1][0] )
mfprintf(etab[0].fo, etab[0].fso, "\t,%s (%s)\n",
islower ( Ota[0] ) ? "primary key" : "PRIMARY KEY", Obuf[1]);
mfprintf(etab[0].fo, etab[0].fso, ");\n");
} else {
mfprintf(etab[0].fo, etab[0].fso, "%s\n\n", head);
}
otinfo_exit:
/* free tdef memory */
for ( p = tdef; p && p->next ; p = p->next );
while ( p ) {
if ( p->prev ) {
p = p->prev;
free ( p->next );
} else {
free ( p );
break;
}
}
/* Free statement handle */
(void)SQLFreeHandle(SQL_HANDLE_STMT, Ostmt);
}
/* Olist:
* perform actions (default print) on schema objects
*
* Oca: Catalog name | SQL_ALL_CATALOGS
* Osc: Schema name | SQL_ALL_SCHEMAS
* Ota: object name
* otype: see struct Otypes[] (type field)
*
* return: void
*/
static void Olist(SQLCHAR *Oca, SQLCHAR *Osc, SQLCHAR *Ota, char otype)
{
SQLCHAR *Otype = 0, /* ODBC Object Type */
Ostn[3][MAXOBJ_LEN]; /* ODBC to save schema/table/type name */
SQLLEN Ostnl[3]; /* ODBC schema/table name/type length */
register unsigned int i=0; /* loop variable */
char so[MAXOBJ_LEN * 2]; /* contains schema.object */
SQLRETURN Or=0; /* ODBC return value */
SQLHSTMT Ostmt; /* Statement Handle */
/* if nocatnull is set */
if ( Oca && Oca[0] == '\0' && f & 04000000000 )
Oca = NULL ;
/* Allocate statement handle */
if (!SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT,
thps[0].Oc, &Ostmt))){
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
return;
}
/* Define the object type */
if ( otype != 'A' ) {
for ( i = 0 ; i < sizeof(Otypes)/sizeof(struct Otype) ; i++ )
if ( otype == Otypes[i].type )
Otype = Otypes[i].Otype;
if ( !Otype ) { /* type not found */
fprintf(stderr, "odb [Olist(%d)] - Invalid object type >%c<\n",
__LINE__, otype);
return;
}
}
if ( otype == 's' || otype == 'c' ) { /* List schemas / catalogs */
if (!SQL_SUCCEEDED(Oret=SQLTables(Ostmt,
Oca, SQL_NTS,
Osc, SQL_NTS,
(SQLCHAR *)"", 0,
(SQLCHAR *)"", 0))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto olist_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) (Oca[0]?1:2),
SQL_C_CHAR, Ostn[0], (SQLLEN)MAXOBJ_LEN, &Ostnl[0]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto olist_exit;
}
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))) {
if ( nc > 1 ) {
mfprintf(etab[0].fo, etab[0].fso, "%-*s", cs, (char *)Ostn[0]);
if ( ! ( ++i % nc ) ) {
mfputc(etab[0].fo, etab[0].fso, '\n');
}
} else {
mfprintf(etab[0].fo, etab[0].fso, "%s\n", (char *)Ostn[0]);
}
}
if ( nc > 1 && i % nc ) {
mfputc(etab[0].fo, etab[0].fso, '\n');
}
} else {
if (!SQL_SUCCEEDED(Oret=SQLTables(Ostmt,
Oca, SQL_NTS,
Osc, SQL_NTS,
Ota, SQL_NTS,
otype == 'A' ? Oaot : Otype , SQL_NTS ))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto olist_exit;
}
/* Bind schema name */
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 2,
SQL_C_CHAR, Ostn[0], (SQLLEN)sizeof(Ostn[0]), &Ostnl[0]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto olist_exit;
}
/* Bind object name */
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 3,
SQL_C_CHAR, Ostn[1], (SQLLEN)sizeof(Ostn[1]), &Ostnl[1]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto olist_exit;
}
/* Bind object type */
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 4,
SQL_C_CHAR, Ostn[2], (SQLLEN)sizeof(Ostn[2]), &Ostnl[2]))) {
Oerr(0, 0, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto olist_exit;
}
if ( otype == 'T' ) {
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))){
Otinfo(Oca?Oca:(SQLCHAR *)"", Ostn[0], Ostn[1], 0);
}
} else if ( otype == 'D' ) {
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))){
Otinfo(Oca?Oca:(SQLCHAR *)"", Ostn[0], Ostn[1], 1);
}
} else if ( otype == 'U' ) {
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))){
Otinfo(Oca?Oca:(SQLCHAR *)"", Ostn[0], Ostn[1], 2);
}
} else if ( otype == 'A' ) {
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))) {
if ( Ostnl[0] ) /* if we have a schema name */
mfprintf(etab[0].fo, etab[0].fso, "%s %s.%s\n", (char *)Ostn[2], (char *)Ostn[0], (char *)Ostn[1]);
else
mfprintf(etab[0].fo, etab[0].fso, "%s %s\n", (char *)Ostn[2], (char *)Ostn[1]);
}
} else {
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))) {
snprintf(so, sizeof(so), "%s.%s", Ostn[0], Ostn[1]);
if ( nc > 1 ) {
mfprintf(etab[0].fo, etab[0].fso, "%-*s", cs, so);
if ( ! ( ++i % nc ) ) {
mfputc(etab[0].fo, etab[0].fso, '\n');
}
} else {
mfprintf(etab[0].fo, etab[0].fso, "%s\n", so);
}
}
if ( nc > 1 && i % nc )
fputc('\n', etab[0].fo);
}
}
olist_exit:
/* Clean statement handle */
(void)SQLFreeHandle(SQL_HANDLE_STMT, Ostmt);
}
/* Odinfo:
* print odb version, data source, driver and database information
*
* return: void
*/
static void Odinfo()
{
int k = 0; /* loop variable */
SQLUINTEGER Ops = 0; /* Connection Packet Size */
mfprintf (etab[0].fo, etab[0].fso, "\t[%s]\n\tBuild: %s [%s %s]\n", odbid, ODBBLD, __DATE__, __TIME__);
for (k=0; k< (int)(sizeof(Oinfo)/sizeof(struct info)); k++) {
if (SQL_SUCCEEDED(Oret=SQLGetInfo(thps[0].Oc, Oinfo[k].Oid,
(SQLPOINTER)Obuf[0], (SQLSMALLINT)sizeof(Obuf[0]), NULL))) {
mfprintf(etab[0].fo, etab[0].fso, "\t%-45s: %s\n", Oinfo[k].desc, (char *)Obuf[0]);
if(Oret == SQL_SUCCESS_WITH_INFO)
fprintf(stderr, "odb [Odinfo(%d)] - Warning data truncation\n", __LINE__);
}
if(!SQL_SUCCEEDED(Oret))
Oerr(0, 0, __LINE__, thps[0].Oc, SQL_HANDLE_DBC);
}
if (SQL_SUCCEEDED(Oret=SQLGetConnectAttr(thps[0].Oc, SQL_ATTR_PACKET_SIZE,
(SQLPOINTER)&Ops, SQL_IS_UINTEGER, 0))) {
mfprintf(etab[0].fo, etab[0].fso, "\t%-45s: %u\n", "Connection Packet Size (SQL_ATTR_PACKET_SIZE)", (unsigned int)Ops);
}
}
/* etabadd:
* add a new entry in the etab[] array allocating space in block of
* ETAB_CHUNK if needed.
*
* type (I): field to add to etab[].type;
* run (I): field to add to etab[].run;
* id (I): field to add to etab[].id;
*
* return: void
*/
static void etabadd(char type, char *run, int id)
{
size_t ll = 0, /* run line length */
lsql = 0,/* SQL file (@) length */
len = 0; /* fread size */
unsigned int j = 0, /* loop variable */
l = 0, /* loop variable */
n = 0, /* loop variable */
o = 0, /* loop variable */
i = 0, /* loop variable */
ldbt = 0, /* local dbt */
bl = 0, /* file name length befor writing multi file no */
np = 0, /* no parse via SQLTable() flag: 0=parse, 1=no_parse */
ret = 0; /* parseopt return value */
int or = no; /* Original etab[] entry (used fo replications) */
SQLHSTMT Os=0, /* ODBC Statement handle to analyze source table(s) */
Os1=0; /* ODBC statement handle to get table partitions */
SQLCHAR Ostn[3][MAXOBJ_LEN]; /* ODBC to save schema/table/type name */
SQLLEN Ostnl[4];/* ODBC schema/table name/type & other lengths */
SQLCHAR Omn[64];/* ODBC to store min splitby as string */
SQLCHAR Omx[64];/* ODBC to store max splitby as string */
long sbmin=0; /* splitby min value */
long sbmax=0; /* splitby max value */
long d = 0; /* splitby delta */
char buff[256]; /* generic buffer */
char buff2[16]; /* generic buffer */
char tabn[512]; /* buffer containing table name to expand */
struct tm *dt; /* used to generate date and time strings */
time_t pt; /* used to generate date and time strings */
FILE *fl = 0; /* List of tables file pointer */
FILE *fsql = 0; /* to read file containing custom SQL */
struct tdesc to = { 0 }; /* used to sort etab[].td[] structure array based on order-by cols */
struct execute edef = { 0 };/* default value structure for input file based etab entries */
etabnew ( -1 );
etab[no].type = type;
etab[no].id = id;
strcpy(etab[no].loadcmd, "IN\0");
if (type == 'l' || type == 'e' || type == 'c' || type == 'd' || type == 'p') {
etab[no].flg |= 016000000400; /* print mark every rowset, print diff ICD */
etab[no].tbe = 1;
if ( !etab[no].buffsz ) /* set default buffer size */
etab[no].buffsz = RWBUFF_LEN ;
etab[no].flg2 |= 0200000; /* bind = auto */
edef = etab[no]; /* save default values */
/* Parse command line parameters */
if ( ( ret = parseopt ( type, run, (unsigned int)strlen(run), 0 ) ) == 1 ) {
goto etabadd_exit;
}
/* Check for mandatory arguments */
switch ( etab[no].type ) {
case 'l':
if ( etab[no].flg2 & 0020 ) { /* "show" mode */
etab[no].r = 1; /* rowset = 1 */
etab[no].ps = 0; /* parallel streams = 0 */
etab[no].flg &= ~0002; /* truncate off */
etab[no].flg &= ~0200000; /* ifempty off */
}
if ( !etab[no].src ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'src\' operator\n", __LINE__ );
goto etabadd_exit;
}
if ( !etab[no].tgt ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'tgt\' operator\n", __LINE__ );
goto etabadd_exit;
}
else
{
/* Allocate new buf to save Catalog/Schema/Table */
char *ptrCST = (char *)malloc(strlen(etab[no].tgt) + 1);
strcpy(ptrCST, etab[no].tgt);
addGlobalPointer(ptrCST);
/* Split Catalog/Schema/Table */
splitcso(ptrCST, etab[no].Ocso, 1);
if (f & 04000000000) /* no catalog as null */
etab[no].Ocso[0] = '\0';
}
if (etab[no].k && (etab[no].mcfs || etab[no].mcrs)) {
fprintf(stderr, "odb [etabadd(%d)] - Error: neither 'mcfs' nor 'mcrs' support skip lines\n", __LINE__);
goto etabadd_exit;
}
if ( etab[no].flg2 & 0400000000 ) { /* user defined iobuff */
if ( !etab[no].iobuff ) { /* iobuff set to zero */
fprintf(stderr, "odb [etabadd(%d)] - Error: cannot set iobuff to zero for loads\n", __LINE__ );
goto etabadd_exit;
}
}
break;
case 'e':
if ( !etab[no].src && !etab[no].sql ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'src\' or \'sql\' operators\n", __LINE__);
goto etabadd_exit;
}
if ( !etab[no].tgt ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'tgt\' operator\n", __LINE__ );
goto etabadd_exit;
}
if ( ( etab[no].flg2 ) & 04000000 && ( etab[no].flg & 01000 ) ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: \'xml\' and \'multi\' operators are not compatible\n", __LINE__ );
goto etabadd_exit;
}
if ( ( etab[no].flg & 010000000 ) && ( etab[no].sql || etab[no].cols ) ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: \'ucs2toutf8\' is available only for full table extracts\n", __LINE__ );
goto etabadd_exit;
}
break;
case 'c':
if ( etab[no].flg & 01000000000 ) { /* if cast... */
etab[no].flg2 &= ~0200000; /* ...auto-bind off */
etab[no].flg2 |= 04000; /* ...char-bind on */
}
if ( !etab[no].tgt ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'tgt\' operator\n", __LINE__ );
goto etabadd_exit;
}
if ( etab[no].roe ) {
if ( etab[no].flg & 0004 ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: you cannot use both \'soe\' and \'roe\' operators\n", __LINE__ );
goto etabadd_exit;
} else if ( etab[no].cmt != (-1) ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: you can use \'roe\' operator only with \'commit=end\'\n", __LINE__ );
goto etabadd_exit;
} else if ( etab[no].nl != 1 ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: you can use \'roe\' operator only with \'loaders=1\'\n", __LINE__ );
goto etabadd_exit;
}
}
if ( ( etab[no].flg & 010000000 ) && ( etab[no].sql || etab[no].cols ) ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: \'ucs2toutf8\' is available only for full table copies\n", __LINE__ );
goto etabadd_exit;
}
break;
case 'p':
if ( !etab[no].ns ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'tgtsql\' operator\n", __LINE__ );
goto etabadd_exit;
}
break;
case 'd':
if ( !etab[no].src ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'src\' operator\n", __LINE__ );
goto etabadd_exit;
}
if ( !etab[no].tgt ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'tgt\' operator\n", __LINE__ );
goto etabadd_exit;
}
if ( etab[no].flg2 & 0010 ) /* quick mode */
etab[no].flg &= ~010000000000; /* do not consider changes */
if ( etab[no].sb ) {
if ( etab[no].ps <= 1 )
fprintf(stderr, "odb [etabadd(%d)] - Warning: \'splitby\' won't be used'\n", __LINE__ );
} else {
if ( etab[no].ps > 1 ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: missing \'splitby\' operator with \'parallel\'\n", __LINE__ );
goto etabadd_exit;
}
}
}
#ifdef HDFS
/* Load libhdfs, get function pointers and connect to HDFS */
if ( etab[no].flg2 & 0100 ) {
if ( etab[no].flg2 & 01000000000 ) {
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [etabadd(%d)]: Loading libMapRClient functions... ", __LINE__);
if ( (hdfs_handle = dlopen("libMapRClient.so", RTLD_LAZY )) == (void *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading libMapRClient.so: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
} else {
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [etabadd(%d)]: Loading libhdfs functions... ", __LINE__);
if ( (hdfs_handle = dlopen("libhdfs.so", RTLD_NOW )) == (void *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading libhdfs.so: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
}
if ( ( *(void **) (&hdfsconn) = dlsym(hdfs_handle, "hdfsConnectAsUser" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading hdfsConnectAsUser symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&hdfsopen) = dlsym(hdfs_handle, "hdfsOpenFile" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading hdfsOpenFile symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&hdfsclose) = dlsym(hdfs_handle, "hdfsCloseFile" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading hdfsCloseFile symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&hdfsseek) = dlsym(hdfs_handle, "hdfsSeek" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading hdfsSeek symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&hdfswrite) = dlsym(hdfs_handle, "hdfsWrite" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading hdfsWrite symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&hdfsread) = dlsym(hdfs_handle, "hdfsRead" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading hdfsRead symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "done.\n");
/* hdfsConnectAsUser parameters:
- host A string containing either a host name, or an ip address of the namenode of a hdfs cluster.s
'host' should be passed as NULL if you want to connect to local filesystem.
'host' should be passed as 'default' (and port as 0) to used the 'configured' filesystem (core-site/core-default.xml).
*/
if ( ( hfs = (*hdfsconn)(
etab[no].hdfshost ? etab[no].hdfshost : "default",
etab[no].hdfsport ? etab[no].hdfsport : 0,
etab[no].hdfsuser ? etab[no].hdfsuser : NULL) ) == (hdfsFS)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error connecting to HDFS (host=%s port=%u, user=%s)\n",
__LINE__,
etab[no].hdfshost ? etab[no].hdfshost : "default",
etab[no].hdfsport ? (unsigned int)etab[no].hdfsport : 0,
etab[no].hdfsuser ? etab[no].hdfsuser : "NULL" );
goto etabadd_exit;
}
}
#endif
#ifdef XML
/* Load libxml2 and get function pointers */
if ( etab[no].xrt ) {
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [etabadd(%d)]: Loading libxml2 functions... ", __LINE__);
if ( (xml_handle = dlopen(XML2LIBNAME, RTLD_NOW )) == (void *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading libxml2.so: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmlfile) = dlsym(xml_handle, "xmlNewTextReaderFilename" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlNewTextReaderFilename symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmlread) = dlsym(xml_handle, "xmlTextReaderRead" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlTextReaderRead symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmltype) = dlsym(xml_handle, "xmlTextReaderNodeType" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlTextReaderNodeType symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmldepth) = dlsym(xml_handle, "xmlTextReaderDepth" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlTextReaderDepth symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmllname) = dlsym(xml_handle, "xmlTextReaderLocalName" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlTextReaderLocalName symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmlnextattr) = dlsym(xml_handle, "xmlTextReaderMoveToNextAttribute" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlTextReaderMoveToNextAttribute symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmlvalue) = dlsym(xml_handle, "xmlTextReaderValue" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlTextReaderValue symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( ( *(void **) (&xmlfree) = dlsym(xml_handle, "xmlFreeTextReader" ) ) == NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error loading xmlFreeTextReader symbol: %s\n",
__LINE__, dlerror());
goto etabadd_exit;
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "done.\n");
}
#endif
/* Max fetches vs rowset check */
if ( etab[no].mr && etab[no].r > etab[no].mr ) /* if # records to fetch < rowset ... */
etab[no].r = etab[no].mr; /* make rowset = records to fetch */
/* Check src database */
if ( type != 'l' ) {
if ( !Oc ) { /* First Time Connection */
if ( ( f & 00006 ) && ( Odsn[0] || clca ) ) {
if (!SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_DBC, Oenv, &Oc))){
Oerr(-1, -1, __LINE__, Oenv, SQL_HANDLE_ENV);
goto etabadd_exit;
}
(void) bcs ( Oics, sizeof(Oics), Ouser, Opwd, Odsn, clca, 0 );
if ( Onps && !SQL_SUCCEEDED(Oret=SQLSetConnectAttr(Oc,
(SQLINTEGER)SQL_ATTR_PACKET_SIZE, (SQLPOINTER)&Onps, SQL_IS_UINTEGER ) ) )
Oerr(-1, -1, __LINE__, Oenv, SQL_HANDLE_ENV);
if (!SQL_SUCCEEDED(Oret=SQLDriverConnect(Oc, (SQLHWND) NULL,
Oics, SQL_NTS, Oocs, (SQLSMALLINT) sizeof(Oocs),
&Oocsl, (SQLUSMALLINT)SQL_DRIVER_NOPROMPT))) {
Oerr(-1, -1, __LINE__, Oc, SQL_HANDLE_DBC);
goto etabadd_exit;
}
} else {
fprintf(stderr, "odb [etabadd(%d)] - Connection parameters not available\n", __LINE__);
goto etabadd_exit;
}
ldbt = checkdb ( -1, &Oc, NULL, NULL ); /* check DB type */
}
etab[no].dbt = ldbt ;
}
/* Check if options are consistent with source database type */
if ( type == 'd' ) {
if ( ( etab[no].flg2 & 01000000 ) && etab[no].dbt != ORACLE ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: odad is set but the source database is not Oracle\n", __LINE__ );
goto etabadd_exit;
}
}
/* Manage Options */
buff[0] = '\0'; /* Initialize buff */
cimutex(&etab[no].gmutex); /* Create & Initialize Group mutex */
if ( etab[no].sql ) { /* source is SQL so... no catalog/schema/table */
if ( etab[no].sql[0] == '-' ) { /* Read list of custom SQL from file */
edef = etab[no]; /* save defaults */
edef.sql = 0; /* sql cannot be defaulted */
if ( ( fl = fopen(&etab[no].sql[1], "r") ) == (FILE *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, &etab[no].sql[1], errno, strerror(errno));
goto etabadd_exit;
}
(void) fseek(fl, 0L, SEEK_END); /* goto EOF */
ll = ftell ( fl ); /* get file size */
(void) fseek(fl, 0L, SEEK_SET); /* rewind */
/* Allocate memory to contain the whole input file */
if ( esrc ) {
fprintf(stderr, "odb [etabadd(%d)] - you can use only one src input file per odb session\n",
__LINE__);
fclose(fl);
goto etabadd_exit;
}
if ( ( esrc = malloc ( ll + 1 ) ) == (void *)NULL) {
fprintf(stderr, "odb [etabadd(%d)] - error allocating memory to read %s\n",
__LINE__, etab[no].src);
fclose(fl);
goto etabadd_exit;
}
/* read input file */
#ifdef _WIN32
len = fread ( esrc, 1, ll, fl) ;
#else
if ( ( len = fread ( esrc, 1, ll, fl) ) != ll ) {
fprintf(stderr, "odb [etabadd(%d)] - error reading %s (got " SIZET_SPEC " bytes, expected " SIZET_SPEC "): [%d] %s\n",
__LINE__, &etab[no].src[1], len, ll, errno, strerror(errno) );
fclose(fl);
goto etabadd_exit;
}
#endif
fclose ( fl ) ;
esrc[len] = '\0';
tabn[0] = '\0'; /* this to run parseopt in the next while loop */
}
do {
if ( fl ) { /* input file with list of SQLs */
etab[no] = edef;
if ( ( ret = parseopt ( type, esrc, (unsigned int)len, 1 ) ) == 2 ) { /* no more options to parse */
break;
} else if ( ret == 1 ) {
goto etabadd_exit;
}
etab[no].parent = no;
etab[no].id = no;
}
if ( etab[no].ps )
etab[no].mr /= etab[no].ps; /* each thread will get a portion of the max record to fetch */
if ( etab[no].sql[0] == '@' ) { /* Read SQL from file */
etab[no].cols = &etab[no].sql[1] ; /* save original file name */
if ( ( fsql = fopen(&etab[no].sql[1], "r") ) == (FILE *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, &etab[no].sql[1], errno, strerror(errno));
goto etabadd_exit;
}
(void) fseek(fsql, 0L, SEEK_END); /* goto EOF */
ll = ftell ( fsql ); /* get file size */
(void) fseek(fsql, 0L, SEEK_SET); /* rewind */
/* Allocate memory to contain the whole input file */
if ( ( etab[no].sql = malloc ( ll + 1 ) ) == (void *)NULL) {
fprintf(stderr, "odb [etabadd(%d)] - error allocating memory to read %s\n",
__LINE__, etab[no].sql);
fclose(fsql);
goto etabadd_exit;
}
/* read input file */
#ifdef _WIN32
lsql = fread ( etab[no].sql, 1, ll, fsql) ;
#else
if ( ( lsql = fread ( etab[no].sql, 1, ll, fsql) ) != ll ) {
fprintf(stderr, "odb [etabadd(%d)] - error reading %s (got " SIZET_SPEC " bytes, expected " SIZET_SPEC "): [%d] %s\n",
__LINE__, &etab[no].sql[1], lsql, ll, errno, strerror(errno) );
fclose(fsql);
goto etabadd_exit;
}
#endif
fclose ( fsql ) ;
etab[no].sql[lsql] = '\0';
etab[no].flg2 |= 0100000 ;
}
if ( etab[no].ns ) { /* target custom SQL */
if ( etab[no].ns[0] == '@' ) { /* tgt SQL from file */
if ( ( fsql = fopen(&etab[no].ns[1], "r") ) == (FILE *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, &etab[no].sql[1], errno, strerror(errno));
goto etabadd_exit;
}
(void) fseek(fsql, 0L, SEEK_END); /* goto EOF */
ll = ftell ( fsql ); /* get file size */
(void) fseek(fsql, 0L, SEEK_SET); /* rewind */
/* Allocate memory to contain the whole input file */
if ( ( etab[no].es = malloc ( ll + 1 ) ) == (void *)NULL) {
fprintf(stderr, "odb [etabadd(%d)] - error allocating memory to read %s\n",
__LINE__, etab[no].ns);
fclose(fsql);
goto etabadd_exit;
}
/* read input file */
#ifdef _WIN32
lsql = fread ( etab[no].es, 1, ll, fsql) ;
#else
if ( ( lsql = fread ( etab[no].es, 1, ll, fsql) ) != ll ) {
fprintf(stderr, "odb [etabadd(%d)] - error reading %s (got " SIZET_SPEC " bytes, expected " SIZET_SPEC "): [%d] %s\n",
__LINE__, &etab[no].ns[1], lsql, ll, errno, strerror(errno) );
fclose(fsql);
goto etabadd_exit;
}
#endif
fclose ( fsql ) ;
etab[no].es[lsql] = '\0';
} else {
etab[no].es = etab[no].ns ;
}
etab[no].fsl = strlen(etab[no].es);
if ( ( etab[no].tgtsql = malloc ( etab[no].fsl + 1 ) ) == (void *)NULL) {
fprintf(stderr, "odb [etabadd(%d)] - error allocating memory for tgtsql\n", __LINE__);
goto etabadd_exit;
}
/* First pass to count parameters and write tgtsql */
for ( ll = 0 ; ll < etab[no].fsl ; ll++ ) {
if ( etab[no].es[ll] == '?' && isdigit((int)etab[no].es[ll+1]) ) {
while ( isdigit((int)etab[no].es[++ll] ) );
etab[no].tgtsql[j++] = '?' ;
etab[no].ep++;
ll--;
} else {
etab[no].tgtsql[j++] = etab[no].es[ll] ;
}
}
etab[no].tgtsql[j] = '\0' ;
if ( etab[no].ep == 0 ) {
fprintf(stderr, "odb [etabadd(%d)] - no parameters in tgtsql\n", __LINE__);
goto etabadd_exit;
}
if ( ( etab[no].tgtp = calloc ( (size_t)etab[no].ep, sizeof(unsigned int) ) ) == (void *)NULL) {
fprintf(stderr, "odb [etabadd(%d)] - error allocating memory for tgtsql parameters\n", __LINE__);
goto etabadd_exit;
}
/* Second pass to get parameter positions */
for ( ll = 0, i = 0 ; ll < etab[no].fsl ; ll++ ) {
if ( etab[no].es[ll] == '?' && isdigit((int)etab[no].es[ll+1]) ) {
etab[no].tgtp[i++] = (unsigned int)strtol(&etab[no].es[ll+1], NULL, 10);
}
}
if ( etab[no].ns ) /* target custom SQL */
if ( etab[no].ns[0] == '@' ) /* tgt SQL from file */
free ( etab[no].es ) ; /* free etab[no].es, SQL already copied to etab[no].tgtsql */
}
if ( strstr(etab[no].sql, "&cds") ) { /* custom splitby */
etab[no].flg2 |= 040000 ; /* custom SQL */
} else if ( etab[no].ps ) {
if ( etab[no].dbt != ORACLE &&
etab[no].dbt != SQLSERVER ) {
fprintf(stderr, "odb [etabadd(%d)] - Error: \"parallel\" without \"&cds/&tds\" is"
" only supported by Oracle and SQL Server databases \n",
__LINE__);
goto etabadd_exit;
}
}
if ( etab[no].type == 'e' ) { /* name & create output file */
for ( i = j = 0; i < sizeof(buff) && etab[no].tgt[i]; i++ ) {
switch ( etab[no].tgt[i] ) {
case '%':
switch ( etab[no].tgt[++i] ) {
case 'd': /* date (YYYYMMDD) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%Y%m%d", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'D': /* date (YYYY-MM-DD) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%Y-%m-%d", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'm': /* time (hhmmss) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%H%M%S", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'M': /* time (hhmmss) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%H:%M:%S", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
default:
fprintf(stderr, "odb [etabadd(%d)] - Invalid %%%c in %s\n",
__LINE__, etab[no].tgt[i], etab[no].tgt );
goto etabadd_exit;
}
break;
default:
buff[j++] = etab[no].tgt[i];
break;
}
}
buff[j] = '\0';
if ( etabout ( no , buff ) ) {
goto etabadd_exit;
}
l = no;
} else if ( etab[no].type == 'c' || etab[no].type == 'p' ) {
l = etab[no].parent;
etab[l].ps = etab[no].ps;
etab[no].lstat = 4; /* other threads to wait while gpar check tgt dbt */
if ( etab[no].nloader == 0 )
etab[no].nloader = etab[no].type == 'c' ? 2 : 1; /* initialize default number of loaders */
etab[no].nltotal = etab[no].ps * etab[no].nloader ;
etab[no].k = no; /* save grand-parent copy thread (first 'c' thread for this pool) */
etab[no].run = etab[no].tgt ;
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
for ( i = 0, j = no; i < etab[j].nloader ; i++ ) {
etabnew ( no++ ); /* Create new etab entry based on original 'c'/'p' struct */
etab[no].type = etab[l].type == 'c' ? 'C' : 'P';
if ( etab[no].type == 'C' )
etab[no].mpre = tmpre ;
etab[no].id = no;
etab[no].parent = j;
etab[no - 1].child = no;
}
}
cimutex(&etab[no].pmutex);
for ( no++, j = 1 ; j < etab[l].ps ; j++ ) {
etabnew ( l );
etab[no].id = no;
etab[no].parent = l;
etab[no].sbmin = j;
if ( etab[no].flg & 01000 ) { /* if multi open the output file now */
buff[bl] = '\0';
snprintf(buff2, sizeof(buff2), ".%04d", j+1);
strmcat (buff, buff2, sizeof(buff), 0 );
if ( etabout ( no , buff ) ) {
goto etabadd_exit;
}
}
etab[no].tbe = 1;
if ( etab[no].type == 'c' || etab[no].type == 'p' ) {
etab[no].parent = no;
etab[no].iobuff = etab[no].ps * etab[no].nloader ; /* total number of loaders */
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
for ( i = 0, n = no; i < etab[j].nloader ; i++ ) {
etabnew ( no++ ); /* Create new etab entry based on original 'c'/'p' struct */
etab[no].type = etab[l].type == 'c' ? 'C' : 'P';
if ( etab[no].type == 'C' )
etab[no].mpre = tmpre ;
etab[no].id = no;
etab[no].parent = n;
etab[no - 1].child = no;
}
}
no++;
}
} while ( fl ) ;
} else { /* Source is a table pattern or a file containing table patterns */
if ( etab[no].src[0] == '-' ) { /* etab[no].src is a file containing list of tables */
edef = etab[no]; /* save defaults */
edef.src = 0; /* src cannot be defaulted */
if ( ( fl = fopen(&etab[no].src[1], "r") ) == (FILE *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, &etab[no].src[1], errno, strerror(errno));
goto etabadd_exit;
}
(void) fseek(fl, 0L, SEEK_END); /* goto EOF */
ll = ftell ( fl ); /* get file size */
(void) fseek(fl, 0L, SEEK_SET); /* rewind */
/* Allocate memory to contain the whole input file */
if ( esrc ) {
fprintf(stderr, "odb [etabadd(%d)] - you can use only one src input file per odb session\n",
__LINE__);
fclose(fl);
goto etabadd_exit;
}
if ( ( esrc = malloc ( ll + 1 ) ) == (void *)NULL) {
fprintf(stderr, "odb [etabadd(%d)] - error allocating memory to read %s\n",
__LINE__, etab[no].src);
fclose(fl);
goto etabadd_exit;
}
/* read input file */
#ifdef _WIN32
len = fread ( esrc, 1, ll, fl) ;
#else
if ( ( len = fread ( esrc, 1, ll, fl) ) != ll ) {
fprintf(stderr, "odb [etabadd(%d)] - error reading %s (got " SIZET_SPEC " bytes, expected " SIZET_SPEC "): [%d] %s\n",
__LINE__, &etab[no].src[1], len, ll, errno, strerror(errno) );
fclose(fl);
goto etabadd_exit;
}
#endif
fclose ( fl ) ;
esrc[ll] = '\0';
tabn[0] = '\0'; /* this to run parseopt in the next while loop */
} else {
strmcpy(tabn, etab[no].src, sizeof(tabn));
}
do {
if ( fl ) { /* input file with list of tables */
etab[no] = edef;
if ( etab[no].seqp ) { /* :seq option at command line level: set global sequence */
f2 |= 0002 ; /* switch global sequence flag on */
gseq = etab[no].seq ; /* save seq start value */
}
if ( ( ret = parseopt ( type, esrc, (unsigned int)len, 1 ) ) == 2 ) { /* no more options to parse */
break;
} else if ( ret == 1 ) {
goto etabadd_exit;
}
etab[no].parent = no;
etab[no].id = no;
}
if (etab[no].bad) { /* bad file */
if (etabout(no, etab[no].bad))
goto etabadd_exit;
}
if ( etab[no].type == 'l' ) { /* load job */
if ( etab[no].ps ) {
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
for ( l = no++, j = 0 ; j < etab[l].ps ; j++ ) {
etabnew ( l );
etab[no].type = 'L';
etab[no].id = no;
etab[no++].parent = l;
}
} else {
etab[no++].tbe = 1;
}
} else { /* not a load job */
etab[no].k = no; /* record grandparent for copy/diff ops */
if (etab[no].mr && etab[no].ps > 1 ){
etab[no].TotalMaxRecords = etab[no].mr;
etab[no].mr /= etab[no].ps; /* each thread will get a portion of the max record to fetch */
if (etab[0].r > etab[0].mr) /* rowset size should not exceeds the max size */
etab[0].r = etab[0].mr;
}
strmcpy(tabn, etab[no].src, sizeof(tabn));
if ( !SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT, Oc, &Os))){
Oerr(-1, -1, __LINE__, Oc, SQL_HANDLE_DBC);
goto etabadd_exit;
}
if ( !SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT, Oc, &Os1))){
Oerr(-1, -1, __LINE__, Oc, SQL_HANDLE_DBC);
goto etabadd_exit;
}
if ( ( strchr ( tabn , '%' ) ) == ( char * ) NULL ) { /* Nothing to expand in the source string */
/* Note: strchr() to be executed before splitcso() inserts NULL in tabn */
splitcso ( tabn, etab[no].Ocso, 1);
strmcpy((char *)Ostn[0], (char *)etab[no].Ocso[0], sizeof(Ostn[0]));
strmcpy((char *)Ostn[1], (char *)etab[no].Ocso[1], sizeof(Ostn[1]));
strmcpy((char *)Ostn[2], (char *)etab[no].Ocso[2], sizeof(Ostn[2]));
Ostnl[0] = (SQLLEN)strlen((char *)Ostn[0]);
Ostnl[1] = (SQLLEN)strlen((char *)Ostn[1]);
Ostnl[2] = (SQLLEN)strlen((char *)Ostn[2]);
np = 1 ;
} else {
splitcso ( tabn, etab[no].Ocso, 1);
if (!SQL_SUCCEEDED(Oret=SQLTables(Os,
etab[no].Ocso[0], SQL_NTS,
etab[no].Ocso[1], SQL_NTS,
etab[no].Ocso[2], SQL_NTS,
(SQLCHAR *)"TABLE" , SQL_NTS ))) {
Oerr(-1, -1, __LINE__, Os, SQL_HANDLE_STMT);
goto etabadd_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Os, (SQLUSMALLINT) 1,
SQL_C_CHAR, Ostn[0], (SQLLEN)sizeof(Ostn[0]), &Ostnl[0]))) {
Oerr(-1, -1, __LINE__, Os, SQL_HANDLE_STMT);
goto etabadd_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Os, (SQLUSMALLINT) 2,
SQL_C_CHAR, Ostn[1], (SQLLEN)sizeof(Ostn[1]), &Ostnl[1]))) {
Oerr(-1, -1, __LINE__, Os, SQL_HANDLE_STMT);
goto etabadd_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Os, (SQLUSMALLINT) 3,
SQL_C_CHAR, Ostn[2], (SQLLEN)sizeof(Ostn[2]), &Ostnl[2]))) {
Oerr(-1, -1, __LINE__, Os, SQL_HANDLE_STMT);
goto etabadd_exit;
}
}
for ( o = 0 ; np || SQL_SUCCEEDED(Oret=SQLFetch(Os)); o++) {
np = 0 ; /* reset no parse flag */
if ( o ) {
etabnew ( -1 );
etab[no] = etab[or];
etab[no].parent = no;
etab[no].id = no;
}
if ( Ostnl[0] < 0 )
Ostnl[0] = 0;
if ( Ostnl[1] < 0 )
Ostnl[1] = 0;
ll = (size_t)(4 + Ostnl[0] + Ostnl[1] + Ostnl[2]);
if ( ( etab[no].src = malloc ( ll ) ) == (void *)NULL ) {
fprintf(stderr,"odb [etabadd(%d)] - Error allocating memory for table name: [%d] %s\n",
__LINE__, errno, strerror(errno) );
goto etabadd_exit;
}
etab[no].src[0] = '\0';
if ( Ostnl[0] ) {
strmcat ( etab[no].src , (char *)Ostn[0] , ll , 0 );
strmcat ( etab[no].src , "." , ll , 0 );
}
if ( Ostnl[1] ) {
strmcat ( etab[no].src , (char *)Ostn[1] , ll , 0 );
strmcat ( etab[no].src , "." , ll , 0 );
}
strmcat ( etab[no].src , (char *)Ostn[2] , ll , 0 );
etab[no].Ocso[2] = &Ostn[2][0]; /* update table name pointer with expanded tab name */
memset ( buff, 0, sizeof(buff));
for ( i = j = 0; i < sizeof(buff) && etab[no].tgt[i]; i++ ) {
switch ( etab[no].tgt[i] ) {
case '%':
switch ( etab[no].tgt[++i] ) {
case 'd': /* date (YYYYMMDD) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%Y%m%d", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'D': /* date (YYYY-MM-DD) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%Y-%m-%d", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'm': /* time (hhmmss) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%H%M%S", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'M': /* time (hhmmss) */
pt = (time_t)tvi.tv_sec;
dt = localtime(&pt);
strftime(buff2, sizeof(buff2), "%H:%M:%S", dt);
j += strmcat (buff, buff2, sizeof(buff), 0 );
break;
case 'c': /* LC catalog name */
j += strmcat (buff, (char *)Ostn[0], sizeof(buff), 2 );
break;
case 'C': /* UC catalog name */
j += strmcat (buff, (char *)Ostn[0], sizeof(buff), 1 );
break;
case 's': /* LC schema name */
j += strmcat (buff, (char *)Ostn[1], sizeof(buff), 2 );
break;
case 'S': /* UC schema name */
j += strmcat (buff, (char *)Ostn[1], sizeof(buff), 1 );
break;
case 't': /* LC table name */
j += strmcat (buff, (char *)Ostn[2], sizeof(buff), 2 );
break;
case 'T': /* UC table name */
j += strmcat (buff, (char *)Ostn[2], sizeof(buff), 1 );
break;
default:
fprintf(stderr, "odb [etabadd(%d)] - Invalid %%%c in %s\n",
__LINE__, etab[no].tgt[i], etab[no].tgt );
goto etabadd_exit;
}
break;
default:
buff[j++] = etab[no].tgt[i];
break;
}
}
buff[j] = '\0';
bl = j;
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: Now Analyzing %s:", etab[no].src);
if ( etab[no].type == 'e' || etab[no].type == 'd' ) {
if ( etab[no].flg & 01000 ) /* if multi open the output file now */
strmcat(buff, ".0001", sizeof(buff), 0);
if ( etab[no].flg & 01416000000 || /* extract with cast and/or trim and/or ucs2toutf8: analyze source table */
etab[no].flg2 & 0040 || /* extract with exclude columns: analyze source table */
etab[no].type == 'd' ) { /* diff: analyze source table */
if ( etab[no].type == 'd' && !etab[no].key[0] ) { /* diff without key... use PK */
/* Get Primary key */
if (!SQL_SUCCEEDED(Oret=SQLPrimaryKeys(Os1,
( f & 04000000000 ) || etab[no].Ocso[0] ? etab[no].Ocso[0] : (SQLCHAR *)"",
etab[no].Ocso[0] ? (SQLSMALLINT) strlen((char *)etab[no].Ocso[0]) : 0,
etab[no].Ocso[1] ? etab[no].Ocso[1] : (SQLCHAR *)"",
etab[no].Ocso[1] ? (SQLSMALLINT) strlen((char *)etab[no].Ocso[1]) : 0,
etab[no].Ocso[2], (SQLSMALLINT) strlen((char *)etab[no].Ocso[2])))) {
Oerr( -1, -1, __LINE__, Os1, SQL_HANDLE_STMT);
goto etabadd_exit;
}
/* Bind result set:
field 4: COLUMN_NAME VARCHAR -> Ostn[0] -> etab[no].key[]
*/
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Os1, (SQLUSMALLINT) 4, SQL_C_CHAR,
&Ostn[0], (SQLLEN)MAXCOL_LEN, &Ostnl[0]))) {
Oerr(-1, -1, __LINE__, Os1, SQL_HANDLE_STMT);
goto etabadd_exit;
}
for ( i = 0 ; SQL_SUCCEEDED(Oret=SQLFetch(Os1)) && i < MAX_PK_COLS; i++) {
if ( ( etab[no].key[i] = malloc ( (size_t)(Ostnl[0]+1) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [etabadd(%d)] - Error allocating memory for etab[%d].key[%u]\n",
__LINE__, no, i);
goto etabadd_exit;
}
strmcpy(etab[no].key[i], (char *)Ostn[0], (size_t)Ostnl[0]);
}
if ( i == 0 ) { /* No PK elements found */
fprintf(stderr, "odb [etabadd(%d)] - You didn't provide \"key\" columns and no PK was found\n",
__LINE__);
goto etabadd_exit;
}
etab[no].pc = (unsigned char)i;
etab[no].em = 1; /* free etab[].key[] buffers */
(void)SQLFreeStmt(Os1, SQL_CLOSE);
(void)SQLFreeStmt(Os1, SQL_UNBIND);
}
/* Analyze source table */
if ( ( i = Otcol(no, &Oc) ) <= 0 ) {
fprintf(stderr, "odb [etabadd(%d)] - Error analyzing source table %s.%s.%s\n",
__LINE__, etab[no].Ocso[0], etab[no].Ocso[1], etab[no].Ocso[2]);
goto etabadd_exit;
}
etab[no].cmt = i; /* save the number of source table columns */
if ( etab[no].type == 'e' && etab[no].flg2 & 0040 ) /* extract with exclude columns */
excol(no, &Oc);
if ( etab[no].type == 'd' )
while ( i-- )
etab[no].td[i].Osize++ ;
/* Sort etab[no].td[] elements based on key column order */
for ( i = 0 ; i < etab[no].pc ; i++ ) {
for ( j = 0 ; j < (unsigned int)etab[no].cmt ; j++ ) {
if ( !strmicmp((char *)etab[no].td[j].Oname, etab[no].key[i], strlen(etab[no].key[i])) ) {
if ( i != j ) {
to = etab[no].td[i];
etab[no].td[i] = etab[no].td[j];
etab[no].td[j] = to;
}
}
}
}
/* mark order-by columns in etab[].td[] */
for ( i = 0 ; i < etab[no].pc ; i++ )
etab[no].td[i].dl = 1;
if ( etab[no].flg2 & 0010 ) /* quick diff */
etab[no].cmt = etab[no].pc; /* limit columns to order-by cols */
}
if ( etabout ( no , etab[no].type == 'd' ? etab[no].run : buff ) ) {
goto etabadd_exit;
}
if ( etab[no].type == 'd' ) {
etab[no].k = no; /* save grand-parent copy thread (first 'd' thread for this pool) */
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
etabnew ( no++ ); /* Helper 'D' thread */
etab[no].type = 'D';
etab[no].id = no;
etab[no].parent = no - 1;
etab[no].cr = 1 ; /* first 'D' thread */
etabnew ( no++ ); /* Helper 'Z' thread */
etab[no].type = 'Z';
etab[no].id = no;
etab[no].lstat |= 0003 ; /* Initial buffers status: src & tgt write available */
etab[no].parent = no - 2; /* 'Z' threads have two parents (type 'd' and type 'D' threads) */
etab[no].child = no - 1; /* 'd' parent eid is saved in .parent, 'D' parent id in .child */
etab[no - 2].child = no; /* d (father) --> Z (child) */
etab[no - 1].child = no; /* D (father) --> Z (child) */
if ( etab[no].flg & 020000000000 ) { /* 's' thread entry */
etabnew ( no++ ); /* Helper 'Z' thread */
etab[no].type = 's';
etab[no].id = no;
etab[no].parent = no - 1; /* 's' threads parents are 'Z' (compare) threads */
}
}
} else if ( etab[no].type == 'c' ) {
etab[no].lstat = 4; /* other threads to wait while gpar check tgt dbt */
if ( etab[no].nloader == 0 )
etab[no].nloader = NUMLOADERS; /* initialize number of loaders to its default */
etab[no].nltotal = etab[no].ps * etab[no].nloader ;
etab[no].k = no; /* save grand-parent copy thread (first 'c' thread for this pool) */
ll = strlen ( buff ) ;
if ( ( etab[no].run = malloc ( ll + 1 ) ) == (void *)NULL ) {
fprintf(stderr,"odb [etabadd(%d)] - Error allocating memory for target table name: [%d] %s\n",
__LINE__, errno, strerror(errno) );
goto etabadd_exit;
}
strmcpy ( etab[no].run, buff, ll );
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
if ( etab[no].flg2 & 0040 || /* exclude columns */
etab[no].flg & 01406000000 ) /* cast and/or [r]trim columns */
excol(no, &Oc);
/* ucs2toutf8 is set and we didn't analyze source table yet... */
if ( ( etab[no].flg & 010000000 ) && !etab[no].td ) {
if ( ( i = Otcol(no, &Oc) ) <= 0 ) {
fprintf(stderr, "odb [etabadd(%d)] - Error analyzing source table %s\n",
__LINE__, etab[no].src);
goto etabadd_exit;
}
}
for ( i = 0, j = no; i < etab[j].nloader ; i++ ) {
etabnew ( no++ ); /* Create new etab entry based on original 'c' struct */
etab[no].type = 'C';
etab[no].mpre = tmpre ;
etab[no].id = no;
etab[no].parent = j;
etab[no - 1].child = no;
}
}
if ( etab[no].ps ) { /* Multi-stream table analysis */
if ( etab[no].sb ) { /* split by */
if (etab[no].map) { /* if we get a pwhere condition, we apply it to improve perfomance */
snprintf((char *)Obuf[0], sizeof(Obuf[0]),
"SELECT MIN(%s), MAX(%s) FROM %s WHERE %s", etab[no].sb, etab[no].sb, etab[no].src, etab[no].map);
}
else {
snprintf((char *)Obuf[0], sizeof(Obuf[0]),
"SELECT MIN(%s), MAX(%s) FROM %s", etab[no].sb, etab[no].sb, etab[no].src);
}
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Os1, Obuf[0], SQL_NTS))) {
Oerr(-1, -1, __LINE__, Os1, SQL_HANDLE_STMT);
goto etabadd_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Os1, (SQLUSMALLINT) 1, SQL_C_CHAR,
&Omn, (SQLLEN)sizeof(Omn), &Olen[0]))) {
Oerr(-1, -1, __LINE__, Os1, SQL_HANDLE_STMT);
goto etabadd_exit;
}
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Os1, (SQLUSMALLINT) 2, SQL_C_CHAR,
&Omx, (SQLLEN)sizeof(Omx), &Olen[1]))) {
Oerr(-1, -1, __LINE__, Os1, SQL_HANDLE_STMT);
goto etabadd_exit;
}
if (!SQL_SUCCEEDED(Oret=SQLFetch(Os1))) {
Oerr(-1, -1, __LINE__, Os1, SQL_HANDLE_STMT);
goto etabadd_exit;
}
Omn[(int)Olen[0]] = '\0';
Omx[(int)Olen[1]] = '\0';
sbmin = strtol((char *)Omn, NULL, 10);
sbmax = strtol((char *)Omx, NULL, 10);
d = sbmax - sbmin;
if ( (unsigned int)d < etab[no].ps ) {
fprintf(stderr,"odb [etabadd(%d)] - Warning: you cannot use %u threads because max(%s)-min(%s)=%ld. Reducing parallelism\n",
__LINE__, etab[no].ps, etab[no].sb, etab[no].sb, d);
etab[no].ps = d ? (unsigned int)d : 1 ;
}
d /= etab[no].ps;
etab[no].sbmin = sbmin;
etab[no].sbmax = d ? ( etab[no].sbmin + d ) : ( etab[no].sbmin + 1 ) ;
if ( etab[no].type == 'C' ) {
etab[no].mpre = tmpre ;
l = etab[no].parent;
etab[l].ps = etab[no].ps;
etab[l].sbmin = etab[no].sbmin;
etab[l].sbmax = etab[no].sbmax;
} else if ( etab[no].type == 'Z' ) {
l = etab[no].parent;
etab[no-1].ps = etab[no-2].ps = etab[no].ps;
etab[no-1].sbmin = etab[no-2].sbmin = etab[no].sbmin;
etab[no-1].sbmax = etab[no-2].sbmax = etab[no].sbmax;
} else {
l = no;
}
cimutex(&etab[no].pmutex);
for ( no++, j = 1 ; j < etab[l].ps ; j++ ) {
etabnew ( l );
etab[no].id = no;
etab[no].parent = l;
etab[no].sbmin = etab[no-1].sbmax ;
etab[no].sbmax = ( j + 1 ) == etab[l].ps ? (sbmax + 1 ) : etab[no].sbmin + d ;
if ( etab[no].flg & 01000 ) { /* if multi open the output file now */
buff[bl] = '\0';
snprintf(buff2, sizeof(buff2), ".%04d", j+1);
strmcat (buff, buff2, sizeof(buff), 0 );
if ( etabout ( no , buff ) ) {
goto etabadd_exit;
}
}
etab[no].tbe = 1;
if ( etab[no].type == 'c' ) {
etab[no].parent = no;
etab[no].iobuff = etab[no].ps * etab[no].nloader ; /* total number of loaders */
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
for ( i = 0, n = no; i < etab[j].nloader ; i++ ) {
etabnew ( no++ ); /* Create new etab entry based on original 'c' struct */
etab[no].type = 'C';
etab[no].mpre = tmpre ;
etab[no].id = no;
etab[no].parent = n;
etab[no - 1].child = no;
}
} else if ( etab[no].type == 'd' ) {
etab[no].parent = no;
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
etabnew ( no++ ); /* Helper 'D' thread (extract from target) */
etab[no].type = 'D';
etab[no].id = no;
if ( ( j + 1 ) == etab[l].ps )
etab[no].cr = 2;
etab[no].parent = no - 1;
etabnew ( no++ ); /* Helper 'D' thread (extract from target) */
etab[no].type = 'Z';
etab[no].id = no;
etab[no].lstat |= 0003 ; /* Initial buffers status: src & tgt write available */
etab[no].parent = no - 2; /* 'Z' threads have two parents (type 'd' and type 'D' threads) */
etab[no].child = no - 1; /* 'd' parent eid is saved in .parent, 'D' parent id in .child */
etab[no - 2].child = no; /* d (father) --> Z (child) */
etab[no - 1].child = no; /* D (father) --> Z (child) */
if ( etab[no].flg & 020000000000 ) { /* 's' thread entry */
etabnew ( no++ ); /* Helper 'Z' thread */
etab[no].type = 's';
etab[no].id = no;
etab[no].parent = no - 1; /* 's' threads parents are 'Z' (compare) threads */
}
}
no++;
}
} else if ( etab[no].dbt == ORACLE || etab[no].dbt == SQLSERVER ) {
/* Parallel option without splitby and source=ORACLE */
/* replicate 'ps' etab entries */
if ( etab[no].type == 'C' ) {
l = etab[no].parent;
etab[no].mpre = tmpre ;
etab[l].ps = etab[no].ps;
} else {
l = no ;
}
cimutex(&etab[no].pmutex);
for ( no++, j = 1 ; j < etab[l].ps ; j++ ) {
etabnew ( l );
etab[no].id = no;
etab[no].parent = l;
etab[no].sbmin = j; /* default current thread number */
if ( etab[no].flg & 01000 ) { /* if multi open the output file now */
buff[bl] = '\0';
snprintf(buff2, sizeof(buff2), ".%04d", j+1);
strmcat (buff, buff2, sizeof(buff), 0 );
if ( etabout ( no , buff ) ) {
goto etabadd_exit;
}
}
etab[no].tbe = 1;
if ( etab[no].type == 'c' ) {
etab[no].parent = no;
cimutex(&etab[no].pmutex);
cicondvar(&etab[no].pcvp);
cicondvar(&etab[no].pcvc);
for ( i = 0, n = no; i < etab[j].nloader ; i++ ) {
etabnew ( no++ ); /* Create new etab entry based on original 'c' struct */
etab[no].type = 'C';
etab[no].mpre = tmpre ;
etab[no].id = no;
etab[no].parent = n;
etab[no - 1].child = no;
}
}
no++;
}
}
else {
/* Parallel option without splitby with unsopported DB */
fprintf(stderr, "odb [etabadd(%d)] - Error: \"parallel\" without \"splitby\" is"
" only supported by Oracle and SQL Server databases \n",
__LINE__);
goto etabadd_exit;
}
/* append the remainder to original etab */
if (etab[or].TotalMaxRecords)
etab[or].mr += etab[or].TotalMaxRecords%etab[or].ps;
(void)SQLFreeStmt(Os1, SQL_CLOSE);
(void)SQLFreeStmt(Os1, SQL_UNBIND);
} else {
no++;
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "output to %s\n", buff);
}
tabn[0] = '\0';
(void)SQLFreeStmt(Os, SQL_CLOSE);
(void)SQLFreeStmt(Os, SQL_UNBIND);
}
} while ( fl ) ;
if ( Os )
(void)SQLFreeHandle(SQL_HANDLE_STMT, Os);
if ( Os1 )
(void)SQLFreeHandle(SQL_HANDLE_STMT, Os1);
}
if ( no == or ) {
fprintf(stderr, "odb [etabadd(%d)] - No table(s) to extract for \'src=%s\'\n",
__LINE__, etab[no].src);
goto etabadd_exit;
}
} else {
etab[no].run = run;
etab[no++].tbe = 1;
}
if ( tn ) { /* tpar option activated */
if ( !fl ) {
fprintf(stderr, "odb [etabadd(%d)] - No table list: tpar ignored\n", __LINE__);
tn = 0 ;
} else {
switch ( type ) {
case 'l':
tn = tn * ( etab[no].ps + 1 ) ;
break;
case 'c':
case 'p':
if ( etab[no].ps )
tn = tn * etab[no].ps * ( etab[etab[no].parent].nl + 1 ) ;
else
tn = tn * ( etab[etab[no].parent].nl + 1 ) ;
break;
case 'd':
if ( etab[no].ps )
tn = tn * etab[no].ps * 3 ;
else
tn = tn * 3 ;
break;
case 'e':
if ( etab[no].ps )
tn = tn * etab[no].ps * 2 ;
else
tn = tn * 2 ;
break;
}
}
}
return;
etabadd_exit: /* something was wrong.... clean and return */
if ( esrc )
free ( esrc ) ;
no = tn = 0;
return;
}
/* etabnew:
* initialize - and allocate if needed - a new etab[] structure element.
* etab entries are allocated in chunks of ETAB_CHUNK.
*
* n(I): existing structure id to copy into the new one (-1 start from scratch)
*
* return: void
*/
static void etabnew(int ix)
{
struct execute zero = { 0 } ;
if ( no >= ( nae * ETAB_CHUNK - 2 ) ) { /* need new memory */
if ((etab=realloc(etab, ++nae*ETAB_CHUNK*sizeof(struct execute)))==(void *)NULL) {
fprintf(stderr, "odb [etabnew(%d)] - Error allocating %dnth block of etab[] memory: [%d] %s\n",
__LINE__, nae, errno, strerror(errno));
exit( EX_OSERR );
}
}
if ( ix < 0 ) {
etab[no] = zero;
etab[no].r = r;
etab[no].fo = stdout;
etab[no].parent = no;
etab[no].fsl = 1;
etab[no].bpwc = BYTESPWCHAR ;
etab[no].bpc = BYTESPCHAR ;
etab[no].buffsz = RWBUFF_LEN ;
} else {
etab[no] = etab[ix];
}
}
/* etabshuffle:
* randomize etab[] array .run elements. This function SHOULD NOT be used
* in a multi-threaded environment. Is it safe to use it with:
* - with serial runs: always
* - with multi-thread tests to shuffle etab[] BEFORE threads are created
*
* return: void
*/
static void etabshuffle()
{
int i=0, /* loop variable */
j=0; /* loop variable */
char *save; /* used as a temp buffer */
srand((unsigned int)time(0));
if ( no ) { /* more than one element in etab[] */
for ( i = 0; i < no - 1 ; i++ ) {
if ( etab[i].type != 'q' ) { /* etab[] type 'q' entries are linked to qtab[] */
j = (int) ( i + rand() / ( RAND_MAX + 1.0 ) * ( no - i ) );
save = etab[j].run;
etab[j].run = etab[i].run;
etab[i].run = save;
}
}
}
}
/* etabout:
* Open execution output files
*
* eid(I): Execution ID
*
* return: 0 if everything was ok ; -1 in case of errors
*/
static int etabout ( int eid , char *file )
{
if ( !(etab[eid].flg2 & 0100) ) { /* NOT HDFS output file */
if ( !file || !strcmp( file, "stdout" ) ) {
if ( etab[no].flg & 01000 ) {
fprintf(stderr, "odb [etabout(%d)] - Error setting stdout if multi is enabled\n", __LINE__);
return ( -1 );
}
etab[eid].fo = stdout;
} else {
if ((etab[eid].fo=fopen((file[0]=='+'?&file[1]:file), (file[0]=='+'?"a":"w")))==(FILE *)NULL) {
fprintf(stderr,"odb [etabout(%d)] - Error opening out file %s: [%d] %s\n",
__LINE__, file, errno, strerror(errno) );
return (-1);
}
if ( etab[eid].flg2 & 0400000000 ) { /* user defined iobuff */
if ( !(etab[eid].flg2 & 0100) ) { /* no HDFS output file */
if ( setvbuf( etab[eid].fo, (char *)NULL, _IOFBF, (size_t)etab[eid].iobuff) ) /* setting IO buff size */
fprintf(stderr,"odb [etabout(%d)] - Error setting IO buff size to %zu : [%d] %s\n",
__LINE__, etab[eid].iobuff, errno, strerror(errno) );
}
}
}
#ifdef HDFS
} else { /* HDFS output file */
if ( ( etab[no].fho = (*hdfsopen)(hfs, file, O_WRONLY|O_CREAT, (int)etab[no].iobuff, 0, etab[no].hblock) ) == (hdfsFile) NULL ) {
fprintf(stderr, "odb [etabout(%d)] - Error opening hdfs file %s\n",
__LINE__, file);
return (-1);
}
#endif
}
return ( 0 );
}
/* cimutex:
* Create and Initialize a mutex object
* - mutexp: pointer to a mutex object
*
* return: void
*/
static void cimutex(Mutex *mutexp)
{
#ifdef _WIN32
InitializeCriticalSection(mutexp);
#else
if ( pthread_mutex_init(mutexp, NULL) ) {
fprintf(stderr, "odb [cimutex(%d)] - Error initializing mutex object: [%d] %s\n",
__LINE__, errno, strerror(errno));
return;
}
#endif
}
/* cicondvar:
* Create and Initialize a Condition Variable object
* - condvarp: pointer to a condition variable object
*
* return: void
*/
static void cicondvar(CondVar *condvarp)
{
#ifdef _WIN32
InitializeConditionVariable(condvarp);
#else
if ( pthread_cond_init(condvarp, NULL) ) {
fprintf(stderr, "odb [cicondvar(%d)] - Error initializing cond var object: [%d] %s\n",
__LINE__, errno, strerror(errno));
return;
}
#endif
}
/* Oload:
* load files using parameters in etab[eid]
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void Oload(int eid)
{
int tid = etab[eid].id; /* Thread ID */
SQLCHAR *Oins = 0; /* INSERT Statement */
SQLCHAR *Odel = 0; /* DELETE Statement */
SQLCHAR *O = 0; /* ODBC temp variable for memory realloc */
SQLRETURN Or=0; /* ODBC return value */
unsigned long nw=0, /* no of wait cycles */
nt=0, /* No of total write cycles */
nrf=0; /* no of read records */
long telaps = 0, /* Elapsed time in ms */
tinit = 0, /* Initialization time in ms */
fsize = 0; /* embedded file size */
size_t bs = TD_CHUNK, /* td[] buffer size */
nb = 0, /* number of bytes read from input file */
mfl = 0, /* max field length */
cl = CMD_CHUNK, /* INSERT command buffer length */
cmdl = 0, /* INSERT command length */
dell = CMD_CHUNK, /* DELETE command length */
len = 0; /* used to scroll the IO buffer */
int ch = 0, /* char read from data file */
z = 0, /* loop variable */
t = 0, /* loop variable */
gzret = 0, /* zlib function return values */
ifl=0; /* input field length */
unsigned int nldr=0, /* number of loaders */
p=0, /* current position in the IO buffer */
k=0, /* input file field number */
n=0, /* input file row number */
m = 0, /* rowset array record number */
mi = 1, /* rowset array record number increment */
c=0, /* current column in the record read from file */
mff=0, /* max file field number mapped to a table column */
nmf=0, /* number of mapped fields */
i=0, /* loop variable */
ffstart=0, /* fixed field start position */
ffend=0, /* fixed field end position */
rnd=0, /* random number */
l=0, /* destination table fields */
o=0, /* loop variable */
j=0, /* loop variable */
lts = etab[eid].k , /* lines to skip */
isgz=0; /* input file is gzipped: 0=no , 1=yes */
int mcfsl = etab[eid].mcfs ? (int)strlen(etab[eid].mcfs) : 0; /* multi character field separator length */
int mcrsl = etab[eid].mcrs ? (int)strlen(etab[eid].mcrs) : 0; /* multi character record separator length */
unsigned char fg = 0, /* Oload flags:
0001 = in a quoted string 0004 = fixed fields 0020 = field ready
0002 = nofile 0010 = delimited fields 0040 = record ready
0100 = escape flag 0200 = embed file read */
ccl = 0, /* Continue cleaning to RS in the next buffer */
pstats = 0, /* Error flag: 0 = print stats, 1 = don't print stats */
lfs = etab[eid].fs, /* local field separator */
lrs = etab[eid].rs, /* local record separator */
lsq = etab[eid].sq ? etab[eid].sq : '"', /* local string qualifier, deafult is '"' */
lem = etab[eid].em, /* local embed character */
lec = etab[eid].ec; /* local escape character */
int *ldrs=0, /* pointer to array containing loaders EIDs */
*rmap=0; /* Input File Fields Map (reverse map):
>=0 maps the correspoding Table Column Number
-1 field to be ignored */
FILE *fl=0, /* data to load file pointer */
*fm=0, /* loadmap file pointer */
*fe=0; /* embed file pointer */
#ifdef HDFS
hdfsFile fhl = 0; /* Hadoop Input File Handle */
#endif
char *buff = 0, /* IO buffer */
*gzbuff = 0, /* GZIP IO buffer */
*bp = 0, /* used to browse line read from mapfile */
*str = 0, /* field buffer */
*sp = 0; /* string pointer loop variable */
struct m { /* Table Column Map */
int min; /* min value */
int max; /* max value */
int idx; /* target table col number:
>=0 maps the corresponding Input File Field number
-1 const/$var -2 seq -3 irand -4 drand
-5 tmrand -6 tsrand -7 crand -8 emrand
-9 null -10 fixed -11 cdate -12 ctime
-13 ctstamp -14 dsrand -15 txtrand -16 nrand
-17 lstrand */
size_t cl; /* Constant length */
char *c; /* pointer to constant */
char trf[16], /* translit from array */
trt[16]; /* translit to array */
char op; /* 1=substr, 2=dconv, 3=tconv, 4=tsconv, 5=replace,
6=toupper, 7=tolower, 8=firstup, 9=csubstr, 10=translit,
11=comp, 12=comp3, 13=zoned, 14=emptyasconst, 15=emptyasempty,
16=div, 17 trim */
char **el; /* dataset element array pointer */
unsigned int prec; /* COMP3/ZONED Precision */
unsigned int scale; /* COMP3/ZONED Scale */
unsigned int *eln; /* dataset element length array pointer */
} *map = 0;
struct tm *dt; /* tm struct for random date/timestamp generation */
time_t trnd; /* to generate random time/timestamp(s) */
char num[32]; /* Formatted Number String */
char tim[15]; /* Formatted Time String */
struct timeval tve; /* timeval struct to define elapesd/timelines */
SQLCHAR *Odp = 0; /* rowset buffer data pointer */
double seconds = 0; /* seconds used for timings */
z_stream gzstream = { 0 } ; /* zlib structure for gziped files */
/* Check if we have to use another ODBC connection */
if ( thps[tid].cr > 0 ) {
thps[tid].Oc = thps[thps[tid].cr].Oc;
thps[tid].Os = thps[thps[tid].cr].Os;
}
/* Set "tgt" variable */
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
/* Run "pre" SQL */
if ( etab[eid].pre ) {
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) /* run a sql script */
z = runsql(tid, eid, 0, (etab[eid].pre + 1 ));
else /* Run single SQL command */
z = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].pre, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( z && etab[eid].flg & 0004 )
goto oload_exit;
etab[eid].nr = 0; /* reset number of resulting rows */
(void)SQLFreeHandle(SQL_HANDLE_STMT, thps[tid].Os);
if ( !SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT, thps[tid].Oc, &thps[tid].Os))){
Oerr(eid, tid, __LINE__, Oc, SQL_HANDLE_DBC);
goto oload_exit;
}
}
/* Check database type */
etab[eid].dbt = checkdb( eid, &thps[tid].Oc, NULL, NULL);
/* Check if truncate */
if ( etab[eid].flg & 0002 ) {
etab[eid].flg &= ~0200000; /* if truncate... unset ifempty */
if ( ( Odel = malloc(dell) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating Odel memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
strmcpy((char *)Odel, dbscmds[etab[eid].dbt].trunc, dell);
Odel = (SQLCHAR *)var_exp((char *)Odel, &dell, &thps[tid].tva);
}
/* check ifempty */
if ( etab[eid].flg & 0200000 ) { /* ifempty is set */
if ( ifempty( eid, etab[eid].tgt ) ) {
fprintf(stderr, "odb [Oload(%d)] - Target table %s is not empty\n",
__LINE__, etab[eid].tgt);
etab[eid].post = 0; /* prevent post SQL execution */
goto oload_exit;
}
}
/* Initialize gzstream structure */
gzstream.zalloc = Z_NULL;
gzstream.zfree = Z_NULL;
gzstream.opaque = Z_NULL;
gzstream.next_in = (unsigned char *)gzbuff;
gzstream.avail_in = 0;
gzstream.next_out = (unsigned char *)buff;
/* Initialize INSERT statement */
cl += strlen(etab[eid].tgt);
if ( ( Oins = malloc ( cl ) ) == (void *) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating Oins memory\n", __LINE__);
goto oload_exit;
}
if (!strcasecmp(etab[eid].loadcmd, "UL"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT USING LOAD %s%sINTO %s(\"",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].tgt);
}
else if (!strcasecmp(etab[eid].loadcmd, "UP"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT %s%sINTO %s(\"",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].tgt);
}
else if (!strcasecmp(etab[eid].loadcmd, "IN"))
{
cmdl += snprintf((char *)Oins, cl, "INSERT %s%sINTO %s(\"",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].tgt);
}
/* Allocate io buffer */
if ( ( buff = malloc((size_t)etab[eid].buffsz) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating IO buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
memset ( buff, 0, (size_t)etab[eid].buffsz);
/* Open input file */
if ( !strcmp ( etab[eid].src , "stdin" ) ) {
fl = stdin ;
} else if ( !strmicmp ( etab[eid].src, "nofile", 6) ) {
if ( ! ( len = (size_t) etab[eid].mr ) ) /* if max record is not set */
len = 100; /* set it to 100 */
fg |= 0002; /* set nofile flag */
} else {
for ( i = j = 0; i < etab[eid].buffsz && etab[eid].src[i]; i++ ) {
switch ( etab[eid].src[i] ) {
case '%':
switch ( etab[eid].src[++i] ) {
case 't':
j += strmcat (buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 2 );
break;
case 'T':
j += strmcat (buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 1 );
break;
case 's':
j += strmcat (buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 2 );
break;
case 'S':
j += strmcat (buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 1 );
break;
case 'c':
j += strmcat (buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 2 );
break;
case 'C':
j += strmcat (buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 1 );
break;
default:
fprintf(stderr, "odb [Oload(%d)] - Invalid expansion %%%c in %s\n",
__LINE__, etab[eid].src[i], etab[eid].src );
goto oload_exit;
}
break;
default:
buff[j++] = etab[eid].src[i];
break;
}
}
buff[j] = '\0';
if ( !(etab[eid].flg2 & 0100) ) { /* No HDFS input file */
if ( ( fl = fopen(buff, "r") ) == (FILE *) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error opening input file %s: [%d] %s\n",
__LINE__, buff, errno, strerror(errno) );
goto oload_exit;
}
if ( etab[eid].flg2 & 0400000000 ) { /* user defined iobuff */
if ( setvbuf( etab[eid].fo, (char *)NULL, _IOFBF, (size_t)etab[eid].iobuff) ) /* setting IO buff size */
fprintf(stderr,"odb [Oload(%d)] - Error setting IO buff size to %zu : [%d] %s\n",
__LINE__, etab[eid].iobuff, errno, strerror(errno) );
}
}
}
/* Open map file */
if ( etab[eid].map && ( fm = fopen(etab[eid].map, "r") ) == (FILE *) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error opening map file %s: [%d] %s\n",
__LINE__, etab[eid].map, errno, strerror(errno) );
goto oload_exit;
}
#ifdef HDFS
/* Open HDFS input file */
if ( etab[eid].flg2 & 0100 ) { /* HDFS input file */
if ( ( fhl = (*hdfsopen)(hfs, buff, O_RDONLY, (int)etab[eid].iobuff, 0, 0) ) == (hdfsFile) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error opening hdfs file %s\n",
__LINE__, buff);
goto oload_exit;
}
}
#endif
/* Analyze target table */
z = Otcol(eid, &thps[tid].Oc) ;
if ( z <= 0 ) {
fprintf(stderr, "odb [Oload(%d)] - Error analyzing target table %s.%s.%s\n",
__LINE__, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
goto oload_exit;
} else {
l = (unsigned int)z ;
}
/* Initilize ODBC dump */
if ( fdmp ) {
for ( j = 0 ; j < l ; j++ ) {
fprintf(fdmp, "[%d] Column %d: name=%s, type=%d (%s), size=%lu, decimals=%d, nullable=%s\n",
tid, j, (char *)etab[eid].td[j].Oname, (int)etab[eid].td[j].Otype,
expandtype(etab[eid].td[j].Otype), (unsigned long)etab[eid].td[j].Osize,
(int)etab[eid].td[j].Odec, etab[eid].td[j].Onull ? "yes" : "no" ) ;
}
}
/* Adjust Osize for WCHAR field (up to 4 bytes/char) */
if ( etab[eid].dbt != VERTICA ) { /* Vertica's CHAR field length is in bytes (not chars) */
for ( j = 0 ; j < l ; j++ ) {
switch ( etab[eid].td[j].Otype ) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpwc; /* Space for UTF-8 conversion */
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpc;/* Space for UTF-8 conversion */
break;
}
}
}
/* Allocate map structure */
if ( ( map = (struct m *)calloc ( l , sizeof(struct m) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating %u memory elements for map[]\n",
__LINE__, l);
goto oload_exit;
}
/* Fill map structure */
if ( fm ) { /* if a loadmap file exists */
while ( fgets(buff, (int)etab[eid].buffsz, fm ) ) {
if ( buff[0] == '#' ) /* Comment: do nothing */
continue;
buff[strlen(buff) - 1] = '\0';
if (strlen(buff) <= 1)
continue;
for ( i = 0 ; buff[i] && buff[i] != ':' ; i++ );
for ( j = 0 ; j < l ; j++ ) {
if ( (len=strlen((char *)etab[eid].td[j].Oname))==(size_t)i )
if ( !strncmp(buff, (char *)etab[eid].td[j].Oname, (size_t)i) )
break;
}
if ( j == l ) {
fprintf(stderr, "odb [Oload(%d)] - Error table column not found for:\n%s\n",
__LINE__, buff);
fclose(fm);
goto oload_exit;
} else {
etab[eid].td[j].dl = 1;
nmf++;
}
bp = buff + i + 1 ;
map[j].op = 0;
if (!strmicmp ( "const", bp, 5 ) ) {
map[j].idx = (-1);
while ( *bp && *bp++ != ':' );
map[j].cl = strlen ( bp );
if ((map[j].c = (char *)malloc ( map[j].cl + 1 )) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - CONST error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
} else {
strmcpy(map[j].c, bp, map[j].cl);
}
} else if (!strmicmp ( "seq", bp, 3 ) ) {
map[j].idx = (-2);
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10);
} else if (!strmicmp ( "irand", bp, 5 ) ) {
map[j].idx = (-3);
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10);
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10);
if ( map[j].min > map[j].max ) {
fprintf(stderr, "odb [Oload(%d)] - IRAND error: min length (%d) greater than max (%d)\n",
__LINE__, map[j].min, map[j].max );
goto oload_exit;
}
} else if (!strmicmp ( "drand", bp, 5 ) ) {
map[j].idx = (-4);
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10);
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10);
if ( map[j].min > map[j].max ) {
fprintf(stderr, "odb [Oload(%d)] - DRAND error: min value (%d) greater than max (%d)\n",
__LINE__, map[j].min, map[j].max );
goto oload_exit;
}
} else if (!strmicmp ( "tmrand", bp, 6 ) ) {
map[j].idx = (-5);
} else if (!strmicmp ( "tsrand", bp, 6 ) ) {
map[j].max = (int) time(0);
map[j].idx = (-6);
} else if (!strmicmp ( "crand", bp, 5 ) ) {
map[j].idx = (-7);
while ( *bp && *bp++ != ':' );
map[j].cl = (size_t) strtol ( bp, NULL, 10);
} else if (!strmicmp ( "emrand", bp, 6 ) ) {
map[j].idx = (-8);
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10); /* username min length */
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10); /* username max length */
if ( map[j].min > map[j].max ) {
fprintf(stderr, "odb [Oload(%d)] - EMRAND error: min userlength (%d) greater than max (%d)\n",
__LINE__, map[j].min, map[j].max );
goto oload_exit;
}
while ( *bp && *bp++ != ':' );
map[j].prec = (int) strtol ( bp, NULL, 10); /* domain min length */
while ( *bp && *bp++ != ':' );
map[j].scale = (int) strtol ( bp, NULL, 10);/* domain max length */
if ( map[j].prec > map[j].scale ) {
fprintf(stderr, "odb [Oload(%d)] - EMRAND error: min domainlength (%d) greater than max (%d)\n",
__LINE__, map[j].min, map[j].max );
goto oload_exit;
}
while ( *bp && *bp++ != ':' );
map[j].cl = strlen(bp);
/* Allocate memory to contain dataset and copy it*/
if ( ( map[j].c = (char *)malloc ( (map[j].cl + 1 ) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - EMRAND error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
MEMCPY(map[j].c, bp , map[j].cl);
map[j].c[map[j].cl] = '\0';
/* count dataset elements (lines) */
for ( map[j].cl = 1 , sp = map[j].c ; *sp ; sp++ )
if ( *sp == ',' )
map[j].cl++;
/* allocate memory for dataset element array */
if ( ( map[j].el = calloc ( map[j].cl, sizeof(char *) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - EMRAND error allocating element array for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
/* allocate memory for dataset element length array */
if ( ( map[j].eln = calloc ( map[j].cl, sizeof(unsigned int) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - EMRAND error allocating element length array for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
/* tokenize dataset and fill dataset element array */
for ( z = 0, sp = map[j].c, map[j].el[0] = map[j].c, o = 0 ; *sp ; sp++ ) {
if ( *sp == ',' ) {
*sp = '\0';
if ( map[j].eln[z] > o )
o = map[j].eln[z]; /* record max dataset element length */
if ( ++z < (int)map[j].cl )
map[j].el[z] = sp + 1 ;
} else {
map[j].eln[z]++;
}
}
if ( ( o + map[j].max + map[j].scale ) > (unsigned int)etab[eid].td[j].Osize ) {
fprintf(stderr, "odb [Oload(%d)] - EMRAND error: total email length greater than \'%s\' target column length (%d)\n",
__LINE__, (char *)etab[eid].td[j].Oname, (int)etab[eid].td[j].Osize);
goto oload_exit;
}
} else if (!strmicmp ( "null", bp, 4 ) ) {
map[j].idx = (-9);
} else if (!strmicmp ( "cdate", bp, 5 ) ) {
map[j].idx = (-11);
} else if (!strmicmp ( "ctime", bp, 5 ) ) {
map[j].idx = (-12);
} else if (!strmicmp ( "ctstamp", bp, 7 ) ) {
map[j].idx = (-13);
} else if (!strmicmp ( "dsrand", bp, 6 ) ) {
map[j].idx = (-14);
while ( *bp && *bp++ != ':' );
if ( ( fe = fopen(bp, "rb") ) == (FILE *) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - DSRAND error opening %s: [%d] %s\n",
__LINE__, bp, errno, strerror(errno) );
goto oload_exit;
}
(void) fseek(fe, 0L, SEEK_END); /* goto EOF */
fsize = ftell ( fe ); /* get file size */
(void) fseek(fe, 0L, SEEK_SET); /* rewind */
/* Allocate memory to contain the whole dataset */
if ( ( map[j].c = (char *)malloc ( (size_t)(fsize + 1 ) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - DSRAND error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
/* read the dataset from disk and close the input file */
if ( ( len = fread ( map[j].c, 1, (size_t)fsize, fe) ) != (size_t)fsize ) {
fprintf(stderr, "odb [Oload(%d)] - DSRAND error reading from %s (got " SIZET_SPEC " bytes, expected " SIZET_SPEC "): [%d] %s\n",
__LINE__, bp, len, (size_t)fsize, errno, strerror(errno) );
goto oload_exit;
}
*(map[j].c + len) = '\0'; /* write a NULL at the end */
fclose ( fe ) ;
/* count dataset elements (lines) */
for ( map[j].cl = 0 , sp = map[j].c ; *sp ; sp++ )
if ( *sp == '\n' || *sp == '\r' )
{
map[j].cl++;
if( *(sp + 1) == '\n' ) /* windows linefeed use \r\n */
sp++;
}
/* allocate memory for dataset element array */
if ( ( map[j].el = calloc ( map[j].cl, sizeof(char *) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - DSRAND error allocating element array for %s\n",
__LINE__, bp);
goto oload_exit;
}
/* allocate memory for dataset element length array */
if ( ( map[j].eln = calloc ( map[j].cl, sizeof(unsigned int) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - DSRAND error allocating element length array for %s\n",
__LINE__, bp);
goto oload_exit;
}
/* tokenize dataset and fill dataset element array */
for ( z = 0, sp = map[j].c, map[j].el[0] = map[j].c ; *sp ; sp++ ) {
if ( *sp == '\n' || *sp == '\r' ) {
*sp = '\0';
if ( *sp == '\r' && *(sp + 1) == '\n' )
{
*(sp + 1) = '\0';
sp++;
}
if ( map[j].eln[z] > (unsigned int)etab[eid].td[j].Osize ) {
fprintf(stderr, "odb [Oload(%d)] - DSRAND warning: dataset element \'%s\' will be truncated to fit \'%s\' column length (%d)\n",
__LINE__, map[j].el[z], (char *)etab[eid].td[j].Oname, (int)etab[eid].td[j].Osize);
map[j].eln[z] = (int)etab[eid].td[j].Osize;
}
if ( ++z < (int)map[j].cl )
map[j].el[z] = sp + 1 ;
} else {
map[j].eln[z]++;
}
}
} else if (!strmicmp ( "txtrand", bp, 7 ) ) {
map[j].idx = (-15);
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10);
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10);
while ( *bp && *bp++ != ':' );
if ( ( fe = fopen(bp, "rb") ) == (FILE *) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error opening %s: [%d] %s\n",
__LINE__, bp, errno, strerror(errno) );
goto oload_exit;
}
(void) fseek(fe, 0L, SEEK_END); /* goto EOF */
map[j].cl = (size_t)ftell ( fe ); /* get file size */
(void) fseek(fe, 0L, SEEK_SET); /* rewind */
/* Allocate memory to contain the whole dataset */
if ( ( map[j].c = (char *)malloc ( (map[j].cl + 1 ) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - TXTRAND error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
/* read the dataset from disk and close the input file */
if ( ( len = fread ( map[j].c, 1, map[j].cl, fe) ) != map[j].cl ) {
fprintf(stderr, "odb [Oload(%d)] - TXTRAND error reading from %s (got " SIZET_SPEC " bytes, expected " SIZET_SPEC "): [%d] %s\n",
__LINE__, bp, len, map[j].cl, errno, strerror(errno) );
goto oload_exit;
}
*(map[j].c + len) = '\0'; /* write a NULL at the end */
fclose ( fe ) ;
if ( map[j].min > map[j].max ) {
fprintf(stderr, "odb [Oload(%d)] - TXTRAND error: min length (%d) greater than max (%d)\n",
__LINE__, map[j].min, map[j].max );
goto oload_exit;
}
if ( (size_t)map[j].max > map[j].cl ) {
fprintf(stderr, "odb [Oload(%d)] - TXTRAND error: max length (%d) exceed file length (" SIZET_SPEC ")\n",
__LINE__, map[j].max, map[j].cl );
goto oload_exit;
}
} else if (!strmicmp ( "nrand", bp, 5 ) ) {
map[j].idx = (-16);
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10); /* precision */
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10); /* scale */
if ( map[j].min > map[j].max ) {
fprintf(stderr, "odb [Oload(%d)] - NRAND error: scale (%d) greater than precision (%d)\n",
__LINE__, map[j].min, map[j].max );
goto oload_exit;
}
map[j].cl = (size_t)(map[j].max + 1); /* total string length */
map[j].max -= map[j].min; /* digits before dec sep */
} else if (!strmicmp ( "lstrand", bp, 7 ) ) {
map[j].idx = (-17);
while ( *bp && *bp++ != ':' );
map[j].cl = strlen(bp);
/* Allocate memory to contain dataset and copy it*/
if ( ( map[j].c = (char *)malloc ( (map[j].cl + 1 ) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - LSTRAND error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
MEMCPY(map[j].c, bp , map[j].cl);
map[j].c[map[j].cl] = '\0';
/* count dataset elements (lines) */
for ( map[j].cl = 1 , sp = map[j].c ; *sp ; sp++ )
if ( *sp == ',' )
map[j].cl++;
/* allocate memory for dataset element array */
if ( ( map[j].el = calloc ( map[j].cl, sizeof(char *) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - LSTRAND error allocating element array memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
/* allocate memory for dataset element length array */
if ( ( map[j].eln = calloc ( map[j].cl, sizeof(unsigned int) ) ) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - LSTRAND error allocating element length array memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
/* tokenize dataset and fill dataset element array */
for ( z = 0, sp = map[j].c, map[j].el[0] = map[j].c ; *sp ; sp++ ) {
if ( *sp == ',' ) {
*sp = '\0';
if ( map[j].eln[z] > (unsigned int)etab[eid].td[j].Osize ) {
fprintf(stderr, "odb [Oload(%d)] - LSTRAND warning: dataset element \'%s\' will be truncated to fit target column %s of length %d\n",
__LINE__, map[j].el[z], (char *)etab[eid].td[j].Oname, (int)etab[eid].td[j].Osize);
map[j].eln[z] = (int)etab[eid].td[j].Osize;
}
if ( ++z < (int)map[j].cl )
map[j].el[z] = sp + 1 ;
} else {
map[j].eln[z]++;
}
}
} else if ( bp[0] == '$' ) {
map[j].idx = (-1);
map[j].c = getenv(bp + 1);
map[j].cl = strlen ( map[j].c );
} else {
if (!strmicmp ( "fixed", bp, 5 ) ) {
map[j].idx = j; /* this will be adjusted afterwards */
fg |= 0004; /* fixed format flag */
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10);
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10);
} else {
fg |= 0010; /* delimited format flag */
map[j].idx = (int)strtol( bp, NULL, 10) - 1;
if (map[j].idx < 0) {
fprintf(stderr, "odb [Oload(%d)] - Error: Field index should should start from 1\n", __LINE__);
goto oload_exit;
}
}
while ( *bp && *bp++ != ':' );
if ( *bp ) {
if ( !strmicmp ( "substr", bp, 6 ) ) {
map[j].op = 1;
while ( *bp && *bp++ != ':' );
map[j].min = (int) strtol ( bp, NULL, 10);
while ( *bp && *bp++ != ':' );
map[j].max = (int) strtol ( bp, NULL, 10);
if ( map[j].min >= map[j].max ) {
fprintf(stderr, "odb [Oload(%d)] - SUBSTR error: min value (%d) is >= max value (%d) for %s\n",
__LINE__, map[j].min, map[j].max, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
} else if ( !strmicmp ( "dconv", bp, 5 ) ) {
map[j].cl = strlen ( bp+6 );
map[j].op = 2;
if ((map[j].c = (char *)malloc ( map[j].cl + 1 )) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - DCONV error allocating format string memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
} else {
strmcpy(map[j].c, bp+6, map[j].cl);
}
} else if ( !strmicmp ( "tconv", bp, 5 ) ) {
map[j].cl = strlen ( bp+6 );
map[j].op = 3;
if ((map[j].c = (char *)malloc ( map[j].cl + 1 )) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - TCONV error allocating format string memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
} else {
strmcpy(map[j].c, bp+6, map[j].cl);
}
} else if ( !strmicmp ( "tsconv", bp, 6 ) ) {
map[j].cl = strlen ( bp+7 );
map[j].op = 4;
if ((map[j].c = (char *)malloc ( map[j].cl + 1 )) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - TSCONV error allocating format string memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
} else {
strmcpy(map[j].c, bp+7, map[j].cl);
}
} else if ( !strmicmp ( "replace", bp, 7 ) ) {
map[j].cl = strlen ( bp+8 );
map[j].op = 5;
if ((map[j].c = (char *)malloc ( map[j].cl + 1 )) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - REPLACE error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
} else {
strmcpy(map[j].c, bp+8, map[j].cl);
}
for ( i = 0 ; map[j].c[i] && map[j].c[i] != ':' ; i++ );
map[j].max = (int) map[j].cl - i - 1;
if ( map[j].max > (int)etab[eid].td[j].Osize )
map[j].max = (int)etab[eid].td[j].Osize;
map[j].cl = i + 1;
map[j].c[i] = '\0';
} else if ( !strmicmp ( "toupper", bp, 7 ) ) {
map[j].op = 6;
} else if ( !strmicmp ( "tolower", bp, 7 ) ) {
map[j].op = 7;
} else if ( !strmicmp ( "firstup", bp, 7 ) ) {
map[j].op = 8;
} else if ( !strmicmp ( "csubstr", bp, 7 ) ) {
map[j].op = 9;
while ( *bp && *bp++ != ':' );
map[j].min = (int) mchar ( bp );
while ( *bp && *bp++ != ':' );
map[j].max = (int) mchar ( bp );
} else if ( !strmicmp ( "translit", bp, 8 ) ) {
map[j].op = 10;
while ( *bp && *bp++ != ':' );
for ( z = 0 ; *bp && *bp != ':' && z < 16 ; z++, bp++ ) {
if ( *bp == '\\' ) {
switch ( *++bp ) {
case 't':
map[j].trf[z] = 9;
break;
case 'n':
map[j].trf[z] = 10;
break;
case 'v':
map[j].trf[z] = 11;
break;
case 'f':
map[j].trf[z] = 12;
break;
case 'r':
map[j].trf[z] = 13;
break;
case 'e':
map[j].trf[z] = 27;
break;
default:
fprintf(stderr, "odb [Oload(%d)] - TRANSLIT error: invalid escape \\%c\n",
__LINE__, *bp);
goto oload_exit;
}
} else {
map[j].trf[z] = *bp;
}
}
for ( t = 0, bp++ ; *bp && *bp != ':' && t < 16 ; t++, bp++ ) {
if ( *bp == '\\' ) {
switch ( *++bp ) {
case 'd':
map[j].trt[t] = 0;
break;
case 't':
map[j].trt[t] = 9;
break;
case 'n':
map[j].trt[t] = 10;
break;
case 'v':
map[j].trt[t] = 11;
break;
case 'f':
map[j].trt[t] = 12;
break;
case 'r':
map[j].trt[t] = 13;
break;
case 'e':
map[j].trt[t] = 27;
break;
default:
fprintf(stderr, "odb [Oload(%d)] - TRANSLIT error: invalid escape \\%c\n",
__LINE__, *bp);
goto oload_exit;
}
} else {
map[j].trt[t] = *bp;
}
}
if ( z != t ) {
fprintf(stderr, "odb [Oload(%d)] - TRANSLIT error: number of \'translit-from\' (%d) and \'translit-to\' (%d) characters differ\n",
__LINE__, z, t);
goto oload_exit;
} else {
map[j].min = z;
}
} else if ( !strmicmp ( "comp3", bp, 5 ) ) {
map[j].op = 12;
while ( *bp && *bp++ != ':' );
map[j].prec = (int) mchar ( bp ); /* read precision */
while ( *bp && *bp++ != ':' );
map[j].scale = (int) mchar ( bp ); /* read scale */
} else if ( !strmicmp ( "comp", bp, 4 ) ) {
map[j].op = 11;
} else if ( !strmicmp ( "zoned", bp, 5 ) ) {
map[j].op = 13;
while ( *bp && *bp++ != ':' );
map[j].prec = (int) mchar ( bp ); /* read precision */
while ( *bp && *bp++ != ':' );
map[j].scale = (int) mchar ( bp ); /* read scale */
} else if ( !strmicmp ( "emptyasconst", bp, 10 ) ) {
while ( *bp && *bp++ != ':' );
map[j].cl = strlen ( bp );
if ((map[j].c = (char *)malloc ( map[j].cl + 1 )) == (void *)NULL) {
fprintf(stderr, "odb [Oload(%d)] - EMPTYASCONST error allocating memory for %s\n",
__LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
} else {
strmcpy(map[j].c, bp, map[j].cl);
}
map[j].op = 14;
} else if ( !strmicmp ( "emptyasempty", bp, 11 ) ) {
map[j].op = 15;
} else if (!strmicmp("div", bp, 3)) {
map[j].op = 16;
while (*bp && *bp++ != ':');
map[j].scale = strtol(bp, NULL, 10);
if (map[j].scale == 0) {
fprintf(stderr, "odb [Oload(%d)] - DIV error for %s\n", __LINE__, (char *)etab[eid].td[j].Oname);
goto oload_exit;
}
} else if (!strmicmp("trim", bp, 4)) {
map[j].op = 17;
}
else {
map[j].op = 0;
}
}
}
}
fclose(fm);
/* Determine max mapped field number */
for ( i = 0, mff = 0 ; i < l ; i++ ) {
if ( map[i].idx > 0 && (unsigned int)map[i].idx > mff )
mff = map[i].idx;
}
mff++;
/* Adjust map[].idx for fixed formats */
if ( fg & 0004 ) {
for ( i = 0 ; i < mff ; i++ ) {
unsigned int rnk = 0;
for ( j = 0 ; j < mff ; j++ ) {
if ( map[j].min < map[i].min ) {
rnk++;
}
}
if ( map[i].idx > 0 )
map[i].idx = rnk;
}
}
} else {
for ( i = 0 ; i < l ; i++ )
etab[eid].td[i].dl = 1;
nmf = mff = l;
}
if ( ( fg & 0010 ) && ( fg & 0004 ) ) {
fprintf(stderr, "odb [Oload(%d)] - You cannot mix fixed/delimited fields \n", __LINE__);
goto oload_exit;
}
/* Allocate & Initialize Reverse Map */
if ( mff ) {
if ( ( rmap = (int *)calloc ( mff , sizeof(int) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating memory for rmap\n", __LINE__);
goto oload_exit;
}
if ( fm ) {
for ( i = 0 ; i < mff ; i++ ) /* Initialize to ignore */
rmap[i] = -1;
for ( i = 0 ; i < l ; i++ )
if ( etab[eid].td[i].dl && map[i].idx >= 0 )
rmap[map[i].idx] = i;
} else {
for ( i = 0 ; i < mff ; i++ )
rmap[i] = i;
}
}
/* Truncate target table */
if ( etab[eid].flg & 0002 ) { /* truncate target table */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: Now truncating target table (%s)... ", (char *)Odel);
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
z = Oexec(tid, eid, 0, 0, Odel, ""); /* Run Truncate */
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( j && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Oload(%d)] - Error truncating Target table. Exiting because Stop On Error was set\n",
__LINE__);
goto oload_exit;
}
if ( etab[eid].cmt ) { /* Autocommit off: have to commit truncate */
if (!SQL_SUCCEEDED(Or=SQLEndTran(SQL_HANDLE_DBC, thps[tid].Oc, SQL_COMMIT))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
goto oload_exit;
}
}
etab[eid].nr = 0; /* reset number of resulting rows */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "done\n");
}
/* Continue INSERT statement building and calculate buffer size */
for ( i = z = 0, etab[eid].s = 0 ; i < l ; i++ ) {
if ( !etab[eid].td[i].dl ) /* skip this column */
continue;
if ( z ) /* not the first column */
cl += strmcat((char *)Oins, "\",\"", cl, 0);
else
z = 1;
cl += strmcat((char *)Oins, (char *)etab[eid].td[i].Oname, cl, 0);
etab[eid].td[i].start = etab[eid].s;
#ifdef __hpux
if ( etab[eid].td[i].Osize % WORDSZ )
etab[eid].td[i].pad = WORDSZ - etab[eid].td[i].Osize % WORDSZ ;
#endif
etab[eid].s += ( etab[eid].td[i].Osize + etab[eid].td[i].pad + sizeof(SQLLEN) ); /* space for length indicator */
if ( cmdl + CMD_CHUNK < cl ) { /* increase Oins buffer */
cl += CMD_CHUNK;
O = Oins;
if ( ( O = realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
Oins = O;
}
}
/* Calculate rowset if buffer size is set */
if ( etab[eid].rbs ) {
etab[eid].r = etab[eid].rbs / etab[eid].s;
if ( etab[eid].mr && etab[eid].r > etab[eid].mr ) /* if # records to fetch < rowset ... */
etab[eid].r = etab[eid].mr; /* make rowset = records to fetch */
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; /* at least one record at a time */
}
if ( etab[eid].flg2 & 0001 ) /* commit as multiplier */
etab[eid].cmt *= (int)etab[eid].r ;
/* Determine max target column length */
for ( i = 0 ; i < l ; i++ )
if ( (size_t)etab[eid].td[i].Osize > mfl )
mfl = (size_t) etab[eid].td[i].Osize;
/* Allocate field buffer */
if ( (str = (char *)calloc (1, etab[eid].buffsz + 1)) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating field buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
/* Complete INSERT statement */
cl += strmcat ( (char *) Oins, "\") VALUES(", cl, 0);
for ( i = 0; i < nmf ; i++ ) {
if ( i )
cl += strmcat ( (char *) Oins, ",", cl, 0);
cl += strmcat ( (char *) Oins, "?", cl, 0);
if ( cmdl + CMD_CHUNK < cl ) { /* increase Oins buffer */
cl += CMD_CHUNK;
O = Oins ;
if ( ( O = realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
Oins = O ;
}
}
(void) strmcat ( (char *) Oins, ")", cl, 0);
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [Oload(%d)] - INSERT statement: %s\n", __LINE__, (char *)Oins);
/* Allocate loader eids array */
nldr = etab[eid].ps ? etab[eid].ps : 1;
if ( ( ldrs = (int *)calloc ( nldr, sizeof ( int ) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating loader eids array: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
/* Initialize loader eids array */
if ( etab[eid].ps ) {
for ( i = 0, j = 0 ; i < (unsigned int)no && j < etab[eid].ps ; i++ ){
if ( etab[i].type == 'L' && etab[i].parent == eid ){
etab[i].r = etab[eid].r; /* adjust rowset when rbs is specified */
etab[i].cmt = etab[eid].cmt;/* adjust rowset when cmt is a multiplier */
etab[i].lstat = 0; /* reset buffer status to available */
etab[i].td = etab[eid].td; /* copy table structure pointer */
ldrs[j++] = i;
}
}
} else {
ldrs[0] = eid;
}
/* Allocate rowset & status memory */
if ( (etab[eid].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[eid].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
if ( etab[eid].ps ) {
for ( j = 0 ; j < nldr ; j++ ) { /* for all loading threads... */
if ( (etab[ldrs[j]].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[ldrs[j]].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
}
}
/* Set rowset size, bind parameters and prepare INSERT */
for ( j = 0 ; j < nldr ; j++ ) {
/* Set max commit mode */
if ( etab[eid].cmt ) {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oload_exit;
}
} else {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oload_exit;
}
}
/* Set max char/varchar/binary column length */
if (!SQL_SUCCEEDED(Oret=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_MAX_LENGTH,
(SQLPOINTER)etab[eid].Omaxl, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
}
/* Bind parameters */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_STATUS_PTR,
etab[ldrs[j]].Ostatusl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMS_PROCESSED_PTR,
&etab[ldrs[j]].Oresl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload_exit;
}
for ( i = 0, z = 1; i < l; i++ ) {
if ( etab[eid].td[i].dl ) {
if (!SQL_SUCCEEDED(Or=SQLBindParameter(thps[etab[ldrs[j]].id].Os, (SQLUSMALLINT)z++,
SQL_PARAM_INPUT, SQL_C_CHAR, etab[eid].td[i].Otype, (SQLULEN)etab[eid].td[i].Osize,
etab[eid].td[i].Odec, &etab[ldrs[j]].Orowsetl[0+etab[eid].td[i].start], etab[eid].td[i].Osize,
(SQLLEN *)&etab[ldrs[j]].Orowsetl[0+etab[eid].td[i].start+etab[eid].td[i].Osize+etab[eid].td[i].pad]))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload_exit;
}
}
}
if (!SQL_SUCCEEDED(Or=SQLPrepare(thps[etab[ldrs[j]].id].Os, Oins, SQL_NTS))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload_exit;
}
etab[ldrs[j]].sp = l; /* save number of target table fields for prec */
}
/* Register Load Start Time */
gettimeofday(&tve, (void *)NULL);
tinit = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
/* Set initial fixed fields start/end */
if ( rmap[k] >= 0 ) { /* rmap[k]=-1 for ignored fields */
ffstart = map[rmap[k]].min;
ffend = map[rmap[k]].max;
}
/* Determine if the input file is gzipped by checking the first 2 chars */
if ( fl ) {
if ( ( fl != stdin ) && ( len = fread ( buff, 1, 2, fl) ) == 2 ) {
if ( (unsigned char)buff[0] == 037 &&
(unsigned char)buff[1] == 0213 ) { /* gzipped file */
isgz = 1 ;
}
(void) fseek(fl, 0L, SEEK_SET); /* rewind */
}
#ifdef HDFS
} else if ( fhl ) {
if ( ( len = (size_t)(*hdfsread)(hfs, fhl, (void *)buff, 2) ) == 2 ) {
if ( (unsigned char)buff[0] == 037 &&
(unsigned char)buff[1] == 0213 ) { /* gzipped file */
isgz = 1 ;
}
(void)(*hdfsseek)(hfs, fhl, 0); /* rewind */
}
#endif
}
/* Allocate buffer for gzip read and initialize inflate */
if ( isgz ) {
/* Allocate gzip io buffer */
if ( ( gzbuff = malloc((size_t)etab[eid].buffsz) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error allocating gzip IO buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload_exit;
}
memset ( gzbuff, 0, (size_t)etab[eid].buffsz);
/* Initalize inflate */
if ( ( gzret = inflateInit2 (&gzstream, windowBits | ENABLE_ZLIB_GZIP) ) != Z_OK ) {
fprintf(stderr, "odb [Oload(%d)] - Error initializing zlib: [%d]\n",
__LINE__, gzret);
goto oload_exit;
}
}
/* Reading input file */
pstats = 1; /* From now on print stats on exit */
Odp = &etab[eid].Orowsetl[m*etab[eid].s];
while ( go ) {
if ( isgz ) { /* is a gzipped file */
if ( gzstream.avail_in ) { /* continue inflating previous gzbuff into buff */
gzstream.avail_out = (unsigned int) etab[eid].buffsz ;
gzstream.next_out = (unsigned char *)buff ;
gzret = inflate (&gzstream, Z_NO_FLUSH) ;
switch ( gzret ) {
case Z_OK:
break ; /* everything is ok - continue */
case Z_STREAM_END:
inflateReset ( &gzstream ) ;
break;
default:
fprintf(stderr, "odb [Oload(%d)] - Error during deflate: [%d]\n",
__LINE__, gzret);
goto oload_exit ;
break;
}
len = etab[eid].buffsz - gzstream.avail_out ;
} else {
if ( fl ) { /* read new data from normal file-system into gzbuff */
len = fread ( gzbuff, 1, (size_t)etab[eid].buffsz, fl);
#ifdef HDFS
} else if ( fhl ) { /* read new data from HDFS into gzbuff */
len = (size_t)(*hdfsread)(hfs, fhl, (void *)buff, etab[eid].buffsz);
#endif
}
gzstream.avail_in = (unsigned int)len ;
gzstream.next_in = (unsigned char *)gzbuff ;
if ( len )
continue ;
}
} else if ( fl ) {
len = fread ( buff, 1, (size_t)etab[eid].buffsz, fl);
#ifdef HDFS
} else if ( fhl ) {
len = (size_t)(*hdfsread)(hfs, fhl, (void *)buff, etab[eid].buffsz);
#endif
}
if ( len == 0 ) { /* EOF */
if ( ( k + 1 ) == mff && !(fg & 0004) ) { /* complete last row & insert */
ch = -1; /* insert this block */
goto oload_lastrow;
} else if ( m ) { /* rows to be inserted */
goto oload_insert;
} else { /* exit loop */
break;
}
}
nb += len; /* update bytes read from file */
p = 0; /* reset buffer index */
if (!etab[eid].mcrs) {
while (lts && p < len) { /* skip initial lines */
if (buff[p++] == lrs) {
--lts;
}
}
}
if (!etab[eid].mcrs) {
if ( ccl ) { /* continue cleaning rest of line */
while ( p < len && buff[p] != lrs ) /* ... skip the rest of the line */
p++;
if (buff[p] == lrs) { /* if a record separator has been found */
ccl = 0; /* switch the continue cleaning flag off */
p++; /* skip the record separator */
}
}
}
for ( ; p < len ; p++ ) {
ch = buff[p];
if ( fg & 0004 ) { /* fixed file format */
if ( c < ffstart ) { /* before field start */
c++; /* do nothing (skip it) */
} else if ( ch == lrs ) { /* if record sep... */
if ( c )
fg |= 0040; /* set record complete flag on */
} else { /* add new character to field buffer */
str[ifl++] = ch;
c++;
if ( ifl == (int)ffend ) { /* fixed file format: end of field */
while ( ifl && str[ifl-1] == etab[eid].pc ) /* "remove" trailing pad chars */
ifl--;
fg |= 0020;
}
}
} else { /* delimited file format */
if ( ch == lec && !(fg & 0100)) { /* if non-escaped escape char... */
fg |= 0100; /* set Escape flag on */
} else if (!etab[eid].mcfs && (ch == lfs) && !(fg & 0001)) { /* if field sep... */
fg |= 0020; /* set field complete flag on */
} else if (!etab[eid].mcrs && (ch == lrs) && !(fg & 0001)) { /* if record sep... */
fg |= 0040; /* set record complete flag on */
} else if ( ch == lsq && !(fg & 0100)) { /* if string qualifier char... */
fg ^= 0001; /* flip quoted string flag */
} else if ( ch == lem && !(fg & 0100)) { /* if embedded file char */
fg |= 0200; /* embed file reading mode */
} else {
str[ifl++] = ch; /* add new character to field buffer */
fg &= ~0100; /* set escape flag off */
if (etab[eid].mcfs && (ifl >= mcfsl)
&& !strncmp(etab[eid].mcfs, &str[ifl-mcfsl], mcfsl)) { /* if field sep... */
fg |= 0020;
ifl -= mcfsl;
} else if (etab[eid].mcrs && (ifl >= mcrsl)
&& !strncmp(etab[eid].mcrs, &str[ifl-mcrsl], mcrsl)) { /* if reco sep... */
fg |= 0040;
ifl -= mcrsl;
}
}
}
if ( fg & 0062 ) { /* field/record ready or nofile */
oload_lastrow:
str[ifl] = '\0';
if ( rmap && rmap[k] >= 0 ) {
Odp = &etab[eid].Orowsetl[m*etab[eid].s + etab[eid].td[rmap[k]].start];
if ( fg & 0200 ) { /* embed file reading mode */
fg &= ~0200 ; /* reset embed file flag */
str[ifl] = '\0'; /* close filename string */
if ( ( fe = fopen(str, "rb") ) == (FILE *) NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Line skipped. Error opening %s: [%d] %s\n",
__LINE__, str, errno, strerror(errno) );
mi = 0;
} else {
(void) fseek(fe, 0L, SEEK_END); /* goto EOF */
fsize = ftell ( fe ); /* get file size */
(void) fseek(fe, 0L, SEEK_SET); /* rewind */
if ( fsize > (long)etab[eid].td[rmap[k]].Osize ) { /* prevent Orowsetl[] overflow */
fprintf(stderr, "odb [Oload(%d)] - Line skipped. %s size (%ld) exceed target field length (%lu)\n",
__LINE__, str, fsize, (unsigned long)etab[eid].td[rmap[k]].Osize );
mi = 0;
} else if ( ( ifl = (int)fread ( Odp, 1, (size_t)fsize, fe) ) != (int)fsize ) {
fprintf(stderr, "odb [Oload(%d)] - Line skipped. Error reading %s: [%d] %s\n",
__LINE__, str, errno, strerror(errno) );
mi = 0;
}
fclose ( fe ) ;
}
} else if ( etab[eid].ns && !strncmp(etab[eid].ns, str, (size_t)ifl) ) {
ifl = SQL_NULL_DATA;
}
else if ( (ifl == 0) && (map[rmap[k]].op != 14) && (map[rmap[k]].op != 15) ) {
if (!etab[eid].ns) {
ifl = SQL_NULL_DATA;
}
} else {
switch ( map[rmap[k]].op ) { /* manipulate str if needed */
case 1: /* substr */
ifl = map[rmap[k]].max - map[rmap[k]].min;
memmove((void *)str, (void *) (str + map[rmap[k]].min), (size_t)ifl);
break;
case 2: /* date conversion */
if ( ( ifl = dconv ( map[rmap[k]].c, str, str, bs, 0) ) == 0 )
fprintf ( stderr, "odb [Oload(%d)] - Error converting date row %u col %u\n"
"Input string: >%s<\nFormat string: >%s<\n",
__LINE__, n, k, str, map[rmap[k]].c );
else if ( ifl == 1 )
fprintf ( stderr, "odb [Oload(%d)] - Error converting date row %u col %u"
" (Bad Format String)\nInput string: >%s<\nFormat string: >%s<\n",
__LINE__, n, k, str, map[rmap[k]].c );
break;
case 3: /* time conversion */
if ( ( ifl = dconv ( map[rmap[k]].c, str, str, bs, 1) ) == 0 )
fprintf ( stderr, "odb [Oload(%d)] - Error converting time row %u col %u\n"
"Input string: >%s<\nFormat string: >%s<\n",
__LINE__, n, k, str, map[rmap[k]].c );
else if ( ifl == 1 )
fprintf ( stderr, "odb [Oload(%d)] - Error converting time row %u col %u"
" (Bad Format String)\nInput string: >%s<\nFormat string: >%s<\n",
__LINE__, n, k, str, map[rmap[k]].c );
break;
case 4: /* timestamp conversion */
if ( ( ifl = dconv ( map[rmap[k]].c, str, str, bs, 2) ) == 0 )
fprintf ( stderr, "odb [Oload(%d)] - Error converting timestamp row %u col %u\n"
"Input string: >%s<\nFormat string: >%s<\n",
__LINE__, n, k, str, map[rmap[k]].c );
else if ( ifl == 1 )
fprintf ( stderr, "odb [Oload(%d)] - Error converting timestamp row %u col %u"
" (Bad Format String)\nInput string: >%s<\nFormat string: >%s<\n",
__LINE__, n, k, str, map[rmap[k]].c );
break;
case 5: /* replace */
if ( !strncmp(map[rmap[k]].c, str, (size_t)ifl) ){
ifl = map[rmap[k]].max;
MEMCPY(str, ( map[rmap[k]].c + map[rmap[k]].cl ) , map[rmap[k]].max);
}
break;
case 6: /* toupper */
str = strup(str);
break;
case 7: /* tolower */
str = strlo(str);
break;
case 8: /* firstup */
str = strlo(str);
*str = (unsigned char) toupper ( (int)*str );
break;
case 9: /* csubstr */
for ( z = 0 ; z < (int)ifl && str[z] != map[rmap[k]].max ; z++ );
if ( map[rmap[k]].min == map[rmap[k]].max && z < (int)ifl )
for ( ++z ; z < (int)ifl && str[z] != map[rmap[k]].max ; z++ );
for ( t = z - 1 ; t >= 0 && str[t] != map[rmap[k]].min ; t-- );
if ( ( ifl = (z - t - 1 ) ) <= 0 )
ifl = 0;
else if ( t >= 0 )
memmove((void *)str, (void *) (str + t + 1), (size_t)ifl);
break;
case 10: /* translit */
for ( i = 0, z = 0 ; i < (unsigned int)ifl ; i++ ) {
for ( t = 0 ; t <= map[rmap[k]].min ; t++ ) {
if ( str[i] == map[rmap[k]].trf[t] ) {
if ( map[rmap[k]].trt[t] )
str[z++] = map[rmap[k]].trt[t] ;
t = -1;
break ;
}
}
if ( t >=0 )
str[z++] = str[i];
}
ifl = (unsigned int)z;
break ;
case 11: /* comp */
z = comp2asc(str, num, ifl, (unsigned int) etab[eid].td[rmap[k]].Osize);
if ( z < 0 ) {
fprintf ( stderr, "odb [Oload(%d)] - COMP field conversion error: row %d col %d. "
"This row won't be loaded\n", __LINE__, n+1, k+1);
mi = 0; /* SKIP THIS ROW */
} else {
strmcpy(str, num, z);
ifl = z;
}
break;
case 12: /* comp3 */
z = comp32asc(str, num, ifl, (unsigned int) etab[eid].td[rmap[k]].Osize, map[rmap[k]].prec, map[rmap[k]].scale);
if ( z < 0 ) {
fprintf ( stderr, "odb [Oload(%d)] - COMP3 field conversion error: row %d col %d. "
"This row won't be loaded\n", __LINE__, n+1, k+1);
mi = 0; /* SKIP THIS ROW */
} else {
strmcpy(str, num, z);
ifl = z;
}
break;
case 13: /* zoned */
z = zoned2asc(str, num, ifl, (unsigned int) etab[eid].td[rmap[k]].Osize, map[rmap[k]].prec, map[rmap[k]].scale);
if ( z < 0 ) {
fprintf ( stderr, "odb [Oload(%d)] - ZONED field conversion error: row %d col %d. "
"This row won't be loaded\n", __LINE__, n+1, k+1);
mi = 0; /* SKIP THIS ROW */
} else {
strmcpy(str, num, z);
ifl = z;
}
break;
case 14: /* emptyasconst */
if ( ifl == 0 ) {
ifl = (int)map[rmap[k]].cl;
strmcpy(str, map[rmap[k]].c, map[rmap[k]].cl);
}
break;
case 15: /* emptyasempty */
if ( ifl == 0 )
ifl = EMPTY ;
break;
case 16:
{
if (ifl > 0) {
double dv = 0;
str[ifl] = '\0';
if (!(is_valid_numeric(str, strlen(str)) && sscanf(str, "%lf", &dv))) {
fprintf(stderr, "odb [Oload(%d)] - DIV field conversion error: row %d col %d. "
"%s is not valid numeric, This row won't be loaded\n", __LINE__, n + 1, k + 1, str);
mi = 0; /* SKIP THIS ROW */
}
dv = dv / map[rmap[k]].scale;
ifl = sprintf(str, "%.*lf", etab[eid].td[rmap[k]].Odec, dv);
}
}
break;
case 17: // trim
str = strtrim(str);
ifl = strlen(str);
break;
}
if ( ifl > (int)etab[eid].td[rmap[k]].Osize ) { /* prevent Orowsetl[] overflow */
str[ifl]='\0';
switch ( etab[eid].fldtr ) {
case 0: /* truncate, warn, load if text */
case 1: /* truncate, load if text */
switch ( etab[eid].td[rmap[k]].Otype ) {
case SQL_CHAR:
case SQL_WCHAR:
case SQL_VARCHAR:
case SQL_WVARCHAR:
case SQL_LONGVARCHAR:
case SQL_WLONGVARCHAR:
if ( !etab[eid].fldtr )
fprintf ( stderr, "odb [Oload(%d)] - Warning: row %d col %d field truncation. Input "
"string: >%s< of length %d. Only the first %lu char(s) will be loaded\n", __LINE__,
n+1, k+1, str, ifl, (long unsigned)etab[eid].td[rmap[k]].Osize );
break;
default:
if ( !etab[eid].fldtr )
fprintf ( stderr, "odb [Oload(%d)] - Error: row %d col %d field truncation. Input "
"string: >%s< of length %d exceeds %lu. This row won't be loaded\n", __LINE__,
n+1, k+1, str, ifl, (long unsigned)etab[eid].td[rmap[k]].Osize );
mi = 0; /* SKIP THIS ROW */
break;
}
break;
case 2: /* warn, skip */
fprintf ( stderr, "odb [Oload(%d)] - Error: row %d col %d field truncation. Input "
"string: >%s< of length %d. This row won't be loaded\n", __LINE__, n+1, k+1, str, ifl);
mi = 0; /* SKIP THIS ROW */
break;
case 3: /* truncate, warn, load */
fprintf ( stderr, "odb [Oload(%d)] - Warning: row %d col %d field truncation. Input "
"string: >%s< of length %d. First %lu char will be loaded\n", __LINE__,
n+1, k+1, str, ifl, (long unsigned)etab[eid].td[rmap[k]].Osize );
break;
case 4: /* truncate, load */
case 5: /* do not truncate */
break;
}
if ( etab[eid].fldtr != 5 )
ifl = (int)etab[eid].td[rmap[k]].Osize;
}
if ( ifl > 0 ) {
MEMCPY(Odp, str, ifl);
} else if ( ifl == 0 ) {
if (!etab[eid].ns) {
ifl = SQL_NULL_DATA;
}
} else if ( ifl == EMPTY ) {
ifl = 0 ;
}
else {
fprintf(stderr, "odb [Oload(%d)] - Error: get unexpected string length(%d) while parsing row %d col %d field.\n", __LINE__,
ifl, n + 1, k + 1);
}
}
*((SQLLEN *)(Odp + etab[eid].td[rmap[k]].Osize + etab[eid].td[rmap[k]].pad)) = (SQLLEN)ifl ;
}
if ( ( ++k == mff ) || ( fg & 0002 ) ) { /* we read all fields from file or nofile */
if ( fg & 0002 ) { /* nofile */
c = 1; /* reset current column to 1 to return here */
p = 0;
} else { /* real file */
c = 0; /* reset column number */
if (!etab[eid].mcrs) {
while ( p < len && buff[p] != lrs ) /* ... skip the rest of the line */
p++;
if ( p == len && buff[p-1] != lrs ) /* Continue cleaning rest of line in the next buffsz */
ccl = 1;
}
}
for ( j = 0 ; j < l ; j++ ) { /* now generate "artificial" fields for this record */
Odp = &etab[eid].Orowsetl[m*etab[eid].s + etab[eid].td[j].start];
switch ( map[j].idx ) {
case (-1): /* const */
MEMCPY(Odp, map[j].c, map[j].cl);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-2): /* seq */
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize, "%d", map[j].min++);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-3): /* irand */
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize, "%d",
(int)RAND(map[j].min, map[j].max));
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-4): /* drand */
rnd = (unsigned int)RAND(map[j].min, map[j].max); /* year */
i = (unsigned int)RAND(1, 12); /* month */
switch ( i ) { /* set date range based on month number */
case 1: /* January */
case 3: /* March */
case 5: /* May */
case 7: /* July */
case 8: /* August */
case 10: /* October */
case 12: /* December */
o = 31;
break;
case 4: /* April */
case 6: /* June */
case 9: /* September */
case 11: /* November */
o = 30;
break;
case 2: /* February */
if ( ( rnd % 400 ) == 0 ) {
o = 29;
} else if ( ( rnd % 100 ) == 0 ) {
o = 28;
} else if ( ( rnd % 4 ) == 0 ) {
o = 29;
} else {
o = 28;
}
break;
}
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize + 1,
"%04u-%02u-%02d", rnd, i, (int)RAND(1, o));
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = 10;
break;
case (-5): /* tmrand */
rnd = (unsigned int)RAND(0, 85399);
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize + 1,
"%02d:%02d:%02d", rnd/3600, ( rnd % 3600 ) / 60, rnd % 60);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-6): /* tsrand */
trnd = (time_t)RAND(0, map[j].max);
dt = localtime(&trnd);
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize + 1, "%04d-%02d-%02d %02d:%02d:%02d",
dt->tm_year+1900, dt->tm_mon+1, dt->tm_mday, dt->tm_hour, dt->tm_min, dt->tm_sec);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-7): /* crand */
for ( i = 0; i < map[j].cl ; i++ ) {
rnd = rand() % ( sizeof(alnum) - 1 ) ;
*(Odp + i) = (SQLCHAR)alnum[rnd] ;
}
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-8): /* emrand */
for ( i = 0 ; i < (unsigned int)RAND(map[j].min, map[j].max) ; i++) {
rnd = rand() % ( sizeof(alnum) - 1 ) ;
*(Odp+i) = (SQLCHAR)alnum[rnd] ;
}
*(Odp + i++ ) = '@';
for ( o = 0; o < (unsigned int)RAND(map[j].prec, map[j].scale) ; o++, i++) {
rnd = rand() % ( sizeof(alnum) - 1 ) ;
*(Odp+i) = (SQLCHAR)alnum[rnd] ;
}
*(Odp + i++ ) = '.';
rnd = (unsigned int)RAND(0, (int)(map[j].cl - 1));
for ( o = 0; o < map[j].eln[rnd] ; o++, i++)
*(Odp + i) = map[j].el[rnd][o];
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = i;
break;
case (-9): /* null */
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = SQL_NULL_DATA;
break;
case (-11): /* cdate */
trnd = time(NULL);
dt = localtime(&trnd);
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize + 1,
"%04d-%02d-%02d", dt->tm_year+1900, dt->tm_mon+1, dt->tm_mday);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-12): /* ctime */
trnd = time(NULL);
dt = localtime(&trnd);
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize + 1,
"%02d:%02d:%02d", dt->tm_hour, dt->tm_min, dt->tm_sec);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-13): /* ctstamp */
trnd = time(NULL);
dt = localtime(&trnd);
map[j].cl = snprintf((char *)Odp, etab[eid].td[j].Osize + 1, "%04d-%02d-%02d %02d:%02d:%02d",
dt->tm_year+1900, dt->tm_mon+1, dt->tm_mday, dt->tm_hour, dt->tm_min, dt->tm_sec);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
case (-14): /* dsrand */
case (-17): /* lstrand */
i = (unsigned int)RAND(0, (int)(map[j].cl - 1));
MEMCPY(Odp, map[j].el[i], map[j].eln[i]);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].eln[i];
break;
case (-15): /* txtrand */
i = (int)RAND(map[j].min, map[j].max);
MEMCPY(Odp, (map[j].c+(int)RAND(0, (int)map[j].cl-i)), i);
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = i;
break;
case (-16): /* nrand */
for ( i = 0; i < (unsigned int)map[j].max ; i++ )
*(Odp + i) = (SQLCHAR)RAND(48, 57) ;
*(Odp + i++) = (SQLCHAR)'.' ;
for ( ; i < (unsigned int)map[j].cl ; i++ )
*(Odp + i) = (SQLCHAR)RAND(48, 57) ;
*((SQLLEN *)(Odp + etab[eid].td[j].Osize + etab[eid].td[j].pad)) = map[j].cl;
break;
}
}
n++;
m += mi;
mi = 1;
nrf++;
k = 0;
if ( m == etab[eid].r || ( etab[eid].mr && nrf >= etab[eid].mr ) || ch == ( -1 ) ) { /* Insert the rowset */
oload_insert:
nt++;
while ( go ) {
if ( etab[eid].ps ) { /* Find a buffer loader thread available */
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0 ; i < nldr ; i++ ) { /* Look for write_available buffers */
if ( etab[ldrs[i]].lstat == 0 ) {
etab[ldrs[i]].lstat = 2; /* write_busy */
break;
}
}
if ( etab[eid].flg & 04000 ) { /* Oloadbuff set error flag */
break;
} else if ( i >= nldr ) { /* nothing available... wait */
nw++;
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
} else {
break ;
}
}
MutexUnlock(&etab[eid].pmutex);
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
break;
memcpy(etab[ldrs[i]].Orowsetl, etab[eid].Orowsetl, etab[eid].r * etab[eid].s);
etab[ldrs[i]].nbs = nrf - (unsigned long)m ;/* pass the input file base row for this rowset */
etab[ldrs[i]].lstat = 1; /* read_available */
etab[ldrs[i]].ar = m;
CondWakeAll(&etab[eid].pcvc);
} else if ( etab[eid].flg2 & 0020 ) { /* "show" mode */
pstats = 0;
printf("Record number %lu:\n", nrf);
printf("%4s %-30s %-20s %9s %3s %4s %7s %9s\n",
"COL#", "COLUMN_NAME", "ODBC_DATA_TYPE", "DISP_SZ", "DEC", "NULL", "FLD_LEN", "[FLD_VALUE]");
for ( i = 0 ; i < l ; i++ ) {
if ( !etab[eid].td[i].dl )
ifl = (-2); /* column value not loaded */
else
ifl = (int) *((SQLLEN *)(etab[eid].Orowsetl + etab[eid].td[i].start + etab[eid].td[i].Osize + etab[eid].td[i].pad));
printf("%4d %-30s %-20s %9u %3d %4s", i,
(char *) etab[eid].td[i].Oname,
expandtype(etab[eid].td[i].Otype),
(unsigned int)etab[eid].td[i].Osize,
(int) etab[eid].td[i].Odec,
etab[eid].td[i].Onull == SQL_NULLABLE ? "YES" : "NO");
switch ( ifl ) {
case -2: /* not loaded */
printf(" ? [(not loaded)]\n");
break;
case -1: /* SQL_NULL_DATA */
printf(" - [(null)]\n");
break;
case 0:
printf(" 0 [(empty)]\n");
break;
default:
printf(" %7d [", ifl);
for ( sp = (char *)(etab[eid].Orowsetl + etab[eid].td[i].start) ; ifl ; sp++, ifl-- )
fputc( *sp , stdout );
fputc( ']' , stdout );
fputc( '\n' , stdout );
}
}
} else {
etab[ldrs[0]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[0]].ar = m;
Oloadbuff(eid);
}
break;
}
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
goto oload_exit;
m = 0;
}
if ( ( etab[eid].mr && nrf >= etab[eid].mr ) || len == 0 )
goto oload_exit;
}
ifl = 0;
fg &= ~0060; /* reset field/record ready */
if ( rmap[k] >= 0 ) { /* rmap[k]=-1 for ignored fields */
ffstart = map[rmap[k]].min; /* set fixed field start */
ffend = map[rmap[k]].max; /* set fixed field end */
}
}
}
if ( fg & 0002 )
break;
}
oload_exit:
etab[eid].flg |= 02000; /* mark EOF */
if ( etab[eid].ps ) { /* wait all loaders to complete */
CondWakeAll(&etab[eid].pcvc);
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0, k=0 ; i < nldr ; i++ ) {
k += etab[ldrs[i]].lstat;
}
if ( k != nldr * 10 )
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
else
break;
}
MutexUnlock(&etab[eid].pmutex);
}
telaps -= 1000*tve.tv_sec + tve.tv_usec/1000;
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps += 1000*tve.tv_sec + tve.tv_usec/1000;
for ( i = 0 ; i < nldr ; i++ )
etab[eid].nrt += etab[ldrs[i]].nr;
/* Print results */
if ( pstats ) {
fprintf(stderr, "[%d] %s Load statistics:\n\t[%d] Target table: %s.%s.%s\n",
tid, odbid, tid, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
if ( etab[eid].flg2 & 01000000000 ) {
fprintf(stderr, "\t[%d] Source: [MAPR] %s\n", tid, etab[eid].src);
} else if ( etab[eid].flg2 & 0100 ) {
fprintf(stderr, "\t[%d] Source: [HDFS] %s\n", tid, etab[eid].src);
} else {
fprintf(stderr, "\t[%d] Source: %s\n", tid, etab[eid].src);
}
seconds = (double)tinit/1000.0 ;
fprintf(stderr, "\t[%d] Pre-loading time: %s s (%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
seconds = (double)telaps/1000.0 ;
fprintf(stderr, "\t[%d] Loading time: %s s(%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
fprintf(stderr, "\t[%d] Total records read: %s\n", tid, strmnum((double)nrf,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total records inserted: %s\n", tid, strmnum((double)etab[eid].nrt,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total number of columns: %s\n", tid, strmnum((double)l,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total bytes read: %s\n", tid, strmnum((double)nb,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Average input row size: %s B\n", tid, strmnum((double)nb*1.0/n,num,sizeof(num),1));
fprintf(stderr, "\t[%d] ODBC row size: %s B (data)", tid, strmnum((double)(etab[eid].s-l*sizeof(SQLLEN)),num,sizeof(num),0));
fprintf(stderr, " + %s B (len ind)\n", strmnum((double)(l*sizeof(SQLLEN)), num,sizeof(num),0));
fprintf(stderr, "\t[%d] Rowset size: %s\n", tid, strmnum((double)etab[eid].r,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Rowset buffer size: %s KiB\n", tid, strmnum((double)etab[eid].s/1024.0*etab[eid].r,num,sizeof(num),2));
fprintf(stderr, "\t[%d] Load throughput (real data): %s KiB/s\n", tid, strmnum((double)nb/1.024/telaps,num,sizeof(num),3));
fprintf(stderr, "\t[%d] Load throughput (ODBC): %s KiB/s\n", tid,
strmnum((double)((etab[eid].s-l*sizeof(SQLLEN))*etab[eid].nrt)/1.024/telaps,num,sizeof(num),3));
if ( etab[eid].ps ){
fprintf(stderr, "\t[%d] Reader Total/Wait Cycles: %s", tid, strmnum((double)nt,num,sizeof(num),0));
fprintf(stderr, "/%s\n", strmnum((double)nw,num,sizeof(num),0));
}
}
/* Unbind parameters (if parallel unbind is executed in Oloadbuff) */
if ( !etab[eid].ps )
(void)SQLFreeStmt(thps[tid].Os, SQL_RESET_PARAMS);
/* Close input file */
if ( isgz )
inflateEnd ( &gzstream ) ;
if (fl && fl != stdin)
fclose ( fl );
#ifdef HDFS
else if ( fhl )
(*hdfsclose)(hfs, fhl);
#endif
/* Free Memory */
if ( Odel )
free ( Odel );
if ( Oins )
free ( Oins );
if ( buff )
free ( buff );
if ( gzbuff )
free ( gzbuff );
if ( str )
free ( str );
if ( ldrs ) /* Free ldrs */
free(ldrs);
if ( etab[eid].Orowsetl ) /* Free Orowsetl if allocated */
free(etab[eid].Orowsetl);
if ( etab[eid].Ostatusl ) /* Free Ostatusl if allocated */
free(etab[eid].Ostatusl);
if ( etab[eid].td ) {
for ( i = 0; i < l ; i++)
free ( etab[eid].td[i].Oname );
free(etab[eid].td);
}
if ( map ) {
for ( i = 0 ; i < l ; i++ ) {
if ( map[i].c )
free ( map[i].c );
if ( map[i].el )
free ( map[i].el );
if ( map[i].eln )
free ( map[i].eln );
}
free ( map );
}
if ( rmap )
free (rmap );
/* Close bad file */
if ( etab[eid].fo != stderr )
fclose ( etab[eid].fo );
/* Run "post" SQL */
if ( etab[eid].post ) {
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].post[0] == '@' ) /* run a sql script */
(void)runsql(tid, eid, 0, (etab[eid].post + 1 ));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].post, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
}
return;
}
/* Oload2:
* load CSV files using parameters in etab[eid] (no map, no escapes, no string qualifiers
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void Oload2(int eid)
{
int tid = etab[eid].id; /* Thread ID */
SQLCHAR *Oins = 0; /* INSERT Statement */
SQLCHAR *Odel = 0; /* DELETE Statement */
SQLCHAR *O = 0; /* ODBC temp variable for memory realloc */
SQLRETURN Or=0; /* ODBC return value */
unsigned long nw=0, /* no of wait cycles */
nt=0, /* No of total write cycles */
nrf=0; /* no of read records */
long telaps = 0, /* Elapsed time in ms */
tinit = 0; /* Initialization time in ms */
size_t nb = 0, /* number of bytes read from input file */
cl = CMD_CHUNK, /* INSERT command buffer length */
cmdl = 0, /* INSERT command length */
dell = CMD_CHUNK, /* DELETE command length */
ifl = 0, /* input field length */
rl = 0, /* residual length */
len = 0; /* used to scroll the IO buffer */
int gzret = 0, /* zlib function return values */
z = 0; /* loop variable */
unsigned int nldr=0, /* number of loaders */
k=0, /* input file field number */
m = 0, /* rowset array record number */
i=0, /* loop variable */
pstats = 0, /* Error flag: 0 = print stats, 1 = don't print stats */
l=0, /* destination table fields */
j=0, /* loop variable */
lts = etab[eid].k , /* lines to skip */
isgz=0; /* input file is gzipped: 0=no , 1=yes */
int *ldrs=0; /* pointer to array containing loaders EIDs */
FILE *fl=0; /* data to load file pointer */
#ifdef HDFS
hdfsFile fhl = 0; /* Hadoop Input File Handle */
#endif
char *buff = 0, /* IO buffer */
*buff_save = 0, /* save original IO buffer pointer */
*gzbuff = 0, /* GZIP IO buffer */
*str = 0, /* field buffer */
*bc = 0; /* current field buffer size pointer */
char num[32]; /* Formatted Number String */
char tim[15]; /* Formatted Time String */
struct timeval tve; /* timeval struct to define elapesd/timelines */
SQLCHAR *Odp = 0; /* rowset buffer data pointer */
double seconds = 0; /* seconds used for timings */
z_stream gzstream = { 0 } ; /* zlib structure for gziped files */
unsigned char fg = 0, /* Oload flags:
0001 = in a quoted string 0020 = field ready
0010 = delimited fields 0040 = record ready
0100 = escape flag */
lfs = etab[eid].fs, /* local field separator */
lrs = etab[eid].rs, /* local record separator */
lsq = etab[eid].sq ? etab[eid].sq : '"'; /* local string qualifier, deafult is '"' */
/* Check if we have to use another ODBC connection */
if ( thps[tid].cr > 0 ) {
thps[tid].Oc = thps[thps[tid].cr].Oc;
thps[tid].Os = thps[thps[tid].cr].Os;
}
/* Set "tgt" variable */
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
/* Run "pre" SQL */
if ( etab[eid].pre ) {
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) /* run a sql script */
z = runsql(tid, eid, 0, (etab[eid].pre + 1 ));
else /* Run single SQL command */
z = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].pre, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( z && etab[eid].flg & 0004 )
goto oload2_exit;
etab[eid].nr = 0; /* reset number of resulting rows */
(void)SQLFreeHandle(SQL_HANDLE_STMT, thps[tid].Os);
if ( !SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT, thps[tid].Oc, &thps[tid].Os))){
Oerr(eid, tid, __LINE__, Oc, SQL_HANDLE_DBC);
goto oload2_exit;
}
}
/* Check database type */
etab[eid].dbt = checkdb( eid, &thps[tid].Oc, NULL, NULL);
/* Check if truncate */
if ( etab[eid].flg & 0002 ) {
etab[eid].flg &= ~0200000; /* if truncate... unset ifempty */
if ( ( Odel = malloc(dell) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating Odel memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
strmcpy((char *)Odel, dbscmds[etab[eid].dbt].trunc, dell);
Odel = (SQLCHAR *)var_exp((char *)Odel, &dell, &thps[tid].tva);
}
/* check ifempty */
if ( etab[eid].flg & 0200000 ) { /* ifempty is set */
if ( ifempty( eid, etab[eid].tgt ) ) {
fprintf(stderr, "odb [Oload2(%d)] - Target table %s is not empty\n",
__LINE__, etab[eid].tgt);
etab[eid].post = 0; /* prevent post SQL execution */
goto oload2_exit;
}
}
/* Allocate io buffer */
if ( ( buff = malloc((size_t)etab[eid].buffsz) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating IO buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
buff_save = buff ;
memset ( buff, 0, (size_t)etab[eid].buffsz);
/* Open input file */
if ( !strcmp ( etab[eid].src , "stdin" ) ) {
fl = stdin ;
} else {
for ( i = j = 0; i < etab[eid].buffsz && etab[eid].src[i]; i++ ) {
switch ( etab[eid].src[i] ) {
case '%':
switch ( etab[eid].src[++i] ) {
case 't':
j += strmcat (buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 2 );
break;
case 'T':
j += strmcat (buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 1 );
break;
case 's':
j += strmcat (buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 2 );
break;
case 'S':
j += strmcat (buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 1 );
break;
case 'c':
j += strmcat (buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 2 );
break;
case 'C':
j += strmcat (buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 1 );
break;
default:
fprintf(stderr, "odb [Oload2(%d)] - Invalid expansion %%%c in %s\n",
__LINE__, etab[eid].src[i], etab[eid].src );
goto oload2_exit;
}
break;
default:
buff[j++] = etab[eid].src[i];
break;
}
}
buff[j] = '\0';
if ( !(etab[eid].flg2 & 0100) && ( fl = fopen(buff, "r") ) == (FILE *) NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error opening input file %s: [%d] %s\n",
__LINE__, buff, errno, strerror(errno) );
goto oload2_exit;
}
}
#ifdef HDFS
/* Open HDFS input file */
if ( etab[eid].flg2 & 0100 ) { /* HDFS input file */
if ( ( fhl = (*hdfsopen)(hfs, buff, O_RDONLY, 0, 0, 0) ) == (hdfsFile) NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error opening hdfs file %s\n",
__LINE__, buff);
goto oload2_exit;
}
}
#endif
/* Analyze target table */
z = Otcol(eid, &thps[tid].Oc) ;
if ( z <= 0 ) {
fprintf(stderr, "odb [Oload2(%d)] - Error analyzing target table %s.%s.%s\n",
__LINE__, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
goto oload2_exit;
} else {
l = (unsigned int)z ;
}
/* Adjust Osize for WCHAR field (up to 4 bytes/char). Set buffer size */
if ( etab[eid].dbt != VERTICA ) { /* Vertica's CHAR field length is in bytes (not chars) */
for ( j = 0 ; j < l ; j++ ) {
switch ( etab[eid].td[j].Otype ) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpwc; /* Space for UTF-8 conversion */
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpc;/* Space for UTF-8 conversion */
break;
}
}
}
/* Determine buffer start positions and size */
for ( j = 0 ; j < l ; j++ ) {
etab[eid].td[j].start = etab[eid].s;
#ifdef __hpux
if ( etab[eid].td[j].Osize % WORDSZ )
etab[eid].td[j].pad = WORDSZ - etab[eid].td[j].Osize % WORDSZ ;
#endif
etab[eid].s += ( etab[eid].td[j].Osize + etab[eid].td[j].pad + sizeof(SQLLEN) ); /* space for length indicator */
}
/* Allocate field buffer */
if ((str = calloc(1, etab[eid].buffsz + 1)) == (void *)NULL) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating field buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
/* Truncate target table */
if ( etab[eid].flg & 0002 ) { /* truncate target table */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: Now truncating target table (%s)... ", (char *)Odel);
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
z = Oexec(tid, eid, 0, 0, Odel, ""); /* Run Truncate */
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( j && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Oload2(%d)] - Error truncating Target table. Exiting because Stop On Error was set\n",
__LINE__);
goto oload2_exit;
}
if ( etab[eid].cmt ) { /* Autocommit off: have to commit truncate */
if (!SQL_SUCCEEDED(Or=SQLEndTran(SQL_HANDLE_DBC, thps[tid].Oc, SQL_COMMIT))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
goto oload2_exit;
}
}
etab[eid].nr = 0; /* reset number of resulting rows */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "done\n");
}
/* Calculate rowset if buffer size is set */
if ( etab[eid].rbs ) {
etab[eid].r = etab[eid].rbs / etab[eid].s;
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; /* at least one record at a time */
}
if ( etab[eid].flg2 & 0001 ) /* commit as multiplier */
etab[eid].cmt *= (int)etab[eid].r ;
/* Build INSERT statement */
if ( ( Oins = malloc ( cl ) ) == (void *) NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating Oins memory\n", __LINE__);
goto oload2_exit;
}
if (!strcasecmp(etab[eid].loadcmd, "UL"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT USING LOAD %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
else if (!strcasecmp(etab[eid].loadcmd, "IN"))
{
cmdl += snprintf((char *)Oins, cl, "INSERT %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
else if (!strcasecmp(etab[eid].loadcmd, "UP"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
for (i = 0; i < l; i++) {
if ( i )
cl += strmcat ( (char *) Oins, ",", cl, 0);
cl += strmcat ( (char *) Oins, "?", cl, 0);
if ( cmdl + CMD_CHUNK < cl ) { /* increase Oins buffer */
cl += CMD_CHUNK;
O = Oins ;
if ( ( O = realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
Oins = O ;
}
}
(void) strmcat ( (char *) Oins, ")", cl, 0);
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [Oload2(%d)] - INSERT statement: %s\n", __LINE__, (char *)Oins);
/* Allocate loader eids array */
nldr = etab[eid].ps ? etab[eid].ps : 1;
if ( ( ldrs = (int *)calloc ( nldr, sizeof ( int ) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating loader eids array: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
/* Initialize loader eids array */
if ( etab[eid].ps ) {
for ( i = 0, j = 0 ; i < (unsigned int)no && j < etab[eid].ps ; i++ ){
if ( etab[i].type == 'L' && etab[i].parent == eid ){
etab[i].r = etab[eid].r; /* adjust rowset when rbs is specified */
etab[i].cmt = etab[eid].cmt;/* adjust rowset when cmt is a multiplier */
etab[i].lstat = 0; /* reset buffer status to available */
etab[i].td = etab[eid].td; /* copy table structure pointer */
ldrs[j++] = i;
}
}
} else {
ldrs[0] = eid;
}
/* Allocate rowset & status memory */
if ( (Odp = etab[eid].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[eid].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
if ( etab[eid].ps ) {
for ( j = 0 ; j < nldr ; j++ ) { /* for all loading threads... */
if ( (etab[ldrs[j]].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[ldrs[j]].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
}
}
/* Set rowset size, bind parameters and prepare INSERT */
for ( j = 0 ; j < nldr ; j++ ) {
/* Set max commit mode */
if ( etab[eid].cmt ) {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oload2_exit;
}
} else {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oload2_exit;
}
}
/* Set max char/varchar/binary column length */
if (!SQL_SUCCEEDED(Oret=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_MAX_LENGTH,
(SQLPOINTER)etab[eid].Omaxl, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
}
/* Bind parameters */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload2_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload2_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_STATUS_PTR,
etab[ldrs[j]].Ostatusl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload2_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMS_PROCESSED_PTR,
&etab[ldrs[j]].Oresl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload2_exit;
}
for ( i = 0, z = 1; i < l; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLBindParameter(thps[etab[ldrs[j]].id].Os, (SQLUSMALLINT)z++,
SQL_PARAM_INPUT, SQL_C_CHAR, etab[eid].td[i].Otype, (SQLULEN)etab[eid].td[i].Osize,
etab[eid].td[i].Odec, &etab[ldrs[j]].Orowsetl[0+etab[eid].td[i].start], etab[eid].td[i].Osize,
(SQLLEN *)&etab[ldrs[j]].Orowsetl[0+etab[eid].td[i].start+etab[eid].td[i].Osize+etab[eid].td[i].pad]))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload2_exit;
}
}
if (!SQL_SUCCEEDED(Or=SQLPrepare(thps[etab[ldrs[j]].id].Os, Oins, SQL_NTS))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oload2_exit;
}
etab[ldrs[j]].sp = l; /* save number of target table fields for prec */
}
/* Register Load Start Time */
gettimeofday(&tve, (void *)NULL);
tinit = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
/* Determine if the input file is gzipped by checking the first 2 chars */
if ( fl ) {
if ( ( fl != stdin ) && ( len = fread ( buff, 1, 2, fl) ) == 2 ) {
if ( (unsigned char)buff[0] == 037 &&
(unsigned char)buff[1] == 0213 ) { /* gzipped file */
isgz = 1 ;
}
(void) fseek(fl, 0L, SEEK_SET); /* rewind */
}
#ifdef HDFS
} else if ( fhl ) {
if ( ( len = (size_t)(*hdfsread)(hfs, fhl, (void *)buff, 2) ) == 2 ) {
if ( (unsigned char)buff[0] == 037 &&
(unsigned char)buff[1] == 0213 ) { /* gzipped file */
isgz = 1 ;
}
(void)(*hdfsseek)(hfs, fhl, 0); /* rewind */
}
#endif
}
/* Allocate buffer for gzip read and initialize inflate */
if ( isgz ) {
/* Allocate gzip io buffer */
if ( ( gzbuff = malloc((size_t)etab[eid].buffsz) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload2(%d)] - Error allocating gzip IO buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oload2_exit;
}
memset ( gzbuff, 0, (size_t)etab[eid].buffsz);
/* Initalize inflate */
if ( ( gzret = inflateInit2 (&gzstream, windowBits | ENABLE_ZLIB_GZIP) ) != Z_OK ) {
fprintf(stderr, "odb [Oload2(%d)] - Error initializing zlib: [%d]\n",
__LINE__, gzret);
goto oload2_exit;
}
}
/* Read input file */
pstats = 1 ; /* From now on print stats on exit */
while ( go ) {
if ( isgz ) { /* is a gzipped file */
if ( gzstream.avail_in ) { /* continue inflating previous gzbuff into buff */
gzstream.avail_out = (unsigned int)etab[eid].buffsz ;
gzstream.next_out = (unsigned char *)buff ;
gzret = inflate (&gzstream, Z_NO_FLUSH) ;
switch ( gzret ) {
case Z_OK:
break ; /* everything is ok - continue */
case Z_STREAM_END:
inflateReset ( &gzstream ) ;
break;
default:
fprintf(stderr, "odb [Oload2(%d)] - Error during deflate: [%d]\n",
__LINE__, gzret);
goto oload2_exit ;
break;
}
len = etab[eid].buffsz - gzstream.avail_out ;
} else {
if ( fl ) { /* read new data from normal file-system into gzbuff */
len = fread ( gzbuff, 1, (size_t)etab[eid].buffsz, fl);
#ifdef HDFS
} else if ( fhl ) { /* read new data from HDFS into gzbuff */
len = (size_t)(*hdfsread)(hfs, fhl, (void *)gzbuff, etab[eid].buffsz);
#endif
}
gzstream.avail_in = (unsigned int) len ;
gzstream.next_in = (unsigned char *)gzbuff ;
if ( len )
continue ;
}
} else if ( fl ) {
len = fread ( buff, 1, (size_t)etab[eid].buffsz, fl);
#ifdef HDFS
} else if ( fhl ) {
len = (size_t)(*hdfsread)(hfs, fhl, (void *)buff, etab[eid].buffsz);
#endif
}
if ( len == 0 ) { /* EOF */
if ( m ) { /* rows to be inserted */
if ( k == ( l - 1 ) ) { /* flush last row not terminated by RS */
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad - rl ;
*((SQLLEN *)(Odp)) = (SQLLEN)rl ;
m++ ;
nrf++ ;
}
goto oload2_insert;
} else { /* exit loop */
break;
}
}
nb += len; /* update bytes read from file */
while ( lts && len ) { /* skip initial lines */
if ( *buff++ == lrs ) {
lts-- ;
}
len-- ;
}
bc = buff ;
for (size_t currentFieldPos = 0; currentFieldPos < len;) {
ifl = 0;
for (; currentFieldPos < len; ++currentFieldPos) {
if ((fg & 0001) && (fg & 0100) && (bc[currentFieldPos] == lfs || bc[currentFieldPos] == lrs)) { /* treat string qualifier before as end quote */
fg &= ~0101;
}
if ((bc[currentFieldPos] == lfs) && !(fg & 0001)) { /* if field sep... */
fg |= 0020; /* set field complete flag on */
++currentFieldPos;
break;
}
else if ((bc[currentFieldPos] == lrs) && !(fg & 0001)) { /* if record sep... */
fg |= 0040; /* set record complete flag on */
++currentFieldPos;
break;
}
else if ((bc[currentFieldPos] == lsq) && (fg & 0001) && !(fg & 0100)) { /* if string qualifier char and in string qualifier */
fg |= 0100;
}
else if ((bc[currentFieldPos] == lsq) && !(fg & 0100)) {
fg ^= 0001; /* flip quoted string flag */
}
else { /* add new character to field buffer */
str[ifl++] = bc[currentFieldPos];
fg &= ~0100; /* set escape flag off */
}
}
if (rl + ifl > (size_t)(etab[eid].td[k].Osize)) { /* prevent Orowsetl[] overflow */
char *tmpbuf = (char*)malloc(rl + ifl + 1);
strncpy(tmpbuf, (const char*)(Odp - rl), rl);
strncpy(tmpbuf + rl, str, ifl);
tmpbuf[rl + ifl] = '\0';
fprintf(stderr, "odb [Oload2(%d)] - Error: row %lu col %u field truncation. Input "
"string: >%s< of length %lu.\n", __LINE__, nrf + 1, k + 1, tmpbuf, ifl);
free(tmpbuf);
goto oload2_exit;
}
if ( fg & 0060 ) { /* field complete */
fg &= 0; /* reset flags */
if ( ifl ) {
MEMCPY(Odp, str, ifl);
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad - rl ;
*((SQLLEN *)(Odp)) = (SQLLEN)(ifl+rl) ;
} else if ( rl ) {
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad - rl ;
*((SQLLEN *)(Odp)) = (SQLLEN)rl ;
} else {
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad - rl ;
*((SQLLEN *)(Odp)) = (SQLLEN)SQL_NULL_DATA ;
}
rl = 0 ;
Odp += sizeof(SQLLEN) ;
if ( ++k == l ) { /* row completed */
k = 0 ;
m++ ;
nrf++ ;
Odp = &etab[eid].Orowsetl[m*etab[eid].s];
}
} else { /* field incomplete */
rl += ifl; /* = change to +=, for = may cause potential bug */
if ( ifl )
MEMCPY(Odp, str, ifl);
memset(str, '\0', ifl);
Odp += ifl ;
break;
}
if ( m == etab[eid].r ) { /* Insert rowset */
oload2_insert:
nt++;
while ( go ) {
if ( etab[eid].ps ) { /* Find a buffer loader thread available */
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0 ; i < nldr ; i++ ) { /* Look for write_available buffers */
if ( etab[ldrs[i]].lstat == 0 ) {
etab[ldrs[i]].lstat = 2; /* write_busy */
break;
}
}
if ( etab[eid].flg & 04000 ) { /* Oloadbuff set error flag */
break;
} else if ( i >= nldr ) { /* nothing available... wait */
nw++;
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
} else {
break ;
}
}
MutexUnlock(&etab[eid].pmutex);
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
break;
memcpy(etab[ldrs[i]].Orowsetl, etab[eid].Orowsetl, m * etab[eid].s);
etab[ldrs[i]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[i]].lstat = 1; /* read_available */
etab[ldrs[i]].ar = m;
CondWakeAll(&etab[eid].pcvc);
} else {
etab[ldrs[0]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[0]].ar = m;
Oloadbuff(eid);
}
break;
}
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
goto oload2_exit;
m = 0;
k = 0;
Odp = etab[eid].Orowsetl ;
}
}
}
oload2_exit:
etab[eid].flg |= 02000; /* mark EOF */
if ( etab[eid].ps ) { /* wait all loaders to complete */
CondWakeAll(&etab[eid].pcvc);
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0, k=0 ; i < nldr ; i++ ) {
k += etab[ldrs[i]].lstat;
}
if ( k != nldr * 10 )
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
else
break;
}
MutexUnlock(&etab[eid].pmutex);
}
telaps -= 1000*tve.tv_sec + tve.tv_usec/1000;
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps += 1000*tve.tv_sec + tve.tv_usec/1000;
for ( i = 0 ; i < nldr ; i++ )
etab[eid].nrt += etab[ldrs[i]].nr;
/* Print results */
if ( pstats ) {
fprintf(stderr, "[%d] %s Load(2) statistics:\n\t[%d] Target table: %s.%s.%s\n",
tid, odbid, tid, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
if ( etab[eid].flg2 & 01000000000 ) {
fprintf(stderr, "\t[%d] Source: [MAPR] %s\n", tid, etab[eid].src);
} else if ( etab[eid].flg2 & 0100 ) {
fprintf(stderr, "\t[%d] Source: [HDFS] %s\n", tid, etab[eid].src);
} else {
fprintf(stderr, "\t[%d] Source: %s\n", tid, etab[eid].src);
}
seconds = (double)tinit/1000.0 ;
fprintf(stderr, "\t[%d] Pre-loading time: %s s (%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
seconds = (double)telaps/1000.0 ;
fprintf(stderr, "\t[%d] Loading time: %s s(%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
fprintf(stderr, "\t[%d] Total records read: %s\n", tid, strmnum((double)nrf,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total records inserted: %s\n", tid, strmnum((double)etab[eid].nrt,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total number of columns: %s\n", tid, strmnum((double)l,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total bytes read: %s\n", tid, strmnum((double)nb,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Average input row size: %s B\n", tid, strmnum((double)nb*1.0/nrf,num,sizeof(num),1));
fprintf(stderr, "\t[%d] ODBC row size: %s B (data)", tid, strmnum((double)(etab[eid].s-l*sizeof(SQLLEN)),num,sizeof(num),0));
fprintf(stderr, " + %s B (len ind)\n", strmnum((double)(l*sizeof(SQLLEN)), num,sizeof(num),0));
fprintf(stderr, "\t[%d] Rowset size: %s\n", tid, strmnum((double)etab[eid].r,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Rowset buffer size: %s KiB\n", tid, strmnum((double)etab[eid].s/1024.0*etab[eid].r,num,sizeof(num),2));
fprintf(stderr, "\t[%d] Load throughput (real data): %s KiB/s\n", tid, strmnum((double)nb/1.024/telaps,num,sizeof(num),3));
fprintf(stderr, "\t[%d] Load throughput (ODBC): %s KiB/s\n", tid,
strmnum((double)((etab[eid].s-l*sizeof(SQLLEN))*etab[eid].nrt)/1.024/telaps,num,sizeof(num),3));
if ( etab[eid].ps ){
fprintf(stderr, "\t[%d] Reader Total/Wait Cycles: %s", tid, strmnum((double)nt,num,sizeof(num),0));
fprintf(stderr, "/%s\n", strmnum((double)nw,num,sizeof(num),0));
}
}
/* Unbind parameters (if parallel unbind is executed in Oloadbuff) */
if ( !etab[eid].ps )
(void)SQLFreeStmt(thps[tid].Os, SQL_RESET_PARAMS);
/* Close input file */
if ( isgz )
inflateEnd ( &gzstream );
if (fl && fl != stdin)
fclose ( fl );
#ifdef HDFS
else if ( fhl )
(*hdfsclose)(hfs, fhl);
#endif
/* Free Memory */
if ( Odel )
free ( Odel );
if ( Oins )
free ( Oins );
if ( gzbuff )
free ( gzbuff );
if ( buff_save )
free ( buff_save );
if ( ldrs ) /* Free ldrs */
free(ldrs);
if ( etab[eid].Orowsetl ) /* Free Orowsetl if allocated */
free(etab[eid].Orowsetl);
if ( etab[eid].Ostatusl ) /* Free Ostatusl if allocated */
free(etab[eid].Ostatusl);
if ( etab[eid].td ) {
for ( i = 0; i < l ; i++)
free ( etab[eid].td[i].Oname );
free(etab[eid].td);
}
if (str) {
free(str);
}
/* Close bad file */
if ( etab[eid].fo != stderr )
fclose ( etab[eid].fo );
/* Run "post" SQL */
if ( etab[eid].post ) {
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].post[0] == '@' ) /* run a sql script */
(void)runsql(tid, eid, 0, (etab[eid].post + 1 ));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].post, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
}
return;
}
#ifdef XML
/* OloadX:
* load XML files using parameters in etab[eid]
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void OloadX(int eid)
{
int tid = etab[eid].id; /* Thread ID */
SQLCHAR *Oins = 0; /* INSERT Statement */
SQLCHAR *Odel = 0; /* DELETE Statement */
SQLCHAR *O = 0; /* ODBC temp variable for memory realloc */
SQLRETURN Or=0; /* ODBC return value */
unsigned long nw=0, /* no of wait cycles */
nt=0, /* No of total write cycles */
nrf=0; /* no of read records */
long telaps = 0, /* Elapsed time in ms */
tinit = 0; /* Initialization time in ms */
size_t nb = 0, /* number of bytes read from input file */
cl = CMD_CHUNK, /* INSERT command buffer length */
cmdl = 0, /* INSERT command length */
dell = CMD_CHUNK, /* DELETE command length */
ifl = 0; /* input field length */
int z = 0; /* loop variable */
unsigned int nldr=0, /* number of loaders */
k=0, /* input file field number */
m = 0, /* rowset array record index */
i=0, /* loop variable */
pstats = 0, /* Error flag: 0 = print stats, 1 = don't print stats */
l=0, /* destination table fields */
j=0; /* loop variable */
int *ldrs=0; /* pointer to array containing loaders EIDs */
char buff[128]; /* buffer to build output file name */
char num[32]; /* Formatted Number String */
char tim[15]; /* Formatted Time String */
struct timeval tve; /* timeval struct to define elapesd/timelines */
SQLCHAR *Odp = 0; /* rowset buffer data pointer */
double seconds = 0; /* seconds used for timings */
xmlTextReaderPtr xread = 0; /* XML reader */
int xnt = 0 ; /* xml node type */
int xnd = 0 ; /* xml node depth */
int xrtnd = 0 ; /* xml row tag node depth */
unsigned int xpflag = 1 ; /* xml process node flag: 0=no, 1=yes */
unsigned int xdump = 0 ; /* xml dump flag: 0=no, 1=yes */
unsigned int xttype = 0 ; /* xml tag type : 0=text, 1=attr */
char *xvalue = 0 ; /* xml return value */
char *xname = 0 ; /* xml node name */
/* Check if we have to use another ODBC connection */
if ( thps[tid].cr > 0 ) {
thps[tid].Oc = thps[thps[tid].cr].Oc;
thps[tid].Os = thps[thps[tid].cr].Os;
}
/* Set "tgt" variable */
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
/* Run "pre" SQL */
if ( etab[eid].pre ) {
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) /* run a sql script */
z = runsql(tid, eid, 0, (etab[eid].pre + 1 ));
else /* Run single SQL command */
z = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].pre, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( z && etab[eid].flg & 0004 )
goto oloadX_exit;
etab[eid].nr = 0; /* reset number of resulting rows */
(void)SQLFreeHandle(SQL_HANDLE_STMT, thps[tid].Os);
if ( !SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT, thps[tid].Oc, &thps[tid].Os))){
Oerr(eid, tid, __LINE__, Oc, SQL_HANDLE_DBC);
goto oloadX_exit;
}
}
/* Check database type */
etab[eid].dbt = checkdb( eid, &thps[tid].Oc, NULL, NULL);
/* Check if truncate */
if ( etab[eid].flg & 0002 ) {
etab[eid].flg &= ~0200000; /* if truncate... unset ifempty */
if ( ( Odel = malloc(dell) ) == (void *)NULL ) {
fprintf(stderr, "odb [OloadX(%d)] - Error allocating Odel memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadX_exit;
}
strmcpy((char *)Odel, dbscmds[etab[eid].dbt].trunc, dell);
Odel = (SQLCHAR *)var_exp((char *)Odel, &dell, &thps[tid].tva);
}
/* check ifempty */
if ( etab[eid].flg & 0200000 ) { /* ifempty is set */
if ( ifempty( eid, etab[eid].tgt ) ) {
fprintf(stderr, "odb [OloadX(%d)] - Target table %s is not empty\n",
__LINE__, etab[eid].tgt);
etab[eid].post = 0; /* prevent post SQL execution */
goto oloadX_exit;
}
}
/* Initialize xttype & xdump */
if ( etab[eid].flg2 & 02000000 )
xttype = 1 ;
if ( etab[eid].flg2 & 0200000000 )
xdump = 1 ;
/* Open input file */
for ( i = j = 0; i < sizeof(buff) && etab[eid].src[i]; i++ ) {
switch ( etab[eid].src[i] ) {
case '%':
switch ( etab[eid].src[++i] ) {
case 't':
j += strmcat (buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 2 );
break;
case 'T':
j += strmcat (buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 1 );
break;
case 's':
j += strmcat (buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 2 );
break;
case 'S':
j += strmcat (buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 1 );
break;
case 'c':
j += strmcat (buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 2 );
break;
case 'C':
j += strmcat (buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 1 );
break;
default:
fprintf(stderr, "odb [OloadX(%d)] - Invalid expansion %%%c in %s\n",
__LINE__, etab[eid].src[i], etab[eid].src );
goto oloadX_exit;
}
break;
default:
buff[j++] = etab[eid].src[i];
break;
}
}
buff[j] = '\0';
/* Open input file */
if ( etab[eid].xrt ) {
if ( ( xread = (*xmlfile)( buff ) ) == (xmlTextReaderPtr) NULL ) {
fprintf(stderr, "odb [OloadX(%d} - Error opening XML input file %s\n", __LINE__, etab[eid].src);
goto oloadX_exit;
}
}
/* Analyze target table */
z = Otcol(eid, &thps[tid].Oc) ;
if ( z <= 0 ) {
fprintf(stderr, "odb [OloadX(%d)] - Error analyzing target table %s.%s.%s\n",
__LINE__, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
goto oloadX_exit;
} else {
l = (unsigned int)z ;
}
/* Adjust Osize for WCHAR field (up to 4 bytes/char). Set buffer size */
if ( etab[eid].dbt != VERTICA ) { /* Vertica's CHAR field length is in bytes (not chars) */
for ( j = 0 ; j < l ; j++ ) {
switch ( etab[eid].td[j].Otype ) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpwc; /* Space for UTF-8 conversion */
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpc;/* Space for UTF-8 conversion */
break;
}
}
}
/* Determine buffer start positions and size */
for ( j = 0 ; j < l ; j++ ) {
etab[eid].td[j].start = etab[eid].s;
#ifdef __hpux
if ( etab[eid].td[j].Osize % WORDSZ )
etab[eid].td[j].pad = WORDSZ - etab[eid].td[j].Osize % WORDSZ ;
#endif
etab[eid].s += ( etab[eid].td[j].Osize + etab[eid].td[j].pad + sizeof(SQLLEN) ); /* space for length indicator */
}
/* Truncate target table */
if ( etab[eid].flg & 0002 ) { /* truncate target table */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: Now truncating target table (%s)... ", (char *)Odel);
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
z = Oexec(tid, eid, 0, 0, Odel, ""); /* Run Truncate */
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( j && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [OloadX(%d)] - Error truncating Target table. Exiting because Stop On Error was set\n",
__LINE__);
goto oloadX_exit;
}
if ( etab[eid].cmt ) { /* Autocommit off: have to commit truncate */
if (!SQL_SUCCEEDED(Or=SQLEndTran(SQL_HANDLE_DBC, thps[tid].Oc, SQL_COMMIT))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
goto oloadX_exit;
}
}
etab[eid].nr = 0; /* reset number of resulting rows */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "done\n");
}
/* Calculate rowset if buffer size is set */
if ( etab[eid].rbs ) {
etab[eid].r = etab[eid].rbs / etab[eid].s;
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; /* at least one record at a time */
}
if ( etab[eid].flg2 & 0001 ) /* commit as multiplier */
etab[eid].cmt *= (int)etab[eid].r ;
/* Build INSERT statement */
if ( ( Oins = malloc ( cl ) ) == (void *) NULL ) {
fprintf(stderr, "odb [OloadX(%d)] - Error allocating Oins memory\n", __LINE__);
goto oloadX_exit;
}
if (!strcasecmp(etab[eid].loadcmd, "UL"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT USING LOAD %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
else if (!strcasecmp(etab[eid].loadcmd, "UP"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
else if (!strcasecmp(etab[eid].loadcmd, "IN"))
{
cmdl += snprintf((char *)Oins, cl, "INSERT %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
for (i = 0; i < l; i++) {
if ( i )
cl += strmcat ( (char *) Oins, ",", cl, 0);
cl += strmcat ( (char *) Oins, "?", cl, 0);
if ( cmdl + CMD_CHUNK < cl ) { /* increase Oins buffer */
cl += CMD_CHUNK;
O = Oins ;
if ( ( O = realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [OloadX(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadX_exit;
}
Oins = O ;
}
}
(void) strmcat ( (char *) Oins, ")", cl, 0);
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb [OloadX(%d)] - INSERT statement: %s\n", __LINE__, (char *)Oins);
/* Allocate loader eids array */
nldr = etab[eid].ps ? etab[eid].ps : 1;
if ( ( ldrs = (int *)calloc ( nldr, sizeof ( int ) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [OloadX(%d)] - Error allocating loader eids array: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadX_exit;
}
/* Initialize loader eids array */
if ( etab[eid].ps ) {
for ( i = 0, j = 0 ; i < (unsigned int)no && j < etab[eid].ps ; i++ ){
if ( etab[i].type == 'L' && etab[i].parent == eid ){
etab[i].r = etab[eid].r; /* adjust rowset when rbs is specified */
etab[i].cmt = etab[eid].cmt;/* adjust rowset when cmt is a multiplier */
etab[i].lstat = 0; /* reset buffer status to available */
etab[i].td = etab[eid].td; /* copy table structure pointer */
ldrs[j++] = i;
}
}
} else {
ldrs[0] = eid;
}
/* Allocate rowset & status memory */
if ( (Odp = etab[eid].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[eid].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [OloadX(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadX_exit;
}
if ( etab[eid].ps ) {
for ( j = 0 ; j < nldr ; j++ ) { /* for all loading threads... */
if ( (etab[ldrs[j]].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[ldrs[j]].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [OloadX(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadX_exit;
}
}
}
/* Set rowset size, bind parameters and prepare INSERT */
for ( j = 0 ; j < nldr ; j++ ) {
/* Set max commit mode */
if ( etab[eid].cmt ) {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oloadX_exit;
}
} else {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oloadX_exit;
}
}
/* Set max char/varchar/binary column length */
if (!SQL_SUCCEEDED(Oret=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_MAX_LENGTH,
(SQLPOINTER)etab[eid].Omaxl, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
}
/* Bind parameters */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadX_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadX_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_STATUS_PTR,
etab[ldrs[j]].Ostatusl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadX_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMS_PROCESSED_PTR,
&etab[ldrs[j]].Oresl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadX_exit;
}
for ( i = 0, z = 1; i < l; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLBindParameter(thps[etab[ldrs[j]].id].Os, (SQLUSMALLINT)z++,
SQL_PARAM_INPUT, SQL_C_CHAR, etab[eid].td[i].Otype, (SQLULEN)etab[eid].td[i].Osize,
etab[eid].td[i].Odec, &etab[ldrs[j]].Orowsetl[0+etab[eid].td[i].start], etab[eid].td[i].Osize,
(SQLLEN *)&etab[ldrs[j]].Orowsetl[0+etab[eid].td[i].start+etab[eid].td[i].Osize+etab[eid].td[i].pad]))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadX_exit;
}
}
if (!SQL_SUCCEEDED(Or=SQLPrepare(thps[etab[ldrs[j]].id].Os, Oins, SQL_NTS))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadX_exit;
}
etab[ldrs[j]].sp = l; /* save number of target table fields for prec */
}
/* Register Load Start Time */
gettimeofday(&tve, (void *)NULL);
tinit = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
/* XML load */
pstats = 1 ; /* from now on print stats on exit */
while ( ( z = (*xmlread)(xread) ) == 1 ) {
xnt = (*xmltype)(xread) ; /* save node type */
xnd = (*xmldepth)(xread) ; /* save node depth */
if ( !strcmp(etab[eid].xrt, (char *)(*xmllname)(xread)) ) {
if ( xnt == 1 ) { /* rowtag start */
if ( xttype ) { /* process attributes */
k = 0 ; /* reset field number counter */
while ( (*xmlnextattr)(xread) ) {
xname = (char *)(*xmllname)(xread) ;
xvalue = (char *)(*xmlvalue)(xread) ;
if ( xdump ) { /* XML dump: print key/value */
printf("%s: %s\n", xname, xvalue);
} else { /* XML Load */
ifl = strlen(xvalue) ;
if ( etab[eid].flg2 & 010000000 ) { /* xmlord is set */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start ;
MEMCPY(Odp, xvalue, ifl);
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad ;
*((SQLLEN *)(Odp)) = (SQLLEN)(ifl) ;
if ( ++k == l )
break ;
} else { /* xmlord not set: use attr names */
for ( k = 0 ; k < l ; k++ ) {
if ( !strmicmp((char *)etab[eid].td[k].Oname, xname, strlen(xname) ) ) { /* name matches */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start ;
MEMCPY(Odp, xvalue, ifl);
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad ;
*((SQLLEN *)(Odp)) = (SQLLEN)(ifl) ;
break;
}
}
}
}
}
} else { /* process nodes */
xpflag = 1 ;
xrtnd = xnd ;
}
} else if ( xnt == 15 ) { /* rowtag end */
m++ ; /* increase rowset index */
nrf++ ; /* increase "read rows" counter */
k = 0 ; /* reset field number counter to zero */
xpflag = 0; /* set process flag to zero */
}
} else if ( xnd > xrtnd + 2 ){
xpflag = 0 ; /* too deep: set process flag to zero */
}
/* process sub-rowtag nodes of depth 1+1 */
if ( xpflag ) {
switch ( xnt ) {
case 1: /* node start */
if ( ( xnd - xrtnd ) == 1 ) {
xname = (char *)(*xmllname)(xread);
xvalue = 0 ;
}
break;
case 3: /* text node */
if ( ( xnd - xrtnd ) == 2 ) {
xvalue = (char *)(*xmlvalue)(xread);
ifl = strlen(xvalue) ;
if (ifl > etab[eid].td[k].Osize) { // prevent Orowsetl[] overflow
fprintf(stderr, "odb [OloadX(%d)] - Error: row %lu col %u field truncation. Input "
"string: >%s< of length %lu.\n", __LINE__, nrf + 1, k + 1, xvalue, ifl);
goto oloadX_exit;
}
if ( xdump ) {
printf("%s: %s\n", xname, xvalue);
} else {
if ( etab[eid].flg2 & 010000000 ) { /* xmlord is set */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start ;
MEMCPY(Odp, xvalue, ifl);
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad ;
*((SQLLEN *)(Odp)) = (SQLLEN)(ifl) ;
} else { /* xmlord not set: use attr names */
for ( k = 0 ; k < l ; k++ ) {
if ( !strmicmp((char *)etab[eid].td[k].Oname, xname, strlen(xname) ) ) { /* name matches */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start ;
MEMCPY(Odp, xvalue, ifl);
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad ;
*((SQLLEN *)(Odp)) = (SQLLEN)(ifl) ;
break;
}
}
}
}
}
break;
case 15: /* node end */
if ( ( xnd - xrtnd ) == 1 ) {
if ( xvalue == 0 ) {/* text node was empty */
if ( xdump ) {
printf("%s: %s\n", xname, xvalue);
} else {
if ( etab[eid].flg2 & 010000000 ) { /* xmlord is set */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start +
etab[eid].td[k].Osize + etab[eid].td[k].pad ;
*((SQLLEN *)(Odp)) = (SQLLEN)SQL_NULL_DATA ;
if ( ++k == l )
break ;
} else { /* xmlord not set: use attr names */
for ( k = 0 ; k < l ; k++ ) {
if ( !strmicmp((char *)etab[eid].td[k].Oname, xname, strlen(xname) ) ) { /* name matches */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start +
etab[eid].td[k].Osize + etab[eid].td[k].pad ;
*((SQLLEN *)(Odp)) = (SQLLEN)(SQL_NULL_DATA) ;
break;
}
}
}
}
}
}
if ( ++k > l )
k = 0 ;
break;
}
}
if ( m == etab[eid].r ) { /* Insert rowset */
nt++;
while ( go ) {
if ( etab[eid].ps ) { /* Find a buffer loader thread available */
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0 ; i < nldr ; i++ ) { /* Look for write_available buffers */
if ( etab[ldrs[i]].lstat == 0 ) {
etab[ldrs[i]].lstat = 2; /* write_busy */
break;
}
}
if ( etab[eid].flg & 04000 ) { /* Oloadbuff set error flag */
break;
} else if ( i >= nldr ) { /* nothing available... wait */
nw++;
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
} else {
break ;
}
}
MutexUnlock(&etab[eid].pmutex);
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
break;
memcpy(etab[ldrs[i]].Orowsetl, etab[eid].Orowsetl, m * etab[eid].s);
etab[ldrs[i]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[i]].lstat = 1; /* read_available */
etab[ldrs[i]].ar = m;
CondWakeAll(&etab[eid].pcvc);
} else {
etab[ldrs[0]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[0]].ar = m;
Oloadbuff(eid);
}
break;
}
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
break ;
m = 0;
k = 0;
}
}
(*xmlfree)(xread);
/* load trailing rows */
if ( m ) { /* Insert rowset */
nt++;
while ( go ) {
if ( etab[eid].ps ) { /* Find a buffer loader thread available */
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0 ; i < nldr ; i++ ) { /* Look for write_available buffers */
if ( etab[ldrs[i]].lstat == 0 ) {
etab[ldrs[i]].lstat = 2; /* write_busy */
break;
}
}
if ( etab[eid].flg & 04000 ) { /* Oloadbuff set error flag */
break;
} else if ( i >= nldr ) { /* nothing available... wait */
nw++;
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
} else {
break ;
}
}
MutexUnlock(&etab[eid].pmutex);
if ( etab[eid].flg & 04000 ) /* Oloadbuff set error flag */
break;
memcpy(etab[ldrs[i]].Orowsetl, etab[eid].Orowsetl, m * etab[eid].s);
etab[ldrs[i]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[i]].lstat = 1; /* read_available */
etab[ldrs[i]].ar = m;
CondWakeAll(&etab[eid].pcvc);
} else {
etab[ldrs[0]].nbs = nrf - (unsigned long)m ; /* pass the input file base row for this rowset */
etab[ldrs[0]].ar = m;
Oloadbuff(eid);
}
break;
}
}
oloadX_exit:
etab[eid].flg |= 02000; /* mark EOF */
if ( etab[eid].ps ) { /* wait all loaders to complete */
CondWakeAll(&etab[eid].pcvc);
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( i = 0, k=0 ; i < nldr ; i++ ) {
k += etab[ldrs[i]].lstat;
}
if ( k != nldr * 10 )
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
else
break;
}
MutexUnlock(&etab[eid].pmutex);
}
telaps -= 1000*tve.tv_sec + tve.tv_usec/1000;
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps += 1000*tve.tv_sec + tve.tv_usec/1000;
for ( i = 0 ; i < nldr ; i++ )
etab[eid].nrt += etab[ldrs[i]].nr;
/* Print results */
if ( pstats ) {
fprintf(stderr, "[%d] %s Load(X) statistics:\n\t[%d] Target table: %s.%s.%s\n",
tid, odbid, tid, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
fprintf(stderr, "\t[%d] Source: %s\n", tid, etab[eid].src);
seconds = (double)tinit/1000.0 ;
fprintf(stderr, "\t[%d] Pre-loading time: %s s (%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
seconds = (double)telaps/1000.0 ;
fprintf(stderr, "\t[%d] Loading time: %s s(%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
fprintf(stderr, "\t[%d] Total records read: %s\n", tid, strmnum((double)nrf,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total records inserted: %s\n", tid, strmnum((double)etab[eid].nrt,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Total number of columns: %s\n", tid, strmnum((double)l,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Average input row size: %s B\n", tid, strmnum((double)nb*1.0/nrf,num,sizeof(num),1));
fprintf(stderr, "\t[%d] ODBC row size: %s B (data)", tid, strmnum((double)(etab[eid].s-l*sizeof(SQLLEN)),num,sizeof(num),0));
fprintf(stderr, " + %s B (len ind)\n", strmnum((double)(l*sizeof(SQLLEN)), num,sizeof(num),0));
fprintf(stderr, "\t[%d] Rowset size: %s\n", tid, strmnum((double)etab[eid].r,num,sizeof(num),0));
fprintf(stderr, "\t[%d] Rowset buffer size: %s KiB\n", tid, strmnum((double)etab[eid].s/1024.0*etab[eid].r,num,sizeof(num),2));
fprintf(stderr, "\t[%d] Load throughput (real data): %s KiB/s\n", tid, strmnum((double)nb/1.024/telaps,num,sizeof(num),3));
fprintf(stderr, "\t[%d] Load throughput (ODBC): %s KiB/s\n", tid,
strmnum((double)((etab[eid].s-l*sizeof(SQLLEN))*etab[eid].nrt)/1.024/telaps,num,sizeof(num),3));
if ( etab[eid].ps ){
fprintf(stderr, "\t[%d] Reader Total/Wait Cycles: %s", tid, strmnum((double)nt,num,sizeof(num),0));
fprintf(stderr, "/%s\n", strmnum((double)nw,num,sizeof(num),0));
}
}
/* Unbind parameters (if parallel unbind is executed in Oloadbuff) */
if ( !etab[eid].ps )
(void)SQLFreeStmt(thps[tid].Os, SQL_RESET_PARAMS);
/* Free Memory */
if ( Odel )
free ( Odel );
if ( Oins )
free ( Oins );
if ( ldrs ) /* Free ldrs */
free(ldrs);
if ( etab[eid].Orowsetl ) /* Free Orowsetl if allocated */
free(etab[eid].Orowsetl);
if ( etab[eid].Ostatusl ) /* Free Ostatusl if allocated */
free(etab[eid].Ostatusl);
if ( etab[eid].td ) {
for ( i = 0; i < l ; i++)
free ( etab[eid].td[i].Oname );
free(etab[eid].td);
}
/* Close bad file */
if ( etab[eid].fo != stderr )
fclose ( etab[eid].fo );
/* Run "post" SQL */
if ( etab[eid].post ) {
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].post[0] == '@' ) /* run a sql script */
(void)runsql(tid, eid, 0, (etab[eid].post + 1 ));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].post, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
}
return;
}
#endif
/* OloadJson:
* load json files using parameters in etab[eid]
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void OloadJson(int eid)
{
int tid = etab[eid].id; /* Thread ID */
SQLCHAR *Oins = 0; /* INSERT Statement */
SQLCHAR *Odel = 0; /* DELETE Statement */
SQLCHAR *O = 0; /* ODBC temp variable for memory realloc */
SQLRETURN Or = 0; /* ODBC return value */
unsigned long nw = 0, /* no of wait cycles */
nt = 0, /* No of total write cycles */
nrf = 0; /* no of read records */
long telaps = 0, /* Elapsed time in ms */
tinit = 0; /* Initialization time in ms */
size_t nb = 0, /* number of bytes read from input file */
cl = CMD_CHUNK, /* INSERT command buffer length */
cmdl = 0, /* INSERT command length */
dell = CMD_CHUNK, /* DELETE command length */
ifl = 0; /* input field length */
int z = 0; /* loop variable */
unsigned int nldr = 0, /* number of loaders */
k = 0, /* input file field number */
m = 0, /* rowset array record index */
i = 0, /* loop variable */
pstats = 0, /* Error flag: 0 = print stats, 1 = don't print stats */
l = 0, /* destination table fields */
j = 0; /* loop variable */
int *ldrs = 0; /* pointer to array containing loaders EIDs */
char buff[128]; /* buffer to build output file name */
char num[32]; /* Formatted Number String */
char tim[15]; /* Formatted Time String */
struct timeval tve; /* timeval struct to define elapesd/timelines */
SQLCHAR *Odp = 0; /* rowset buffer data pointer */
double seconds = 0; /* seconds used for timings */
JsonReader *pJsonReader = 0; /* XML reader */
int readState = 0; /* 0: look for key, 1: look for array value */
char keybuf[128];
char *valuebuf;
/* Check if we have to use another ODBC connection */
if (thps[tid].cr > 0) {
thps[tid].Oc = thps[thps[tid].cr].Oc;
thps[tid].Os = thps[thps[tid].cr].Os;
}
/* Set "tgt" variable */
var_set(&thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt);
/* Run "pre" SQL */
if (etab[eid].pre) {
etab[eid].flg2 |= 020000000; /* Oexec to allocate/use its own handle */
if (etab[eid].pre[0] == '@') /* run a sql script */
z = runsql(tid, eid, 0, (etab[eid].pre + 1));
else /* Run single SQL command */
z = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].pre, "");
etab[eid].flg2 &= ~020000000; /* reset Oexec to allocate/use its own handle */
if (z && etab[eid].flg & 0004)
goto oloadJson_exit;
etab[eid].nr = 0; /* reset number of resulting rows */
(void)SQLFreeHandle(SQL_HANDLE_STMT, thps[tid].Os);
if (!SQL_SUCCEEDED(Or = SQLAllocHandle(SQL_HANDLE_STMT, thps[tid].Oc, &thps[tid].Os))) {
Oerr(eid, tid, __LINE__, Oc, SQL_HANDLE_DBC);
goto oloadJson_exit;
}
}
/* Check database type */
etab[eid].dbt = checkdb(eid, &thps[tid].Oc, NULL, NULL);
/* Check if truncate */
if (etab[eid].flg & 0002) {
etab[eid].flg &= ~0200000; /* if truncate... unset ifempty */
if ((Odel = malloc(dell)) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error allocating Odel memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadJson_exit;
}
strmcpy((char *)Odel, dbscmds[etab[eid].dbt].trunc, dell);
Odel = (SQLCHAR *)var_exp((char *)Odel, &dell, &thps[tid].tva);
}
/* check ifempty */
if (etab[eid].flg & 0200000) { /* ifempty is set */
if (ifempty(eid, etab[eid].tgt)) {
fprintf(stderr, "odb [OloadJson(%d)] - Target table %s is not empty\n",
__LINE__, etab[eid].tgt);
etab[eid].post = 0; /* prevent post SQL execution */
goto oloadJson_exit;
}
}
/* alocate valuebuf */
if ((valuebuf = calloc(1, etab[eid].buffsz + 1)) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error allocating field buffer: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadJson_exit;
}
/* Open input file */
for (i = j = 0; i < sizeof(buff) && etab[eid].src[i]; i++) {
switch (etab[eid].src[i]) {
case '%':
switch (etab[eid].src[++i]) {
case 't':
j += strmcat(buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 2);
break;
case 'T':
j += strmcat(buff, (char *)etab[eid].Ocso[2], (size_t)etab[eid].buffsz, 1);
break;
case 's':
j += strmcat(buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 2);
break;
case 'S':
j += strmcat(buff, (char *)etab[eid].Ocso[1], (size_t)etab[eid].buffsz, 1);
break;
case 'c':
j += strmcat(buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 2);
break;
case 'C':
j += strmcat(buff, (char *)etab[eid].Ocso[0], (size_t)etab[eid].buffsz, 1);
break;
default:
fprintf(stderr, "odb [OloadJson(%d)] - Invalid expansion %%%c in %s\n",
__LINE__, etab[eid].src[i], etab[eid].src);
goto oloadJson_exit;
}
break;
default:
buff[j++] = etab[eid].src[i];
break;
}
}
buff[j] = '\0';
/* Open input file */
if (etab[eid].jsonKey) {
if ((pJsonReader = jsonReaderNew(buff)) == NULL) {
fprintf(stderr, "odb [OloadJson(%d} - Error opening json input file %s\n", __LINE__, etab[eid].src);
goto oloadJson_exit;
}
}
/* Analyze target table */
z = Otcol(eid, &thps[tid].Oc);
if (z <= 0) {
fprintf(stderr, "odb [OloadJson(%d)] - Error analyzing target table %s.%s.%s\n",
__LINE__, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
goto oloadJson_exit;
}
else {
l = (unsigned int)z;
}
/* Adjust Osize for WCHAR field (up to 4 bytes/char). Set buffer size */
if (etab[eid].dbt != VERTICA) { /* Vertica's CHAR field length is in bytes (not chars) */
for (j = 0; j < l; j++) {
switch (etab[eid].td[j].Otype) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpwc; /* Space for UTF-8 conversion */
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
etab[eid].td[j].Osize *= etab[eid].bpc;/* Space for UTF-8 conversion */
break;
}
}
}
/* Determine buffer start positions and size */
for (j = 0; j < l; j++) {
etab[eid].td[j].start = etab[eid].s;
#ifdef __hpux
if (etab[eid].td[j].Osize % WORDSZ)
etab[eid].td[j].pad = WORDSZ - etab[eid].td[j].Osize % WORDSZ;
#endif
etab[eid].s += (etab[eid].td[j].Osize + etab[eid].td[j].pad + sizeof(SQLLEN)); /* space for length indicator */
}
/* Truncate target table */
if (etab[eid].flg & 0002) { /* truncate target table */
if (f & 020000) /* if verbose */
fprintf(stderr, "odb: Now truncating target table (%s)... ", (char *)Odel);
etab[eid].flg2 |= 020000000; /* Oexec to allocate/use its own handle */
z = Oexec(tid, eid, 0, 0, Odel, ""); /* Run Truncate */
etab[eid].flg2 &= ~020000000; /* reset Oexec to allocate/use its own handle */
if (j && etab[eid].flg & 0004) {
fprintf(stderr, "odb [OloadJson(%d)] - Error truncating Target table. Exiting because Stop On Error was set\n",
__LINE__);
goto oloadJson_exit;
}
if (etab[eid].cmt) { /* Autocommit off: have to commit truncate */
if (!SQL_SUCCEEDED(Or = SQLEndTran(SQL_HANDLE_DBC, thps[tid].Oc, SQL_COMMIT))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
goto oloadJson_exit;
}
}
etab[eid].nr = 0; /* reset number of resulting rows */
if (f & 020000) /* if verbose */
fprintf(stderr, "done\n");
}
/* Calculate rowset if buffer size is set */
if (etab[eid].rbs) {
etab[eid].r = etab[eid].rbs / etab[eid].s;
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; /* at least one record at a time */
}
if (etab[eid].flg2 & 0001) /* commit as multiplier */
etab[eid].cmt *= (int)etab[eid].r;
/* Build INSERT statement */
if ((Oins = malloc(cl)) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error allocating Oins memory\n", __LINE__);
goto oloadJson_exit;
}
if (!strcasecmp(etab[eid].loadcmd, "UL"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT USING LOAD %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
else if (!strcasecmp(etab[eid].loadcmd, "UP"))
{
cmdl += snprintf((char *)Oins, cl, "UPSERT %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
else if (!strcasecmp(etab[eid].loadcmd, "IN"))
{
cmdl += snprintf((char *)Oins, cl, "INSERT %s%sINTO %s%c%s%c%s VALUES(",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : " ",
etab[eid].Ocso[0] ? '.' : ' ',
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : " ",
etab[eid].Ocso[1] ? '.' : ' ', (char *)etab[eid].Ocso[2]);
}
for (i = 0; i < l; i++) {
if (i)
cl += strmcat((char *)Oins, ",", cl, 0);
cl += strmcat((char *)Oins, "?", cl, 0);
if (cmdl + CMD_CHUNK < cl) { /* increase Oins buffer */
cl += CMD_CHUNK;
O = Oins;
if ((O = realloc(O, cl)) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadJson_exit;
}
Oins = O;
}
}
(void)strmcat((char *)Oins, ")", cl, 0);
if (f & 020000) /* if verbose */
fprintf(stderr, "odb [OloadJson(%d)] - INSERT statement: %s\n", __LINE__, (char *)Oins);
/* Allocate loader eids array */
nldr = etab[eid].ps ? etab[eid].ps : 1;
if ((ldrs = (int *)calloc(nldr, sizeof(int))) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error allocating loader eids array: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadJson_exit;
}
/* Initialize loader eids array */
if (etab[eid].ps) {
for (i = 0, j = 0; i < (unsigned int)no && j < etab[eid].ps; i++) {
if (etab[i].type == 'L' && etab[i].parent == eid) {
etab[i].r = etab[eid].r; /* adjust rowset when rbs is specified */
etab[i].cmt = etab[eid].cmt;/* adjust rowset when cmt is a multiplier */
etab[i].lstat = 0; /* reset buffer status to available */
etab[i].td = etab[eid].td; /* copy table structure pointer */
ldrs[j++] = i;
}
}
}
else {
ldrs[0] = eid;
}
/* Allocate rowset & status memory */
if ((Odp = etab[eid].Orowsetl = (SQLCHAR *)calloc(etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[eid].Ostatusl = (SQLUSMALLINT *)calloc(etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadJson_exit;
}
if (etab[eid].ps) {
for (j = 0; j < nldr; j++) { /* for all loading threads... */
if ((etab[ldrs[j]].Orowsetl = (SQLCHAR *)calloc(etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[ldrs[j]].Ostatusl = (SQLUSMALLINT *)calloc(etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL) {
fprintf(stderr, "odb [OloadJson(%d)] - Error allocating rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto oloadJson_exit;
}
}
}
/* Set rowset size, bind parameters and prepare INSERT */
for (j = 0; j < nldr; j++) {
/* Set max commit mode */
if (etab[eid].cmt) {
if (!SQL_SUCCEEDED(Or = SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oloadJson_exit;
}
}
else {
if (!SQL_SUCCEEDED(Or = SQLSetConnectAttr(thps[etab[ldrs[j]].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Oc, SQL_HANDLE_DBC);
goto oloadJson_exit;
}
}
/* Set max char/varchar/binary column length */
if (!SQL_SUCCEEDED(Oret = SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_MAX_LENGTH,
(SQLPOINTER)etab[eid].Omaxl, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
}
/* Bind parameters */
if (!SQL_SUCCEEDED(Or = SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadJson_exit;
}
if (!SQL_SUCCEEDED(Or = SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadJson_exit;
}
if (!SQL_SUCCEEDED(Or = SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAM_STATUS_PTR,
etab[ldrs[j]].Ostatusl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadJson_exit;
}
if (!SQL_SUCCEEDED(Or = SQLSetStmtAttr(thps[etab[ldrs[j]].id].Os, SQL_ATTR_PARAMS_PROCESSED_PTR,
&etab[ldrs[j]].Oresl, 0))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadJson_exit;
}
for (i = 0, z = 1; i < l; i++) {
if (!SQL_SUCCEEDED(Or = SQLBindParameter(thps[etab[ldrs[j]].id].Os, (SQLUSMALLINT)z++,
SQL_PARAM_INPUT, SQL_C_CHAR, etab[eid].td[i].Otype, (SQLULEN)etab[eid].td[i].Osize,
etab[eid].td[i].Odec, &etab[ldrs[j]].Orowsetl[0 + etab[eid].td[i].start], etab[eid].td[i].Osize,
(SQLLEN *)&etab[ldrs[j]].Orowsetl[0 + etab[eid].td[i].start + etab[eid].td[i].Osize + etab[eid].td[i].pad]))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadJson_exit;
}
}
if (!SQL_SUCCEEDED(Or = SQLPrepare(thps[etab[ldrs[j]].id].Os, Oins, SQL_NTS))) {
Oerr(eid, tid, __LINE__, thps[etab[ldrs[j]].id].Os, SQL_HANDLE_STMT);
goto oloadJson_exit;
}
etab[ldrs[j]].sp = l; /* save number of target table fields for prec */
}
/* Register Load Start Time */
gettimeofday(&tve, (void *)NULL);
tinit = 1000 * (tve.tv_sec - tvi.tv_sec) + (tve.tv_usec - tvi.tv_usec) / 1000;
/* Json load */
pstats = 1; /* from now on print stats on exit */
while (pJsonReader->errorCode == JSON_SUCCESS) {
// read start
if (readState == 0) {
while (pJsonReader->errorCode == JSON_SUCCESS) {
if (pJsonReader->state == JSON_STATE_MEMBER_KEY) {
jsonReadKey(pJsonReader, keybuf, sizeof(keybuf));
if (pJsonReader->errorCode == JSON_SUCCESS && !strmicmp((char *)etab[eid].td[k].Oname, keybuf, strlen(keybuf))) {
readState = 1;
break;
}
}
else {
jsonRead(pJsonReader);
}
}
}
else {
// read rows
while (pJsonReader->errorCode == JSON_SUCCESS) {
if (pJsonReader->state == JSON_STATE_MEMBER_KEY) {
jsonReadKey(pJsonReader, keybuf, sizeof(keybuf));
}
else if (pJsonReader->state == JSON_STATE_MEMBER_VALUE) {
jsonReadMemberValue(pJsonReader, valuebuf, etab[eid].buffsz);
ifl = strlen(valuebuf);
for (k = 0; k < l; k++) {
if (!strmicmp((char *)etab[eid].td[k].Oname, keybuf, strlen(keybuf))) { /* name matches */
Odp = etab[eid].Orowsetl + m*etab[eid].s + etab[eid].td[k].start;
if (ifl > etab[eid].td[k].Osize) { // prevent Orowsetl[] overflow
fprintf(stderr, "odb [OloadJson(%d)] - Error: row %lu col %u field truncation. Input "
"string: >%s< of length %lu.\n", __LINE__, nrf + 1, k + 1, valuebuf, ifl);
goto oloadJson_exit;
}
MEMCPY(Odp, valuebuf, ifl);
Odp += etab[eid].td[k].Osize + etab[eid].td[k].pad;
*((SQLLEN *)(Odp)) = (SQLLEN)(ifl);
break;
}
}
}
else if (pJsonReader->state == JSON_STATE_OBJECT_FINISH) {
++m;
jsonRead(pJsonReader);
}
else if (pJsonReader->state == JSON_STATE_ARRAY_FINISH) {
readState = 0;
break;
}
else {
jsonRead(pJsonReader);
}
}
}
if (m == etab[eid].r) { /* Insert rowset */
nt++;
while (go) {
if (etab[eid].ps) { /* Find a buffer loader thread available */
MutexLock(&etab[eid].pmutex);
while (go) {
for (i = 0; i < nldr; i++) { /* Look for write_available buffers */
if (etab[ldrs[i]].lstat == 0) {
etab[ldrs[i]].lstat = 2; /* write_busy */
break;
}
}
if (etab[eid].flg & 04000) { /* Oloadbuff set error flag */
break;
}
else if (i >= nldr) { /* nothing available... wait */
nw++;
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
}
else {
break;
}
}
MutexUnlock(&etab[eid].pmutex);
if (etab[eid].flg & 04000) /* Oloadbuff set error flag */
break;
memcpy(etab[ldrs[i]].Orowsetl, etab[eid].Orowsetl, m * etab[eid].s);
etab[ldrs[i]].nbs = nrf - (unsigned long)m; /* pass the input file base row for this rowset */
etab[ldrs[i]].lstat = 1; /* read_available */
etab[ldrs[i]].ar = m;
CondWakeAll(&etab[eid].pcvc);
}
else {
etab[ldrs[0]].nbs = nrf - (unsigned long)m; /* pass the input file base row for this rowset */
etab[ldrs[0]].ar = m;
Oloadbuff(eid);
}
break;
}
if (etab[eid].flg & 04000) /* Oloadbuff set error flag */
break;
m = 0;
k = 0;
}
}
if (pJsonReader->errorCode != JSON_ERROR_PARSE_EOF) {
fprintf(stderr, "odb [OloadJson(%d)] - Error parse json file encountered error:%s\n", __LINE__, jsonReaderErrorMessage(pJsonReader));
}
jsonReaderFree(pJsonReader);
/* load trailing rows */
if (m) { /* Insert rowset */
nt++;
while (go) {
if (etab[eid].ps) { /* Find a buffer loader thread available */
MutexLock(&etab[eid].pmutex);
while (go) {
for (i = 0; i < nldr; i++) { /* Look for write_available buffers */
if (etab[ldrs[i]].lstat == 0) {
etab[ldrs[i]].lstat = 2; /* write_busy */
break;
}
}
if (etab[eid].flg & 04000) { /* Oloadbuff set error flag */
break;
}
else if (i >= nldr) { /* nothing available... wait */
nw++;
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
}
else {
break;
}
}
MutexUnlock(&etab[eid].pmutex);
if (etab[eid].flg & 04000) /* Oloadbuff set error flag */
break;
memcpy(etab[ldrs[i]].Orowsetl, etab[eid].Orowsetl, m * etab[eid].s);
etab[ldrs[i]].nbs = nrf - (unsigned long)m; /* pass the input file base row for this rowset */
etab[ldrs[i]].lstat = 1; /* read_available */
etab[ldrs[i]].ar = m;
CondWakeAll(&etab[eid].pcvc);
}
else {
etab[ldrs[0]].nbs = nrf - (unsigned long)m; /* pass the input file base row for this rowset */
etab[ldrs[0]].ar = m;
Oloadbuff(eid);
}
break;
}
}
oloadJson_exit:
etab[eid].flg |= 02000; /* mark EOF */
if (etab[eid].ps) { /* wait all loaders to complete */
CondWakeAll(&etab[eid].pcvc);
MutexLock(&etab[eid].pmutex);
while (go) {
for (i = 0, k = 0; i < nldr; i++) {
k += etab[ldrs[i]].lstat;
}
if (k != nldr * 10)
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
else
break;
}
MutexUnlock(&etab[eid].pmutex);
}
telaps -= 1000 * tve.tv_sec + tve.tv_usec / 1000;
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps += 1000 * tve.tv_sec + tve.tv_usec / 1000;
for (i = 0; i < nldr; i++)
etab[eid].nrt += etab[ldrs[i]].nr;
/* Print results */
if (pstats) {
fprintf(stderr, "[%d] %s Load(X) statistics:\n\t[%d] Target table: %s.%s.%s\n",
tid, odbid, tid, etab[eid].Ocso[0], etab[eid].Ocso[1], etab[eid].Ocso[2]);
fprintf(stderr, "\t[%d] Source: %s\n", tid, etab[eid].src);
seconds = (double)tinit / 1000.0;
fprintf(stderr, "\t[%d] Pre-loading time: %s s (%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
seconds = (double)telaps / 1000.0;
fprintf(stderr, "\t[%d] Loading time: %s s(%s)\n", tid,
strmnum(seconds, num, sizeof(num), 3), strmtime(seconds, tim));
fprintf(stderr, "\t[%d] Total records read: %s\n", tid, strmnum((double)nrf, num, sizeof(num), 0));
fprintf(stderr, "\t[%d] Total records inserted: %s\n", tid, strmnum((double)etab[eid].nrt, num, sizeof(num), 0));
fprintf(stderr, "\t[%d] Total number of columns: %s\n", tid, strmnum((double)l, num, sizeof(num), 0));
fprintf(stderr, "\t[%d] Average input row size: %s B\n", tid, strmnum((double)nb*1.0 / nrf, num, sizeof(num), 1));
fprintf(stderr, "\t[%d] ODBC row size: %s B (data)", tid, strmnum((double)(etab[eid].s - l * sizeof(SQLLEN)), num, sizeof(num), 0));
fprintf(stderr, " + %s B (len ind)\n", strmnum((double)(l * sizeof(SQLLEN)), num, sizeof(num), 0));
fprintf(stderr, "\t[%d] Rowset size: %s\n", tid, strmnum((double)etab[eid].r, num, sizeof(num), 0));
fprintf(stderr, "\t[%d] Rowset buffer size: %s KiB\n", tid, strmnum((double)etab[eid].s / 1024.0*etab[eid].r, num, sizeof(num), 2));
fprintf(stderr, "\t[%d] Load throughput (real data): %s KiB/s\n", tid, strmnum((double)nb / 1.024 / telaps, num, sizeof(num), 3));
fprintf(stderr, "\t[%d] Load throughput (ODBC): %s KiB/s\n", tid,
strmnum((double)((etab[eid].s - l * sizeof(SQLLEN))*etab[eid].nrt) / 1.024 / telaps, num, sizeof(num), 3));
if (etab[eid].ps) {
fprintf(stderr, "\t[%d] Reader Total/Wait Cycles: %s", tid, strmnum((double)nt, num, sizeof(num), 0));
fprintf(stderr, "/%s\n", strmnum((double)nw, num, sizeof(num), 0));
}
}
/* Unbind parameters (if parallel unbind is executed in Oloadbuff) */
if (!etab[eid].ps)
(void)SQLFreeStmt(thps[tid].Os, SQL_RESET_PARAMS);
/* Free Memory */
if (Odel)
free(Odel);
if (Oins)
free(Oins);
if (ldrs) /* Free ldrs */
free(ldrs);
if (etab[eid].Orowsetl) /* Free Orowsetl if allocated */
free(etab[eid].Orowsetl);
if (etab[eid].Ostatusl) /* Free Ostatusl if allocated */
free(etab[eid].Ostatusl);
if (etab[eid].td) {
for (i = 0; i < l; i++)
free(etab[eid].td[i].Oname);
free(etab[eid].td);
}
/* Close bad file */
if (etab[eid].fo != stderr)
fclose(etab[eid].fo);
/* Run "post" SQL */
if (etab[eid].post) {
var_set(&thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt);
etab[eid].flg2 |= 020000000; /* Oexec to allocate/use its own handle */
if (etab[eid].post[0] == '@') /* run a sql script */
(void)runsql(tid, eid, 0, (etab[eid].post + 1));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].post, "");
etab[eid].flg2 &= ~020000000; /* reset Oexec to allocate/use its own handle */
}
return;
}
/* Oloadbuff:
* load buffer pointed by etab[eid].Orowsetl
*
* eid (I): etab entry ID
*
* return: 0=OK, 1=Fetch Error, 2=Load Error
*/
static int Oloadbuff(int eid)
{
size_t embs=ERR_MSG_LEN; /* Error message buffer size */
SQLRETURN Or=0; /* ODBC return value */
SQLCHAR Ostate[6]; /* ODBC state */
SQLSMALLINT Oi=1; /* ODBC error index */
SQLSMALLINT Oln=0; /* ODBC error message length */
SQLLEN Onrows=0; /* ODBC no of affected rows */
SQLCHAR *Otxt=0; /* ODBC error message buffer pointer */
SQLINTEGER Onative=0; /* ODBC native erroro code */
char num[32]; /* Formatted Number String */
int tid = etab[eid].id; /* Thread ID */
int par = etab[eid].parent; /* Parent shortcut */
int gpar = etab[eid].k; /* grand parent eid */
struct timeval tve; /* timeval struct to define elapesd/timelines */
unsigned int umx = 0; /* Local use mutexes flag: 0=no, 1=yes */
unsigned int pmk = 0; /* Local print mark flag: 0=no, 1=yes (simple), 2=yes (timeline) */
char type = etab[eid].type; /* Local thread type */
unsigned int i = 0; /* loop variable */
SQLLEN Orown = 0; /* Row number with errors in rowset */
long telaps = 0; /* Register elapsed time */
struct timeval tvel; /* timeval struct to define elapesd/timelines */
int mtype = 0; /* olbmex message index */
unsigned long lnr = etab[eid].nr ; /* Local copy of etab[eid].nr */
unsigned int lcr = etab[eid].cr ; /* Local copy of etab[eid].cr */
Mutex *parmutex = &etab[par].pmutex;/* Local copy of parent mutex address */
int fexst = 0 ; /* Function Exit Status: 0=OK, 1=Fetch Error, 2=Load Error */
#ifdef ODB_PROFILE
struct timespec tsp1, tsp2 ;/* Profiling function */
unsigned long ti = 0 ; /* Insert profiled time in nanoseconds */
unsigned long ts = 0 ; /* Thread synch profiled time in nanoseconds */
#endif
if ( etab[eid].fo == stdout )
etab[eid].fo = stderr ;
switch ( type ) {
case 'L':
mtype = 0;
gpar = par;
break;
case 'C':
case 'P':
if ( type == 'C' )
mtype = 1;
else
mtype = 2;
umx = 1;
/* Run mpre-SQL on target (tmpre) */
if ( etab[eid].mpre ) {
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].mpre[0] == '@' ) { /* run a sql script */
etab[eid].type = 'f'; /* set execution type to file during runsql() */
i = runsql(tid, eid, 0, (etab[eid].mpre + 1 ));
} else { /* Run single SQL command */
etab[eid].type = 'x'; /* set execution type to file during runsql() */
i = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].mpre, "");
}
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( i && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Oloadbuff(%d)] - Error running mpre-SQL on tgt. Exiting because Stop On Error is set\n",
__LINE__);
etab[gpar].flg |= 04000; /* set error flag */
return(fexst);
}
etab[eid].nr = 0; /* reset number of resulting rows */
etab[eid].type = type; /* reset thread type */
}
break;
}
if ( etab[eid].ps )
umx = 1;
if ( etab[eid].flg & 0400 )
pmk = 1;
if ( etab[eid].flg2 & 020000 )
pmk = 2;
/* Initial memory allocation for the error message buffer */
if ( ( Otxt = malloc(embs) ) == (void *)NULL ) {
fprintf(stderr, "[%d] odb [loadbuff(%d)] - Error allocating err msg buffers: [%d] %s\n",
tid, __LINE__, errno, strerror(errno));
return(fexst);
}
/* Main loop */
while ( go ) {
if ( umx ) {
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
MutexLock( parmutex );
while ( go ) {
if ( etab[eid].lstat == 1 ) {
etab[eid].lstat = 3; /* read_busy */
break ;
} else if ( etab[par].flg & 02000 && /* EOF */
etab[eid].lstat != 10 ) { /* Job not restarted */
goto oloadbuff_exit ; /* no more data */
} else {
CondWait(&etab[par].pcvc, parmutex);
}
}
MutexUnlock(parmutex);
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
ts += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
}
if ( etab[eid].ar < etab[eid].r ) /* Reset Rowset size */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[tid].Os,
SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)(etab[eid].ar), 0)))
Oerr(eid, tid, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
Or = SQLExecute(thps[tid].Os) ; /* Execute INSERT (load/copy) or tgt command */
SQLLEN tLastRow = -1; /* remember last bad row to ensure that a bad row will be printed only once. */
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
ti += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
switch ( Or ) { /* Evaluate SQLExecute Result */
case SQL_SUCCESS:
lnr += (unsigned long)etab[eid].Oresl;
if ( etab[par].cmt )
lcr += (unsigned long)etab[eid].Oresl;
break;
default:
Oi = 1; //the record number need to be initialize for next loop
/* Loop through the ODBC error stack for this statement handle. */
while ( ( Or = SQLGetDiagRec(SQL_HANDLE_STMT, thps[tid].Os, Oi, Ostate, &Onative, Otxt,
(SQLSMALLINT)embs, &Oln) ) != SQL_NO_DATA ) {
/* Error management process is made of three steps:
1. print the error messages and - when possible - the 'wrong' row/record
close to the error message and preceeded by ">>> " so you can
easily sed/grep "bad rows". This depends on ODBC driver
SQL_DIAG_ROW_NUMBER availability
2. understand how many rows have been loaded using SQLRowCount if possible
- SQL_PARAM_ARRAY_ROW_COUNTS = SQL_PARC_NO_BATCH - or browing the
status array
3. manage max-error conditions. Please note, from 'odb point of view'
errors are rows or records that were not loaded or copied. For example
if you have a 100 rows rowset and row nomber 13 is not loaded because
of a PK violation, the ODBC error stack contains a single error due to
the PK violation. Now, depending on ODBC driver bevaviour:
- if the driver inserts the remaining 99 rows (if valid) odb will
increase error count by 1 (because only one row was not loaded)
- if the driver loads the 12 records "before" the error and then
stops when it found the PK violation, odb will increase the error
counter by 88 because only the first 12 rows out of 100 have been
loaded.
- if the driver other driver will discard the whole rowset odb will
increase the error counter by 100 */
/* STEP 1: we have to understand which row this error message belongs to */
if ( etab[eid].r == 1 ) {
/* this is easy: rowset is made of a single row... */
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Error loading row %lu (State: %s, Native %ld)\n%s\n",
tid, __LINE__, etab[eid].nbs + 1, (char *)Ostate, (long)Onative, (char *)Otxt);
if ( type == 'C' ) { /* 'C' thread */
if ( etab[eid].fdmp ) { /* dump ODBC buffer */
MutexLock(&etab[gpar].pmutex);
fprintf(etab[eid].fdmp, "[%d] odb [Oloadbuff(%d)] - Error loading row %lu (State: %s, Native %ld)\n%s\n",
tid, __LINE__, etab[eid].nbs + 1, (char *)Ostate, (long)Onative, (char *)Otxt);
fprintf(etab[eid].fdmp, "[%d] Dumping failing row. ODBC row length = %zu\n", tid, etab[par].s ) ;
dumpbuff(etab[eid].fdmp, tid, (unsigned char *)etab[eid].Orowsetl, etab[par].s , 0 );
MutexUnlock(&etab[gpar].pmutex);
}
} else { /* either multi ('L') or single ('l') threaded loaders */
prec('L', (unsigned char *)etab[eid].Orowsetl, eid);
}
} else if ( SQL_SUCCEEDED(Or=SQLGetDiagField(SQL_HANDLE_STMT, thps[tid].Os, Oi, SQL_DIAG_ROW_NUMBER,
(SQLPOINTER)&Orown, 0, NULL))) {
if ( (size_t)Oln > embs ) { /* error message buffer was too small */
embs = (size_t) ( Oln + 1 ); /* increase buffer size */
if ( ( Otxt = realloc ( Otxt, embs ) ) == (void *)NULL ) /* Re-alloc message buffer */
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Error re-allocating err msg buff: [%d] %s\n",
tid, __LINE__, errno, strerror(errno));
}
switch ( (int)Orown ) {
case SQL_NO_ROW_NUMBER: /* error not associated to specific row number */
case SQL_ROW_NUMBER_UNKNOWN: /* driver doesn't know row number for this error */
/* Just print the error message but no failing data */
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Error (State: %s, Native %ld)\n%s\n",
tid, __LINE__, (char *)Ostate, (long)Onative, (char *)Otxt);
if ( type == 'C' ) { /* 'C' thread */
if ( etab[eid].fdmp ) { /* dump ODBC buffer */
MutexLock(&etab[gpar].pmutex);
fprintf(etab[eid].fdmp, "[%d] odb [Oloadbuff(%d)] - Error (State: %s, Native %ld)\n%s\n",
tid, __LINE__, (char *)Ostate, (long)Onative, (char *)Otxt);
fprintf(etab[eid].fdmp, "[%d] Dumping whole rowset of %zu rows. ODBC row length = %zu\n",
tid, etab[eid].ar, etab[par].s ) ;
dumpbuff(etab[eid].fdmp, tid, (unsigned char *)etab[eid].Orowsetl, etab[eid].ar * etab[par].s , 0 );
MutexUnlock(&etab[gpar].pmutex);
}
}
break;
default:
/* Ok, now we have an error message (Otxt), a five char SQLState (Ostate),
* a native error code (Onative) and the rowset row number (Orown). Let's
* print everything to stderr. */
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Error loading row %lu (State: %s, Native %ld)\n%s\n",
tid, __LINE__, (unsigned long)Orown + etab[eid].nbs, (char *)Ostate, (long)Onative, (char *)Otxt);
if (type == 'C') { /* 'C' thread */
if (etab[eid].fdmp) { /* dump ODBC buffer */
MutexLock(&etab[gpar].pmutex);
fprintf(etab[eid].fdmp, "[%d] odb [Oloadbuff(%d)] - Error loading row %lu (State: %s, Native %ld)\n%s\n",
tid, __LINE__, (unsigned long)Orown + etab[eid].nbs, (char *)Ostate, (long)Onative, (char *)Otxt);
fprintf(etab[eid].fdmp, "[%d] Dumping row %lu in a block of %zu rows. ODBC row length = %zu\n",
tid, (unsigned long)Orown, etab[eid].ar, etab[par].s);
dumpbuff(etab[eid].fdmp, tid, (unsigned char *)(etab[eid].Orowsetl + (Orown - 1) * etab[par].s), etab[par].s, 0);
MutexUnlock(&etab[gpar].pmutex);
}
}
if (tLastRow != Orown) { /* ensure no duplicated rows in bad file */
char type = etab[eid].type;
tLastRow = Orown;
if (type == 'l') type = 'L';
prec(type, (unsigned char *)(etab[eid].Orowsetl + etab[par].s*(Orown - 1)), eid);
}
break;
}
} else {
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Unable to get SQL_DIAG_ROW_NUMBER. Printing the whole ODBC error stack\n",
tid, __LINE__);
Oerr(eid, tid, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
break;
}
Oi++;
}
/* STEP 2: determine how many rows have been loaded */
if ( etab[eid].flg2 & 01000 ) { /* tgt SQL_PARAM_ARRAY_ROW_COUNTS set to SQL_PARC_NO_BATCH */
if (!SQL_SUCCEEDED(Or=SQLRowCount(thps[tid].Os, &Onrows))) {
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Unable to get row counts from driver\n", tid, __LINE__);
Oerr(eid, tid, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
}
} else if ( etab[eid].dbt == ORACLE ) {
/* Oracle does not insert anything if one of the rows in the rowset fails.
* Unfortunately it still sets all status array elements to SQL_SUCCESS !! */
Onrows = 0;
} else {
/* etab[eid].Oresl contains the number of 'processed' rows.
* However the number of 'processed' rows could be different from
* the number of 'inserted' rows. Here we analyze the status array
* to determine how many 'processed' rows have been 'inserted'. */
for ( i = 0, Onrows = 0 ; i < (unsigned int) etab[eid].Oresl ; i++ ) {
switch ( etab[eid].Ostatusl[i] ) {
case SQL_PARAM_SUCCESS_WITH_INFO:
case SQL_PARAM_SUCCESS:
Onrows++;
break;
}
}
}
/* STEP 3 - Finally we have to manage max error conditions.
* I do consider as 'errors' any 'non-loaded/copied' rows. */
if ( etab[gpar].mer ) {
MutexLock(&etab[gpar].pmutex);
etab[gpar].mer -= (int) ( etab[eid].ar - (size_t)Onrows ) ;
if ( etab[gpar].mer <= 0 ) {
etab[gpar].flg |= 04000 ;
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "[%d] odb [Oloadbuff(%d)] - Max number of error reached\n",
tid, __LINE__);
}
MutexUnlock(&etab[gpar].pmutex);
}
lnr += Onrows < 0 ? 0 : (unsigned long)Onrows;
if ( ( etab[eid].flg & 0004 ) || etab[eid].roe ) { /* Stop or Restart on Error */
etab[gpar].flg |= 04000;
fexst = 2 ; /* Function Exit Status = Load Error */
}
break;
}
#ifndef _WIN32
pthread_testcancel();
#endif
if ( etab[par].cmt > 0 && lcr >= (unsigned int)etab[par].cmt ) {
if (!SQL_SUCCEEDED(Or=SQLEndTran(SQL_HANDLE_DBC, thps[tid].Oc, SQL_COMMIT)))
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
lcr = 0;
}
switch ( pmk ) {
case 1: /* simple mark */
fprintf(stderr, "[%d] %s records %s %s\n",
tid, strmnum((double)lnr,num,sizeof(num),0), olbmex[mtype], lcr ? "" : "[commit]");
break;
case 2: /* timeline mark */
gettimeofday(&tvel, (void *)NULL); /* register end time */
telaps = 1000*(tvel.tv_sec-tvi.tv_sec)+(tvel.tv_usec-tvi.tv_usec)/1000;
fprintf(stderr, "[%d] %s records %s %s (%ld ms)\n",
tid, strmnum((double)lnr,num,sizeof(num),0), olbmex[mtype], lcr ? "" : "[commit]", telaps);
break;
}
if ( fdmp ) { /* dump ODBC buffer */
MutexLock(&etab[gpar].pmutex);
fprintf(fdmp, "[%d] Block of %zu rows. Row length = %zu\n", tid, etab[eid].ar, etab[eid].s ) ;
dumpbuff(fdmp, tid, (unsigned char *)etab[eid].Orowsetl, etab[eid].ar * etab[par].s, 1 );
MutexUnlock(&etab[gpar].pmutex);
}
if ( umx ) {
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
MutexLock(parmutex);
etab[eid].lstat = 0; /* write_available */
MutexUnlock(parmutex);
CondWake( &etab[par].pcvp );
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
ts += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
} else {
break;
}
}
oloadbuff_exit:
#ifdef ODB_PROFILE
fprintf(stderr, "[%d] (odb profile Oloadbuff) Insert time (ns): %s\n", tid, strmnum((double)ti,num,sizeof(num),0));
fprintf(stderr, "[%d] (odb profile Oloadbuff) Thread synch time (ns): %s\n", tid, strmnum((double)ts,num,sizeof(num),0));
#endif
etab[eid].nr = lnr ; /* save lnr */
if ( Otxt )
free ( Otxt ) ;
if ( etab[par].cmt ) { /* Rolling back */
if (!SQL_SUCCEEDED(SQLEndTran(SQL_HANDLE_DBC, thps[tid].Oc, SQL_COMMIT)))
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
}
if ( umx ) {
(void)SQLFreeStmt(thps[tid].Os, SQL_RESET_PARAMS);
etab[eid].lstat = 10; /* Set lstat available */
MutexUnlock(parmutex);
CondWake( &etab[par].pcvp );
gettimeofday(&tve, (void *)NULL); /* save thread-end time */
etab[eid].nrt = 1000*tve.tv_sec + tve.tv_usec/1000 - etab[par].nbs ;
if ( etab[eid].Orowsetl ) /* Free Olrowsetl if allocated */
free(etab[eid].Orowsetl);
if ( etab[eid].Ostatusl ) /* Free Olstatusl if allocated */
free(etab[eid].Ostatusl);
}
if ( fexst ) {
return ( fexst ) ;
}
if ( ( type == 'C' || type == 'P' ) && etab[gpar].post ) {
MutexLock(&etab[gpar].pmutex);
etab[gpar].nltotal--; /* decrease number of active loaders */
MutexUnlock(&etab[gpar].pmutex);
if ( etab[gpar].nltotal == 0 ) { /* no more active loaders: run post-SQL */
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].post[0] == '@' ) /* run a sql script */
(void)runsql(tid, eid, 0, (etab[gpar].post + 1 ));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[gpar].post, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
}
}
return (fexst);
}
/* Oextract:
* extract to output file
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void Oextract(int eid)
{
int tid = etab[eid].id; /* Thread ID */
char num[32]; /* to build formatted numbers */
char tim[15]; /* formatted time string */
char *xbuff = 0; /* buffer allocated for XML output */
size_t xbuffl = 33 + MAXOBJ_LEN * 3 ; /* xmlbuff length */
SQLCHAR *Ocmd = 0; /* ODBC Command buffer */
SQLCHAR *O = 0; /* ODBC temp variable for realloc */
SQLRETURN Or=0; /* ODBC return value */
size_t cl=CMD_CHUNK; /* ODBC Command Length */
int par = etab[eid].parent; /* Parent shortcut */
int ptid = etab[par].id; /* Parent thead ID */
int i = 0; /* loop variable */
int cmdl = 0; /* Ocmd length */
int gpar = etab[eid].k; /* grand parent eid */
unsigned long b = 0; /* total number of bytes written by "pool" */
unsigned long nr = 0; /* total number of recs written by "pool" */
double seconds = 0; /* seconds used for timings */
/* Allocate XML buffer */
if ( etab[eid].flg2 & 04000000 ) { /* initialize XML output buffer */
if ( ( xbuff = malloc ( xbuffl ) ) == (void *) NULL ) {
fprintf(stderr, "odb [Oextract(%d)] - Error allocating xbuff memory\n", __LINE__);
etab[gpar].lstat |= 0010 ; /* mark pre-xml error */
return;
}
}
/* Allocate Ocmd memory */
cl += etab[eid].sql ? strlen(etab[eid].sql) : 0;
cl += etab[eid].src ? strlen(etab[eid].src) : 0;
cl += etab[eid].map ? strlen(etab[eid].map) : 0;
cl += etab[eid].cols ? strlen(etab[eid].cols) : 0;
if ( ( Ocmd = malloc ( cl ) ) == (void *) NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating Ocmd memory\n", __LINE__);
return ;
}
Ocmd[0] = '\0' ; /* ready for strmcat */
/* First thread to run "pre-extract" work - others to wait */
MutexLock(&etab[gpar].gmutex); /* lock shared mutex */
if ( ( ( etab[gpar].lstat & 0002 ) && /* pre-SQL ran by another thread with errors and... */
( etab[eid].flg & 0004 ) ) || /* ... soe is set or... */
( etab[gpar].lstat & 0010 ) ) { /* ... XML init errors */
MutexUnlock(&etab[gpar].gmutex); /* unlock mutex */
return;
}
if ( etab[gpar].lstat & 0001 ) { /* XML output to initialize */
xbuffl = snprintf(xbuff, xbuffl, "<?xml version=\"1.0\"?>\n<%s>",
etab[eid].src ? etab[eid].src : "select" );
#ifdef HDFS
if ( etab[eid].fho ) {
(*hdfswrite)(hfs, etab[eid].fho, (void *)xbuff, xbuffl);
} else if ( etab[eid].fo ) {
(void)fwrite ( xbuff, 1, xbuffl, etab[eid].fo );
}
#else
(void)fwrite ( xbuff, 1, xbuffl, etab[eid].fo );
#endif
etab[gpar].lstat &= ~0001 ; /* mark XML initialization done */
}
if ( etab[gpar].lstat & 0004 ) { /* pre SQL to run */
etab[eid].fso = etab[eid].fo; /* save output file */
etab[eid].fo = stdout; /* set output to stdout */
var_set ( &thps[tid].tva, VTYPE_I, "src", etab[eid].src );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) { /* run a sql script */
etab[eid].type = 'f'; /* set execution type to file during runsql() */
i = runsql(tid, eid, 0, (etab[eid].pre + 1 ));
} else { /* Run single SQL command */
etab[eid].type = 'x'; /* set execution type to run single SQL */
i = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].pre, "");
}
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
etab[gpar].lstat &= ~0004; /* mark pre-SQL done */
if ( i ) {
etab[gpar].lstat |= 0002 ; /* mark pre-sql error */
if ( etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Oextract(%d)] - Error running pre-SQL. Exiting because Stop On Error is set\n",
__LINE__);
MutexUnlock(&etab[gpar].gmutex);/* unlock mutex */
return;
}
}
etab[eid].type = 'e'; /* reset execution type */
etab[eid].fo = etab[eid].fso; /* reset output file */
etab[eid].fso = (FILE *)NULL; /* reset spool file */
etab[eid].nr = 0; /* reset number of resulting rows */
}
MutexUnlock(&etab[gpar].gmutex);/* unlock mutex */
/* Every extraction thread to run "mpre" SQL script */
if ( etab[eid].mpre ) {
etab[eid].fso = etab[eid].fo; /* save output file */
etab[eid].fo = stdout; /* set output to stdout */
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].mpre[0] == '@' ) { /* run a sql script */
etab[eid].type = 'f'; /* set execution type to file during runsql() */
i = runsql(tid, eid, 0, (etab[eid].mpre + 1 ));
} else { /* Run single SQL command */
etab[eid].type = 'x'; /* set execution type to run single SQL */
i = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].mpre, "");
}
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( i && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Oextract(%d)] - Error running mpre-SQL. Exiting because Stop On Error is set\n",
__LINE__);
return;
}
etab[eid].type = 'e'; /* reset execution type */
etab[eid].fo = etab[eid].fso; /* reset output file */
etab[eid].fso = (FILE *)NULL; /* reset spool file */
etab[eid].nr = 0; /* reset number of resulting rows */
}
/* Set connection attribute Read Only */
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[tid].Oc,
SQL_ATTR_ACCESS_MODE, (SQLPOINTER)SQL_MODE_READ_ONLY, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
return;
}
/* Set max char/varchar/binary column length */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[tid].Os, SQL_ATTR_MAX_LENGTH,
(SQLPOINTER)etab[eid].Omaxl, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
}
/* Build & run extract statement */
if ( etab[eid].sql ) {
if ( etab[eid].flg2 & 040000 ) { /* custom parallel SQL */
/* Set initial Ocmd */
strmcpy((char *)Ocmd, etab[eid].sql, cl);
/* Set tds and cds variables */
(void)snprintf(num, sizeof(num), "%u", etab[eid].ps);
var_set ( &thps[tid].tva, VTYPE_I, "tds", num );
(void)snprintf(num, sizeof(num), "%ld", etab[eid].sbmin);
var_set ( &thps[tid].tva, VTYPE_I, "cds", num );
/* Expand Ocmd */
Ocmd = (SQLCHAR *)var_exp((char *)Ocmd, &cl, &thps[tid].tva);
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "[%d] odb [Oextract(%d)] - EXTRACT statement: %s\n",
tid, __LINE__, (char *)Ocmd);
Oexec ( tid, eid, 0, 0, Ocmd, "");
} else {
Oexec ( tid, eid, 0, 0, (SQLCHAR *)etab[eid].sql, "");
}
} else {
cmdl = strmcat((char *)Ocmd, "SELECT ", cl, 0);
if ( etab[eid].flg & 01406000000 ) { /* cast and/or trim: we have to analyze source table */
for ( i = 0 ; i < etab[eid].cmt ; i++ ) {
if ( i )
cmdl += strmcat((char *)Ocmd, ",", cl, 0);
switch ( etab[eid].td[i].Otype ) {
case SQL_CHAR:
case SQL_WCHAR:
if ( etab[eid].flg & 0400000000 ) { /* trim */
cmdl += strmcat ( (char *) Ocmd, "TRIM(", cl, 0);
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[i].Oname, cl, 0);
cmdl += strmcat ( (char *) Ocmd, ")", cl, 0);
break;
} else if ( etab[eid].flg & 02000000 ) { /* rtrim */
cmdl += strmcat ( (char *) Ocmd, "RTRIM(", cl, 0);
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[i].Oname, cl, 0);
cmdl += strmcat ( (char *) Ocmd, ")", cl, 0);
break;
}
/* FALLTHRU */
case SQL_VARCHAR:
case SQL_WVARCHAR:
case SQL_LONGVARCHAR:
case SQL_WLONGVARCHAR:
if ( etab[eid].flg2 & 040000000 ) { /* trim+ */
cmdl += strmcat ( (char *) Ocmd, "TRIM(", cl, 0);
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[i].Oname, cl, 0);
cmdl += strmcat ( (char *) Ocmd, ")", cl, 0);
} else if ( etab[eid].flg & 04000000 ) { /* rtrim+ */
cmdl += strmcat ( (char *) Ocmd, "RTRIM(", cl, 0);
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[i].Oname, cl, 0);
cmdl += strmcat ( (char *) Ocmd, ")", cl, 0);
} else {
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[i].Oname, cl, 0);
}
break;
default:
if ( etab[eid].flg & 01000000000 ) { /* cast */
cmdl += snprintf((char *)(Ocmd + cmdl), cl, "CAST(%s AS VARCHAR(%d))",
(char *)etab[eid].td[i].Oname, (int)etab[eid].td[i].Osize);
} else {
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[i].Oname, cl, 0);
}
}
if ( cl - cmdl < CMD_CHUNK ) {
cl += CMD_CHUNK;
O = Ocmd ;
if ( ( O = (SQLCHAR *)realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oextract(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
return;
}
Ocmd = O ;
}
}
} else if ( etab[eid].cols ) {
cmdl += strmcat((char *)Ocmd, etab[eid].cols, cl, 0);
} else {
cmdl += strmcat((char *)Ocmd, "*", cl, 0);
}
if ( etab[eid].ps ) {
if ( etab[eid].sb ) { /* split by */
snprintf((char *)(Ocmd +cmdl), cl,
" FROM %s WHERE %s >= %ld AND %s < %ld %s%s %s",
etab[eid].src, etab[eid].sb, etab[eid].sbmin, etab[eid].sb, etab[eid].sbmax,
etab[eid].map ? "AND " : "", etab[eid].map ? etab[eid].map : "",
etab[eid].flg & 0001 ? dbscmds[etab[eid].dbt].uncomm : "");
} else if ( etab[eid].dbt == ORACLE ) {
snprintf((char *)(Ocmd +cmdl), cl,
" FROM %s WHERE MOD(ORA_HASH(ROWID), %u) = %ld%s%s",
etab[eid].src, etab[eid].ps, etab[eid].sbmin,
etab[eid].map ? " AND " : "", etab[eid].map ? etab[eid].map : "");
} else if ( etab[eid].dbt == SQLSERVER ) {
snprintf((char *)(Ocmd +cmdl), cl,
/* The following will provide a better distribution but is (much) slower:
" FROM %s WHERE ABS(HASHBYTES('MD5',%%%%PHYSLOC%%%%) %% %u) = %ld%s%s" */
" FROM %s WHERE (%%%%PHYSLOC%%%% / 147) %% %u = %ld%s%s",
etab[eid].src, etab[eid].ps, etab[eid].sbmin,
etab[eid].map ? " AND " : "", etab[eid].map ? etab[eid].map : "");
} else {
if (!SQL_SUCCEEDED(SQLExecDirect (thps[tid].Os,
(SQLCHAR *)"CONTROL QUERY DEFAULT DBTR_PROCESS 'ON'", SQL_NTS))) {
Oerr(eid, tid, __LINE__, thps[tid].Os, SQL_HANDLE_STMT);
return;
}
snprintf((char *)(Ocmd + cmdl), cl,
" FROM TABLE(TABLE %s, PARTITION NUMBER FROM %u TO %u) %s%s %s",
etab[eid].src, etab[eid].sp, etab[eid].ep,
etab[eid].map ? "WHERE " : "", etab[eid].map ? etab[eid].map : "",
etab[eid].flg & 0001 ? dbscmds[etab[eid].dbt].uncomm : " ");
}
} else {
snprintf((char *)(Ocmd + cmdl), cl, " FROM %s %s%s %s", etab[eid].src,
etab[eid].map ? "WHERE " : "", etab[eid].map ? etab[eid].map : "",
etab[eid].flg & 0001 ? dbscmds[etab[eid].dbt].uncomm : " ");
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "[%d] odb [Oextract(%d)] - EXTRACT statement: %s\n",
tid, __LINE__, (char *)Ocmd);
Oexec ( tid, eid, 0, 0, Ocmd, "");
}
/* Close output file & print stats */
if ( (etab[eid].flg & 01000) ) { /* multi-parted output: each thread closes its file */
if ( etab[eid].fo && etab[eid].fo != stdout )
fclose ( etab[eid].fo );
#ifdef HDFS
else if ( etab[eid].fho )
(*hdfsclose)(hfs, etab[eid].fho);
#endif
}
/* decrease "still active" parallel streams counter */
MutexLock( &etab[par].gmutex );
if ( etab[par].ps ) {
etab[par].ps--;
if ( etab[par].ps ) /* Not the last thread */
goto oextract_exit;
}
if ( !etab[par].ps ) { /* Last thread */
MutexUnlock( &etab[par].gmutex );
/* sum bytes & rows written */
for ( i = 0 ; i < no ; i++ ) {
if ( etab[i].parent == par ) {
b += etab[i].nbs;
nr += etab[i].nr;
}
}
if ( etab[eid].flg2 & 04000000 ) { /* initialize XML output buffer */
xbuffl = snprintf(xbuff, xbuffl, "\n</%s>\n",
etab[eid].src ? etab[eid].src : "select" );
#ifdef HDFS
if ( etab[eid].fho ) {
(*hdfswrite)(hfs, etab[eid].fho, (void *)xbuff, xbuffl);
} else {
if ( etab[eid].fo ) {
(void)fwrite ( xbuff, 1, xbuffl, etab[eid].fo );
}
}
#else
(void)fwrite ( xbuff, 1, xbuffl, etab[eid].fo );
#endif
}
/* close single outut file */
if (!(etab[eid].flg & 01000)) { /* non multiparted output */
#ifdef HDFS
if (etab[eid].fho)
(*hdfsclose)(hfs, etab[eid].fho);
#endif
if (etab[eid].fo && etab[eid].fo != stdout)
fclose(etab[eid].fo);
}
/* Print stats */
fprintf(stderr,"[%d] %s Extract statistics:\n", ptid, odbid);
if ( etab[eid].flg2 & 0100000 ) /* print custom SQL file name */
fprintf(stderr,"\t[%d] Source (SQL file): %s\n", ptid, etab[eid].cols);
else if ( etab[eid].flg2 & 040000 ) /* print custom SQL file name */
fprintf(stderr,"\t[%d] Source (SQL): %s\n", ptid, etab[eid].sql);
else /* print input table name */
fprintf(stderr,"\t[%d] Source: %s\n", ptid, etab[eid].src);
if ( etab[eid].flg2 & 01000000000 ) {
fprintf(stderr,"\t[%d] Target: [MAPR] %s\n", ptid, etab[par].tgt);
} else if ( etab[eid].flg2 & 0100 ) {
fprintf(stderr,"\t[%d] Target: [HDFS] %s\n", ptid, etab[par].tgt);
} else {
fprintf(stderr,"\t[%d] Target: %s\n", ptid, etab[par].tgt);
}
fprintf(stderr,"\t[%d] Record buffer size: %s bytes\n", ptid, strmnum((double)etab[par].k,num,sizeof(num),0));
fprintf(stderr,"\t[%d] Rowset size: %s\n", ptid, strmnum((double)etab[par].r,num,sizeof(num),0));
fprintf(stderr,"\t[%d] Rowset buffer size: %s KiB\n", ptid, strmnum((double)etab[par].k/1024.0*etab[par].r,num,sizeof(num),2));
seconds = (double)etab[eid].mr/1000.0 ;
fprintf(stderr,"\t[%d] Pre-extract time: %s s (%s)\n", ptid,
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
seconds = (double)etab[eid].nrt/1000.0 ;
fprintf(stderr,"\t[%d] Extract time: %s s (%s)\n", ptid,
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
fprintf(stderr,"\t[%d] Total records extracted: %s ", ptid, strmnum((double)nr,num,sizeof(num),0));
fprintf(stderr,"(%s krec/s)\n", strmnum((double)nr*1.0/etab[eid].nrt,num,sizeof(num),3));
fprintf(stderr,"\t[%d] Total data bytes written: %s ", ptid, strmnum((double)b,num,sizeof(num),0));
fprintf(stderr,"(%s KiB/s)\n", strmnum((double)b/1.024/etab[eid].nrt,num,sizeof(num),3));
if ( i > 1 ) { /* more than one extract stream... */
for ( i = 0 ; i < no ; i++ ) {
if ( etab[i].parent == par ) {
fprintf(stderr,"\t\t[%d] %s records extracted", i, strmnum((double)etab[i].nr,num,sizeof(num),0));
fprintf(stderr," (%s bytes)", strmnum((double)etab[i].nbs,num,sizeof(num),0));
seconds = (double)etab[i].nrt/1000.0 ;
fprintf(stderr," in %s s (%s)\n",
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
}
}
}
if ( etab[eid].td ) { /* last thread frees table desc structure memory */
for ( i = 0 ; i < etab[eid].cmt ; i++ )
free ( etab[eid].td[i].Oname );
free(etab[eid].td);
}
if ( etab[eid].post ) { /* run post-SQL script */
etab[eid].type = 'f'; /* set execution type to file during runsql() */
etab[eid].fo = stdout; /* set output to stdout */
var_set ( &thps[tid].tva, VTYPE_I, "src", etab[eid].src );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].post[0] == '@' ) /* run a sql script */
(void)runsql(tid, eid, 0, (etab[eid].post + 1 ));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].post, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
}
if ( etab[eid].flg2 & 0040 )
free ( etab[eid].cols ) ;
if ( etab[eid].flg2 & 0100000 )
free ( etab[eid].sql ) ;
}
oextract_exit:
MutexUnlock( &etab[par].gmutex );
/* Free memory */
if ( Ocmd )
free ( Ocmd );
if ( xbuff )
free ( xbuff ) ;
}
/* Ocopy:
* copy between ODBC connections
*
* eid (I): etab entry ID to run
*
* return: 0=OK, 1=Fetch Error, 2=Load Error
*/
static int Ocopy(int eid)
{
SQLCHAR *Ocmd = 0; /* ODBC Command buffer */
SQLCHAR *O = 0; /* ODBC temp variable for realloc */
SQLCHAR *Osq = 0; /* ODBC first row sequence buffer address */
SQLRETURN Or=0; /* ODBC return value */
size_t cl=CMD_CHUNK; /* ODBC Command Length */
size_t rbl = 0; /* rowset buffer length */
size_t sqo = 0; /* sequence offset */
int j = 0, /* loop variable */
k = 0, /* loop variable */
i = 0, /* loop variable */
np = 0; /* Number of parameters */
long max = (long) etab[eid].mr ; /* max record to copy flag */
unsigned long nw=0, /* no of wait cycles */
nt=0; /* No of total write cycles */
int tid = etab[eid].id; /* Thread ID */
int echild = etab[eid].child; /* first child process eid */
int tchild = etab[echild].id; /* first child process tid */
int nchild = (int)etab[eid].nloader; /* number of children: each 'c'/'p' (extraction) thread
has 'nchild' children 'C'/'P' (loading) threads having etab[]
index from 'echild' to 'echild + nchild'*/
int gpar = etab[eid].k; /* grand parent eid */
int gchild = gpar+1; /* grand parent first child eid */
int gptid = etab[gpar].id; /* grand parent tid */
SQLSMALLINT Oncol; /* ODBC number of columns in the result set */
unsigned int ucs = 0; /* Local ucs2toutf8 conversion flag */
SQLCHAR *Op = 0 ; /* Orowsetl buffer pointer used during UCS-2/UTF-8 conversion */
SQLCHAR Oname[MAXOBJ_LEN]; /* ODBC Column Name */
SQLUSMALLINT Otf = 0 ; /* ODBC target field number */
struct timeval tve; /* timeval struct to define elapesd/timelines */
long telaps = 0, /* Elapsed time in ms */
tinit = 0; /* Initialization time in ms */
char num[32]; /* formatted numbers */
char tim[15]; /* formatted time string */
char type = etab[eid].type ; /* save thread type */
SQLULEN Orespl = 0; /* ODBC ulen to store # of fetched rows */
unsigned long nr = 0; /* total number of records copied by pool */
SQLHSTMT Ostmt=thps[tid].Os; /* Local ODBC Statement handle */
double seconds = 0; /* seconds used for timings */
static int tdbteid = 0; /* EID with target dbt */
uint64_t lseq = 0 ; /* local sequence value */
unsigned long ul = 0 ; /* sequence loop variable */
int fexst = 0 ; /* function exist status: 0=OK, 1=Fetch Error, 2=Load Error */
#ifdef ODB_PROFILE
struct timespec tsp1, tsp2 ; /* Profiling function */
unsigned long tf = 0 ; /* Fetch profiled time in nanoseconds */
unsigned long ts = 0 ; /* Thread synch time in nanoseconds */
unsigned long tm = 0 ; /* memcpy profiled time in nanoseconds */
#endif
/* Allocate Ocmd memory */
cl += etab[eid].sql ? strlen(etab[eid].sql) : 0;
cl += etab[eid].src ? strlen(etab[eid].src) : 0;
cl += etab[eid].map ? strlen(etab[eid].map) : 0;
cl += etab[eid].cols ? strlen(etab[eid].cols) : 0;
if ( ( Ocmd = malloc ( cl ) ) == (void *) NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating Ocmd memory\n", __LINE__);
goto ocopy_exit;
}
if ( etab[eid].flg & 010000000 ) /* Convert ucs2toutf8 */
ucs = 1 ;
/* First thread to run: truncate/ifempty check/pre-SQL on target (using child connection) */
MutexLock(&etab[gpar].gmutex); /* lock shared mutex */
if ( etab[gpar].lstat == 4 ) { /* pre-copy work to do */
if ( tdbteid ) { /* target dbt was already found */
etab[gchild].dbt = etab[tdbteid].dbt ;
} else {
etab[gchild].dbt = checkdb ( echild, &thps[tchild].Oc, NULL, NULL ); /* check tgt DB type */
tdbteid = gchild ;
}
if ( ( etab[eid].flg2 & 0200000 ) && /* if bind=auto */
( ( etab[eid].dbt != etab[gchild].dbt ) || /* different SRC/TGT */
( etab[eid].dbt == 0 && etab[gchild].dbt == 0 ) ) ) /* or both SRC & TGT are generic */
etab[gpar].flg2 |= 04000 ; /* set gpar bind=char */
if ( !etab[eid].tgtsql )
var_set ( &thps[tchild].tva, VTYPE_I, "tgt", etab[eid].run );
if ( etab[eid].pre ) { /* run pre SQL */
etab[echild].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) { /* run a sql script */
etab[eid].type = 'f'; /* set execution type to file during runsql() */
j = runsql(tchild, echild, 0, (etab[eid].pre + 1 ));
} else { /* Run single SQL command */
etab[eid].type = 'x'; /* set execution type to file during runsql() */
j = Oexec(tchild, echild, 0, 0, (SQLCHAR *)etab[eid].pre, "");
}
etab[echild].flg2 &= ~020000000; /* reset Oexec to allocate/use its own handle */
etab[echild].nr = 0; /* reset number of resulting rows */
etab[eid].type = type ;
etab[gpar].lstat = j ? 5 : 0; /* set "SQL" done (with or without errors)*/
if ( j && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error running pre-SQL. Exiting because Stop On Error has set\n",
__LINE__);
goto ocopy_exit;
}
}
if ( etab[eid].flg & 0002 ) { /* truncate target table */
strmcpy((char *)Ocmd, dbscmds[etab[gchild].dbt].trunc, cl);
Ocmd = (SQLCHAR *)var_exp((char *)Ocmd, &cl, &thps[tchild].tva);
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: Now truncating target table (%s)\n", (char *)Ocmd);
etab[echild].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
j = Oexec(tchild, echild, 0, 0, Ocmd, ""); /* Run Truncate */
etab[echild].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( j && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error truncating Target table. Exiting because Stop On Error was set\n",
__LINE__);
goto ocopy_exit;
}
if ( etab[echild].cmt ) { /* Autocommit off: have to commit truncate */
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "odb: Autocommit off. Now committing truncate target table (%s)\n", (char *)Ocmd);
if (!SQL_SUCCEEDED(Or=SQLEndTran(SQL_HANDLE_DBC, thps[tchild].Oc, SQL_COMMIT))) {
Oerr(echild, tchild, __LINE__, thps[tchild].Oc, SQL_HANDLE_DBC);
goto ocopy_exit;
}
}
etab[echild].nr = 0; /* reset number of resulting rows */
etab[gpar].lstat = 0; /* set "truncate" done */
}
if ( etab[eid].flg & 0200000 ) { /* check ifempty */
j = (int)ifempty(echild, etab[eid].run);
if ( j ) {
etab[gpar].lstat = j ? 6 : 0; /* set "ifempty" done (with or without errors)*/
fprintf(stderr, "odb [Ocopy(%d)] - Target table %s is not empty\n",
__LINE__, etab[eid].run);
etab[gpar].post = 0; /* do not run post SQL */
goto ocopy_exit;
}
}
etab[gpar].lstat = 0 ; /* set lstat write available */
}
etab[eid].lstat = 0; /* set "SQL" done (with or without errors)*/
MutexUnlock(&etab[gpar].gmutex); /* unlock shared mutex */
if ( etab[gpar].lstat == 6 || /* tgt not empty & ifempty is set */
( etab[gpar].lstat == 5 && etab[eid].flg & 0004 ) ) /* pre-SQL errors & soe */
goto ocopy_exit;
/* Run mpre-SQL on source */
if ( etab[eid].mpre ) {
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].mpre[0] == '@' ) { /* run a sql script */
etab[eid].type = 'f'; /* set execution type to file during runsql() */
j = runsql(tid, eid, 0, (etab[eid].mpre + 1 ));
} else{ /* Run single SQL command */
etab[eid].type = 'x'; /* set execution type to file during runsql() */
j = Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].mpre, "");
}
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
if ( j && etab[eid].flg & 0004 ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error running mpre-SQL. Exiting because Stop On Error is set\n",
__LINE__);
goto ocopy_exit;
}
etab[echild].nr = 0; /* reset number of resulting rows */
etab[eid].type = type ;
}
/* Set connection attribute Read Only (on source) */
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[tid].Oc,
SQL_ATTR_ACCESS_MODE, (SQLPOINTER)SQL_MODE_READ_ONLY, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
return(fexst);
}
/* Build Extract Command */
if ( etab[eid].ps > 1 ) {
if ( etab[eid].flg2 & 040000 ) { /* custom parallel SQL */
/* Set initial Ocmd */
strmcpy((char *)Ocmd, etab[eid].sql, cl);
/* Set tds and cds variables */
(void)snprintf(num, sizeof(num), "%u", etab[echild].ps);
/* I'm using echild here above because gpar could be decremented by other threads
before being used by thread with eid=gpar */
var_set ( &thps[tid].tva, VTYPE_I, "tds", num );
(void)snprintf(num, sizeof(num), "%ld", etab[eid].sbmin);
var_set ( &thps[tid].tva, VTYPE_I, "cds", num );
/* Expand Ocmd */
Ocmd = (SQLCHAR *)var_exp((char *)Ocmd, &cl, &thps[tid].tva);
} else if ( etab[eid].sb ) {
snprintf((char *) Ocmd, cl,
"SELECT %s FROM %s WHERE %s >= %ld AND %s < %ld %s%s %s",
etab[eid].cols ? etab[eid].cols : "*",
etab[eid].src, etab[eid].sb, etab[eid].sbmin, etab[eid].sb, etab[eid].sbmax,
etab[eid].map ? "AND " : "", etab[eid].map ? etab[eid].map : "",
etab[eid].flg & 0001 ? dbscmds[etab[eid].dbt].uncomm : "");
} else if ( etab[eid].dbt == ORACLE ) {
snprintf((char *) Ocmd, cl,
"SELECT %s FROM %s WHERE MOD(ORA_HASH(ROWID), %u) = %ld%s%s",
etab[eid].cols ? etab[eid].cols : "*",
etab[eid].src, etab[eid].ps, etab[eid].sbmin,
etab[eid].map ? " AND " : "", etab[eid].map ? etab[eid].map : "");
} else if ( etab[eid].dbt == SQLSERVER ) {
snprintf((char *)Ocmd, cl,
/* the following will provide a better distribution but is (much) slower:
"SELECT %s FROM %s WHERE ABS(HASHBYTES('MD5',%%%%PHYSLOC%%%%) %% %u) = %ld%s%s" */
"SELECT %s FROM %s WHERE (%%%%PHYSLOC%%%% / 147) %% %u = %ld%s%s",
etab[eid].cols ? etab[eid].cols : "*",
etab[eid].src, etab[eid].ps, etab[eid].sbmin,
etab[eid].map ? " AND " : "", etab[eid].map ? etab[eid].map : "");
} else {
if (!SQL_SUCCEEDED(Or=SQLExecDirect (Ostmt,
(SQLCHAR *)"CONTROL QUERY DEFAULT DBTR_PROCESS 'ON'", SQL_NTS))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
snprintf((char *)Ocmd, cl,
"SELECT %s FROM TABLE(TABLE %s, PARTITION NUMBER FROM %u TO %u) %s%s %s",
etab[eid].cols ? etab[eid].cols : "*",
etab[eid].src, etab[eid].sp, etab[eid].ep,
etab[eid].map ? "WHERE " : "", etab[eid].map ? etab[eid].map : "",
etab[eid].flg & 0001 ? dbscmds[etab[eid].dbt].uncomm : "");
}
} else if ( etab[eid].sql ) { /* no parallel custom SQL */
strmcpy((char *)Ocmd, etab[eid].sql, cl);
} else { /* no parallel SQL copy */
snprintf((char *)Ocmd, cl, "SELECT %s FROM %s%s%s %s",
etab[eid].cols ? etab[eid].cols : "*", etab[eid].src,
etab[eid].map ? " WHERE " : "", etab[eid].map ? etab[eid].map : "",
etab[eid].flg & 0001 ? dbscmds[etab[eid].dbt].uncomm : "");
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "[%d] odb [Ocopy(%d)] - SOURCE statement: %s\n",
tid, __LINE__, (char *)Ocmd);
/* Prepare command */
if ((Or=SQLPrepare(Ostmt, Ocmd, SQL_NTS)) != SQL_SUCCESS) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if ( Or != SQL_SUCCESS_WITH_INFO )
goto ocopy_exit;
}
/* Execute command */
if (!SQL_SUCCEEDED(Or=SQLExecute(Ostmt))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
/* Find number of returned columns */
if (!SQL_SUCCEEDED(Or=SQLNumResultCols(Ostmt, &Oncol))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
etab[eid].sp = (unsigned int)Oncol; /* save number of src/tgt table fields for prec */
/* Allocate memory for the tdesc array */
if ( (etab[eid].td = (struct tdesc *)calloc ((size_t)Oncol, sizeof(struct tdesc))) == (void *)NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating tdesc array memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto ocopy_exit;
}
/* Get Result Set columns descriptions */
for (j = 0; j < Oncol; j++) {
if ( !SQL_SUCCEEDED(Or=SQLDescribeCol(Ostmt, (SQLUSMALLINT)(j+1),
NULL, 0, &etab[eid].td[j].OnameLen, &etab[eid].td[j].Otype, &etab[eid].td[j].Osize,
&etab[eid].td[j].Odec, &etab[eid].td[j].Onull))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if ((etab[eid].td[j].Oname = (SQLCHAR*)malloc(etab[eid].td[j].OnameLen + 1)) == (void *)NULL) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating etab[%d].td[%d].Oname array memory: [%d] %s\n",
__LINE__, eid, j, errno, strerror(errno));
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or = SQLColAttribute(Ostmt, (SQLUSMALLINT)(j + 1), SQL_DESC_NAME, etab[eid].td[j].Oname,
etab[eid].td[j].OnameLen + 1, &etab[eid].td[j].OnameLen, NULL))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if ( etab[gpar].flg2 & 04000 ) { /* Force Char Binding */
if(!SQL_SUCCEEDED(Or=SQLColAttribute(Ostmt, (SQLUSMALLINT)(j+1), SQL_DESC_DISPLAY_SIZE,
(SQLPOINTER) NULL, (SQLSMALLINT) 0, (SQLSMALLINT *) NULL, (SQLPOINTER)&etab[eid].td[j].OdisplaySize))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if ( etab[eid].dbt != VERTICA ) { /* Vertica's CHAR field length is in bytes (not chars) */
if ( etab[eid].dbt == ORACLE && etab[eid].td[j].Otype == SQL_TYPE_TIMESTAMP )
/* The precision of Oracle's Timestamp is always 9 */
{
etab[eid].td[j].OdisplaySize = SQL_TIMESTAMP_LEN + 10; /* Display Size of TIMESTAMP(9) */
}
switch (etab[eid].td[j].Otype) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
etab[eid].td[j].OdisplaySize *= etab[eid].bpwc; /* Space for UTF-8 conversion */
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
etab[eid].td[j].OdisplaySize *= etab[eid].bpc;
break;
}
}
etab[eid].td[j].Octype = SQL_C_CHAR ; /* Bind to char */
} else { /* Try to use "C Default" data types to minimize conversions and reduce buffer size */
switch ( etab[eid].td[j].Otype ) {
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_BIGINT:
etab[eid].td[j].Osize = sizeof (SQLBIGINT) ;
etab[eid].td[j].Octype = SQL_C_SBIGINT ;
break;
case SQL_FLOAT:
case SQL_DOUBLE:
etab[eid].td[j].Osize = sizeof (SQLDOUBLE) ;
etab[eid].td[j].Octype = SQL_C_DOUBLE ;
break;
case SQL_TYPE_DATE:
etab[eid].td[j].Osize = sizeof (SQL_DATE_STRUCT) ;
etab[eid].td[j].Octype = SQL_C_TYPE_DATE ;
break;
case SQL_TYPE_TIMESTAMP:
etab[eid].td[j].Osize = sizeof (SQL_TIMESTAMP_STRUCT) ;
etab[eid].td[j].Octype = SQL_C_TYPE_TIMESTAMP ;
break;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
etab[eid].td[j].Octype = SQL_C_BINARY ;
break;
default:
etab[eid].td[j].Octype = SQL_C_CHAR ;
if(!SQL_SUCCEEDED(Or=SQLColAttribute(Ostmt, (SQLUSMALLINT)(j+1), SQL_DESC_DISPLAY_SIZE,
(SQLPOINTER) NULL, (SQLSMALLINT) 0, (SQLSMALLINT *) NULL, (SQLPOINTER) &etab[eid].td[j].OdisplaySize))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if ( etab[eid].dbt != VERTICA ) { /* Vertica's CHAR field length is in bytes (not chars) */
switch ( etab[eid].td[j].Otype ) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
etab[eid].td[j].OdisplaySize *= etab[eid].bpwc; /* Space for UTF-8 conversion */
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
etab[eid].td[j].OdisplaySize *= etab[eid].bpc;
break;
}
}
break;
}
}
++etab[eid].td[j].OdisplaySize; /* buffer should contain space for NULL termination in order to avoid truncation */
#ifdef __hpux
if (Ors[j] % WORDSZ )
padl[j] = WORDSZ - (unsigned int)etab[eid].td[j].OdisplaySize % WORDSZ ;
#endif
if ( ( j + 1 ) == (int)etab[eid].seqp ) { /* Allocate extra space for sequence and length indicator */
sqo = etab[eid].s ; /* record sequence offset */
etab[eid].s += (size_t) ( sizeof(SQLBIGINT) + sizeof(SQLLEN) ) ;
}
etab[eid].td[j].start = (int)etab[eid].s;
etab[eid].td[j].Ocdatabufl = etab[eid].td[j].Octype == SQL_C_CHAR ? etab[eid].td[j].OdisplaySize : (SQLLEN)etab[eid].td[j].Osize;
etab[eid].s += (size_t)(etab[eid].td[j].Ocdatabufl + etab[eid].td[j].pad + sizeof(SQLLEN));
if ( etab[eid].flg & 0400000 && eid == gpar ) /* Grand Parent to Describe result set */
fprintf(stderr, "--- col #%d, name=%s type=%d (%s) size=%lu, dec=%d, nullable=%s\n",
j, (char *)Oname, etab[eid].td[j].Otype, expandtype(etab[eid].td[j].Otype), (unsigned long)etab[eid].td[j].Osize,
(int) etab[eid].td[j].Odec, etab[eid].td[j].Onull ? "yes" : "no");
if ( fdmp && eid == gpar ) /* Grand Parent to initialize ODBC dump */
fprintf(fdmp, "[%d] Column %d: name=%s, type=%d (%s), size=%lu, decimals=%d, nullable=%s\n",
tid, j, (char *)etab[eid].td[j].Oname, (int)etab[eid].td[j].Otype, expandtype(etab[eid].td[j].Otype),
(unsigned long)etab[eid].td[j].Ocdatabufl, (int)etab[eid].td[j].Odec, etab[eid].td[j].Onull ? "yes" : "no" ) ;
}
/* Calculate rowset if buffer size is set */
if ( etab[eid].rbs ) {
etab[eid].r = etab[eid].rbs / etab[eid].s;
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; /* at least one record at a time */
if ( etab[eid].mr && etab[eid].r > etab[eid].mr ) /* if # records to fetch < rowset ... */
etab[eid].r = etab[eid].mr; /* make rowset = records to fetch */
for ( i = echild ; i < echild + nchild ; i++ )
etab[i].r = etab[eid].r;
}
/* Adjust commit if multiplier */
if ( etab[eid].flg2 & 0001 ) { /* commit as multiplier */
etab[eid].cmt *= (int)etab[eid].r ;
for ( i = echild ; i < echild + nchild ; i++ )
etab[i].cmt = etab[eid].cmt;
}
/* Allocate memory for result set from source */
if ( (etab[eid].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating source rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto ocopy_exit;
}
/* Allocate memory for children rowset and status array and pass td to children */
for ( i = echild ; i < echild + nchild ; i++ ) {
if ( (etab[i].Orowsetl = (SQLCHAR *)calloc (etab[eid].r, etab[eid].s)) == (void *)NULL ||
(etab[i].Ostatusl = (SQLUSMALLINT *)calloc (etab[eid].r, sizeof(SQLUSMALLINT))) == (void *)NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating target rowset memory for child etab[%d]: [%d] %s\n",
__LINE__, i, errno, strerror(errno));
goto ocopy_exit;
}
etab[i].td = etab[eid].td;
etab[i].sp = etab[eid].sp;
}
rbl = etab[eid].r * etab[eid].s ;
/* Set Children Commit */
if ( etab[eid].cmt ) {
for ( i = echild ; i < echild + nchild ; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[i].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Oc, SQL_HANDLE_DBC);
}
}
} else {
for ( i = echild ; i < echild + nchild ; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[etab[i].id].Oc,
SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Oc, SQL_HANDLE_DBC);
}
}
}
/* Reset gpar error flag */
etab[gpar].flg &= ~04000 ;
/* Set row-wise binding for source */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_ARRAY_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_STATUS_PTR,
NULL, 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROWS_FETCHED_PTR,
&Orespl, 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
/* Set row-wise binding for target */
for ( i = echild ; i < echild + nchild ; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[i].id].Os, SQL_ATTR_PARAM_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[i].id].Os, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[i].id].Os, SQL_ATTR_PARAM_STATUS_PTR,
etab[i].Ostatusl, 0))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(thps[etab[i].id].Os, SQL_ATTR_PARAMS_PROCESSED_PTR,
&etab[i].Oresl, 0))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
}
/* Bind Source */
for (j = 0; j < Oncol; j++) {
if (!SQL_SUCCEEDED(Or=SQLBindCol(Ostmt, (SQLUSMALLINT)j + 1, etab[eid].td[j].Octype,
&etab[eid].Orowsetl[etab[eid].td[j].start], etab[eid].td[j].Ocdatabufl,
(SQLLEN *)&etab[eid].Orowsetl[etab[eid].td[j].start + etab[eid].td[j].Ocdatabufl + etab[eid].td[j].pad]))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
}
/* Bind Target */
np = etab[eid].tgtsql ? (int) etab[eid].ep : (int) Oncol;
for (j = 0; j < np; j++) {
k = etab[eid].tgtsql ? (int)etab[eid].tgtp[j] - 1 : j ;
Otf = (SQLUSMALLINT)(j+1); /* SQLBindParameter target field number */
if ( etab[eid].seqp && etab[eid].seqp <= (unsigned int)(j+1) )
Otf++;
for ( i = echild ; i < echild + nchild ; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLBindParameter(thps[etab[i].id].Os, Otf,
SQL_PARAM_INPUT, etab[eid].td[k].Octype, etab[eid].td[k].Otype, etab[eid].td[k].Osize,
etab[eid].td[k].Odec, &etab[i].Orowsetl[etab[eid].td[k].start], etab[eid].td[k].Ocdatabufl,
(SQLLEN *)&etab[i].Orowsetl[etab[eid].td[k].start + etab[eid].td[k].Ocdatabufl + etab[eid].td[k].pad]))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
}
}
/* Bind sequence to target */
if ( etab[eid].seqp ) {
for ( i = echild ; i < echild + nchild ; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLBindParameter(thps[etab[i].id].Os, (SQLUSMALLINT)etab[eid].seqp,
SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_BIGINT, (SQLULEN)sizeof(SQLBIGINT), 0,
&etab[i].Orowsetl[sqo], 0, (SQLLEN *)&etab[i].Orowsetl[sqo+sizeof(SQLUBIGINT)]))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
}
}
/* Build child's statement */
if ( etab[eid].tgtsql ) { /* custom tgt sql */
if ( Ocmd ) /* free previously allocated Ocmd */
free ( Ocmd ) ;
Ocmd = (SQLCHAR *)etab[eid].tgtsql ;
} else {
if ( cl < (size_t)( 172 + Oncol * 2 ) ) {
cl = 172 + Oncol * 2 ;
O = Ocmd ;
if ( ( O = (SQLCHAR *)realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto ocopy_exit;
}
Ocmd = O ;
}
if (!strcasecmp(etab[eid].loadcmd, "IN"))
{
snprintf((char *)Ocmd, cl, "INSERT %s%sINTO %s VALUES (?",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "", etab[eid].run);
}
else if (!strcasecmp(etab[eid].loadcmd, "UP"))
{
snprintf((char *)Ocmd, cl, "UPSERT %s%sINTO %s VALUES (?",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "", etab[eid].run);
}
else if (!strcasecmp(etab[eid].loadcmd, "UL"))
{
snprintf((char *)Ocmd, cl, "UPSERT USING LOAD %s%sINTO %s VALUES (?",
etab[eid].flg & 040000000 ? "/*+ DIRECT */ " : "",
etab[eid].flg2 & 0002 ? "WITH NO ROLLBACK " : "", etab[eid].run);
}
for (j = 1; j < Oncol; j++)
(void) strmcat ( (char *) Ocmd, ",?", cl, 0);
if ( etab[eid].seqp )
(void) strmcat ( (char *) Ocmd, ",?", cl, 0);
(void) strmcat ( (char *) Ocmd, ")", cl, 0);
}
if ( f & 020000 ) /* if verbose */
fprintf(stderr, "[%d] odb [Ocopy(%d)] - TARGET statement: %s\n",
tid, __LINE__, (char *)Ocmd);
/* Prepare INSERT for children */
for ( i = echild ; i < echild + nchild ; i++ ) {
if (!SQL_SUCCEEDED(Or=SQLPrepare(thps[etab[i].id].Os, Ocmd, SQL_NTS))) {
Oerr(i, etab[i].id, __LINE__, thps[etab[i].id].Os, SQL_HANDLE_STMT);
goto ocopy_exit;
}
}
/* Reset children statuses to write available */
for ( i = echild ; i < echild + nchild ; i++ )
etab[i].lstat = 0;
/* Register Copy Start Time */
gettimeofday(&tve, (void *)NULL);
tinit = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
etab[eid].nbs = (unsigned long) (1000*tve.tv_sec+tve.tv_usec/1000);
/* Fetch data */
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
while ( go &&
SQL_SUCCEEDED(Or = SQLFetchScroll(Ostmt, SQL_FETCH_NEXT, 0))) {
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
tf += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
nt++; /* increase total number of cycles counter */
while ( go ) {
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
MutexLock(&etab[eid].pmutex); /* lock shared mutex */
while ( go ) {
if ( etab[gpar].flg & 04000 ) { /* Oloadbuff error flag is set */
fexst = 2 ; /* Function Exit Status = Load Error */
break;
}
for ( i = echild ; i < echild + nchild ; i++ ) {
if ( etab[i].lstat == 0 ) { /* if loader available */
etab[i].lstat = 2; /* mark loader buffer write_busy */
break ;
}
}
if ( i >= (echild + nchild) ) { /* no loaders available */
nw++ ; /* increase waiting cycles counter */
CondWait(&etab[eid].pcvp, &etab[eid].pmutex); /* wait */
} else {
break;
}
}
MutexUnlock(&etab[eid].pmutex); /* unlock mutex */
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
ts += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
if ( etab[gpar].flg & 04000 ) { /* Oloadbuff error flag is set */
fexst = 2 ; /* Function Exit Status = Load Error */
break;
}
if ( go == 0 ) /* In case we got a break after SQLFetch() */
break ;
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
if ( ucs ) { /* UCS-2 to UTF-8 conversion */
for ( j = 0, Op = etab[eid].Orowsetl ; j < (int)Orespl ; j++, Op += etab[eid].s ) {
for ( k = 0 ; k < Oncol ; k++ ) {
switch ( etab[eid].td[k].Otype ) {
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
if ( ucs2toutf8 ( Op + etab[eid].td[k].start ,
(SQLLEN *)(Op + etab[eid].td[k].start + etab[eid].td[k].Ocdatabufl + etab[eid].td[k].pad),
etab[eid].td[k].Ocdatabufl, etab[eid].bucs2 ) ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error converting UCS-2 column %s\n",
__LINE__, (char *)etab[eid].td[k].Oname);
}
break;
default:
break; /* do nothing */
}
}
}
}
memcpy(etab[i].Orowsetl, etab[eid].Orowsetl, rbl);
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
tm += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
if ( etab[eid].seqp ) { /* manage sequences */
MutexLock(&dlbmutex); /* lock global mutex */
if ( f2 & 0002 ) { /* if global sequence flag */
lseq = gseq ; /* get current global seq value */
gseq += (uint64_t)Orespl ; /* update global sequence value */
} else {
lseq = etab[gpar].seq ; /* get current sequence value */
etab[gpar].seq += (uint64_t)Orespl ; /* update grand parent sequence value */
}
MutexUnlock(&dlbmutex); /* unlock global mutex */
ul = (unsigned long) Orespl ;
Osq = &etab[i].Orowsetl[sqo] ; /* first row sequence address */
for ( ; ul ; ul--, Osq += etab[eid].s ) {
*((SQLUBIGINT *)Osq) = lseq++ ;
*((SQLLEN *)(Osq+sizeof(SQLUBIGINT))) = (SQLLEN)sizeof(SQLUBIGINT) ;
}
}
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
MutexLock(&etab[eid].pmutex); /* lock shared mutex */
etab[i].lstat = 1; /* mark loader buffer read_available */
MutexUnlock(&etab[eid].pmutex); /* lock shared mutex */
etab[i].ar = Orespl; /* inform loader about #rec to insert */
CondWakeAll(&etab[eid].pcvc); /* wake-up sleeping loader threads */
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
ts += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
break; /* exit this loop and... */
}
if ( etab[gpar].flg & 04000 ) { /* Oloadbuff error flag is set */
fexst = 2 ; /* Function Exit Status = Load Error */
break;
}
if ( max ) { /* max record to copy limit exists */
max -= (long)Orespl; /* decrese max record counter */
if ( max <= 0 ) { /* max limit has been reached */
break ;
} else if (max < (long)etab[eid].r) { /* we do not need to fetch full rowsets any more */
if (!SQL_SUCCEEDED(Or = SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_ARRAY_SIZE,
(SQLPOINTER)(max), 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto ocopy_exit;
}
}
}
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
}
if ( !SQL_SUCCEEDED ( Or ) && Or != SQL_NO_DATA ) {
fexst = 1 ; /* Function Exit Status = Fetch Error */
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
etab[eid].flg |= 02000; /* mark EOF */
CondWakeAll(&etab[eid].pcvc);
/* Wait children thread */
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp1);
#endif
MutexLock(&etab[eid].pmutex);
while ( go ) {
for ( j = 0 , i = echild ; i < echild + nchild ; i++ )
j += etab[i].lstat ;
if ( j != 10 * nchild ) { /* one or more child still working */
CondWait(&etab[eid].pcvp, &etab[eid].pmutex);
} else {
break;
}
}
MutexUnlock(&etab[eid].pmutex);
#ifdef ODB_PROFILE
clock_gettime(CLOCK_MONOTONIC, &tsp2);
ts += tspdiff ( &tsp1 , &tsp2 ) ;
#endif
/* Check Function Exit Status */
if ( etab[gpar].flg & 04000 )
fexst = 2 ;
if ( fexst )
goto ocopy_exit ;
/* Mark End Copy Timestamp */
telaps -= 1000*tve.tv_sec + tve.tv_usec/1000;
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps += 1000*tve.tv_sec + tve.tv_usec/1000;
/* Save Total/Wait cycles counter into global variables */
etab[eid].nt = nt ;
etab[eid].nw = nw ;
#ifdef ODB_PROFILE
fprintf(stderr, "[%d] (odb profile Ocopy) Fetch time (ns): %s\n", tid, strmnum((double)tf,num,sizeof(num),0));
fprintf(stderr, "[%d] (odb profile Ocopy) Thread synch time (ns): %s\n", tid, strmnum((double)ts,num,sizeof(num),0));
fprintf(stderr, "[%d] (odb profile Ocopy) Memory copy time (ns): %s\n", tid, strmnum((double)tm,num,sizeof(num),0));
#endif
/* print sequence info */
if ( etab[eid].seqp && f & 020000 ) /* if verbose */
fprintf(stderr, "[%d] odb [Ocopy(%d)] - Max sequence value: %llu\n",
tid, __LINE__, (unsigned long long)(lseq - 1) );
/* Decrease "still active" parallel streams counter. Last thread to print stats */
MutexLock( &etab[gpar].gmutex );
if ( etab[gpar].ps ) {
etab[gpar].ps--;
if ( etab[gpar].ps ) /* this is not the last thread */
goto ocopy_exit;
}
if ( !etab[gpar].ps ) { /* Last thread to print stats */
MutexUnlock( &etab[gpar].gmutex );
for ( j = 0 ; j < no ; j++ ) {
if ( ( etab[j].type == 'C' || etab[j].type == 'P' ) && etab[j].k == (unsigned int)gpar ) {
nr += etab[j].nr;
}
}
fprintf(stderr,"[%d] %s %s statistics:\n", tid, odbid, etab[eid].tgtsql ? "Pipe" : "Copy");
if ( etab[eid].flg2 & 0100000 ) /* print custom SQL file name */
fprintf(stderr,"\t[%d] Source (SQL file): %s\n", gptid, etab[eid].cols);
else if ( etab[eid].sql ) /* print custom SQL cmd */
fprintf(stderr,"\t[%d] Source (SQL): %s\n", gptid, etab[eid].sql);
else /* print input table name */
fprintf(stderr,"\t[%d] Source: %s\n", gptid, etab[eid].src);
if ( etab[eid].tgtsql && etab[eid].ns[0] == '@' ) /* print custom tgt sql file name */
fprintf(stderr,"\t[%d] Target (SQL file): %s\n", gptid, &etab[eid].ns[1]);
else if ( etab[eid].tgtsql )
fprintf(stderr,"\t[%d] Target (SQL): %s\n", gptid, etab[eid].tgtsql);
else
fprintf(stderr,"\t[%d] Target: %s\n", gptid, etab[eid].run);
fprintf(stderr, "\t[%d] Total number of columns: %s\n", gptid, strmnum((double)Oncol,num,sizeof(num),0));
fprintf(stderr, "\t[%d] ODBC row size: %s B (data)", gptid, strmnum((double)(etab[eid].s-Oncol*sizeof(SQLLEN)),num,sizeof(num),0));
fprintf(stderr, " + %s B (len ind)\n", strmnum((double)(Oncol*sizeof(SQLLEN)), num,sizeof(num),0));
fprintf(stderr,"\t[%d] Rowset size: %s\n", gptid, strmnum((double)etab[eid].r,num,sizeof(num),0));
fprintf(stderr,"\t[%d] Rowset buffer size: %s KiB\n", gptid, strmnum((double)rbl/1024.0,num,sizeof(num),2));
seconds = (double)tinit/1000.0 ;
fprintf(stderr,"\t[%d] Pre-%s time: %s s (%s)\n", gptid, etab[eid].tgtsql ? "pipe" : "copy",
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
seconds = (double)telaps/1000.0 ;
fprintf(stderr,"\t[%d] %s time: %s s (%s)\n", gptid, etab[eid].tgtsql ? "Pipe" : "Copy",
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
fprintf(stderr,"\t[%d] Total records %s: %s ", gptid, etab[eid].tgtsql ? "piped" : "copied",
strmnum((double)nr,num,sizeof(num),0));
fprintf(stderr,"(%s krec/s)\n", strmnum((double)nr*1.0/(telaps),num,sizeof(num),3));
fprintf(stderr, "\t[%d] %s throughput (ODBC): %s MiB/s ", gptid, etab[eid].tgtsql ? "Pipe" : "Copy",
strmnum((double)((etab[eid].s-Oncol*sizeof(SQLLEN))*nr)/1.024/1024.0/telaps,num,sizeof(num),3));
fprintf(stderr, " (%s GiB/h)\n",
strmnum((double)((etab[eid].s-Oncol*sizeof(SQLLEN))*nr)*3600.0/1.024/1024.0/1024.0/telaps,num,sizeof(num),3));
if ( j > 1 ) { /* more than one extract stream... */
for ( j = 0 ; j < no ; j++ ) {
if ( ( etab[j].type == 'c' || etab[j].type == 'p' ) && etab[j].k == (unsigned int)gpar ) {
fprintf(stderr,"\t\t[%d] Total/Wait cycles: %s/", etab[j].id, strmnum((double)etab[j].nt,num,sizeof(num),0));
fprintf(stderr,"%s\n", strmnum((double)etab[j].nw,num,sizeof(num),0));
for ( i = etab[j].child ; i < etab[j].child + nchild ; i++ ) {
fprintf(stderr,"\t\t\t[%d>%d] %s records %s", etab[j].id, etab[i].id,
strmnum((double)etab[i].nr,num,sizeof(num),0), etab[eid].tgtsql ? "piped" : "copied");
seconds = (double)etab[i].nrt/1000.0 ;
fprintf(stderr," in %s (%s s)\n",
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
}
}
}
}
if ( ( etab[eid].flg2 & 0040 ) && etab[eid].cols )
free ( etab[eid].cols ) ;
if ( etab[eid].flg2 & 0100000 )
free ( etab[eid].sql ) ;
if ( etab[eid].tgtp )
free ( etab[eid].tgtp ) ;
if ( etab[eid].tgtsql )
free ( etab[eid].tgtsql ) ;
if ( etab[eid].fdmp ) /* close errdmp file */
fclose(etab[eid].fdmp);
if ( etab[eid].type == 'c' ) /* it's a copy thread */
if ( etab[eid].src ) /* table (not SQL) copy */
if ( etab[eid].run )
free ( etab[eid].run ) ;
}
/* Clean & exit */
ocopy_exit:
MutexUnlock( &etab[gpar].gmutex );
etab[eid].flg |= 02000; /* mark EOF */
CondWakeAll(&etab[eid].pcvc);
(void)SQLFreeStmt(Ostmt, SQL_CLOSE);
(void)SQLFreeStmt(Ostmt, SQL_UNBIND);
if (Ocmd)
if (!etab[eid].tgtsql)
free ( Ocmd );
if (etab[eid].td) {
for (j = 0; j < (int)etab[eid].sp; ++j) {
if (etab[eid].td[j].Oname)
free(etab[eid].td[j].Oname);
}
free(etab[eid].td);
}
if ( etab[eid].Orowsetl )
free (etab[eid].Orowsetl);
return(fexst);
}
/* Odiff:
* Extract tables to compare them
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void Odiff(int eid)
{
SQLCHAR *Ocmd = 0; /* ODBC Command buffer */
SQLCHAR *O = 0; /* ODBC temp variable for realloc */
SQLRETURN Or=0; /* ODBC return value */
size_t cl=CMD_CHUNK; /* ODBC Command Length */
size_t rbl = 0; /* rowset buffer length */
int j = 0; /* loop variable */
unsigned int i = 0; /* loop variable */
int tid = etab[eid].id; /* Thread ID */
int zchild = etab[eid].child; /* child Zthread process eid */
int dchild = etab[zchild].child;/* child Dthread process eid */
int epars = eid; /* parent process eid */
int gpar = etab[eid].k; /* grand parent eid */
int fchild = etab[eid].k + 1; /* grand parent child eid */
int gptid = etab[gpar].id; /* grand parent tid */
struct timeval tve; /* timeval struct to define elapesd/timelines */
long telaps = 0, /* Elapsed time in ms */
tinit = 0; /* Initialization time in ms */
char num[64]; /* formatted numbers */
char tim[15]; /* formatted time string */
SQLULEN Orespl = 0; /* ODBC ulen to store # of fetched rows */
unsigned char wava = 0001; /* used to check buffer write_available */
unsigned char wbsy = 0020; /* used to mark buffer write_busy */
unsigned char rava = 0004; /* used to mark buffer read_available */
unsigned int cmdl = 0; /* Ocmd length */
SQLHSTMT Ostmt=thps[tid].Os; /* Local ODBC Statement handle */
double seconds = 0; /* seconds used for timings */
/* Run pre-SQL on target (using child connection) */
if ( etab[gpar].flg2 & 02000 ) /* pre-SQL executed with errors & soe */
goto odiff_exit;
MutexLock(&etab[gpar].gmutex); /* lock shared mutex */
if ( !(etab[gpar].flg2 & 0400) ) { /* if checkdb/pre is not done */
etab[fchild].dbt = checkdb ( fchild, &thps[etab[fchild].id].Oc, NULL, NULL ); /* check tgt DB type */
if ( etab[eid].pre ) { /* Pre-SQL to run */
var_set ( &thps[etab[dchild].id].tva, VTYPE_I, "tgt", etab[eid].tgt );
etab[dchild].flg2 |= 020000000;/* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) /* run a sql script */
j = runsql(etab[dchild].id, dchild, 0, (etab[eid].pre + 1 ));
else /* Run single SQL command */
j = Oexec(etab[dchild].id, dchild, 0, 0, (SQLCHAR *)etab[eid].pre, "");
etab[dchild].flg2 &= ~020000000;/* Oexec to allocate/use its own handle */
etab[dchild].nr = 0; /* reset numer of resulting rows */
if ( j && etab[eid].flg & 0004 ) {
etab[eid].flg2 |= 02000 ; /* set pre-sql error & dtop on error flag */
fprintf(stderr, "odb [Odiff(%d)] - Error running pre-SQL. Exiting because Stop On Error is set\n",
__LINE__);
MutexUnlock(&etab[gpar].gmutex); /* unlock shared mutex */
goto odiff_exit;
}
}
etab[eid].flg2 |= 0400 ; /* set checkdb/pre flag done */
}
MutexUnlock(&etab[gpar].gmutex); /* lock shared mutex */
/* Set connection attribute Read Only */
if (!SQL_SUCCEEDED(Or=SQLSetConnectAttr(thps[tid].Oc,
SQL_ATTR_ACCESS_MODE, (SQLPOINTER)SQL_MODE_READ_ONLY, SQL_IS_UINTEGER))) {
Oerr(eid, tid, __LINE__, thps[tid].Oc, SQL_HANDLE_DBC);
return;
}
/* Allocate initial Ocmd memory */
if ( ( Ocmd = malloc ( cl * 2 ) ) == (void *) NULL ) {
fprintf(stderr, "odb [Ocopy(%d)] - Error allocating Ocmd memory\n", __LINE__);
goto odiff_exit;
}
Ocmd[0] = '\0';
cmdl = strmcat((char *)Ocmd, "SELECT ", cl, 0);
/* Build Extraction Command */
for ( j = 0 ; j < etab[eid].cmt ; j++ ) {
if ( j )
cmdl += strmcat ((char *)Ocmd, ",", cl, 0);
if ( ( ( etab[eid].type == 'd' && etab[eid].dbt == ORACLE ) ||
( etab[eid].type == 'D' && etab[fchild].dbt == ORACLE ) ) &&
( etab[eid].td[j].Otype == SQL_DECIMAL && etab[eid].td[j].Odec > 0 ) ) {
/* Oracle - differently from MySQL, PostgreSQL... export NUMBER(x,y) fields without trailing
* zeroes and this creates "false differences" comparing - for example - 12.30 with 12.3. So I cast all
* SQL_DECIMAL (Oracle number) to char using the "right" number of digits before/after the decimal separator.
* Be aware, odb uses the decimal separator defined through -dsep command line option */
for ( i = 0 ; i < etab[eid].td[j].Osize - etab[eid].td[j].Odec - 4 ; i++ )
num[i] = '9' ;
num[i++] = '0' ;
num[i++] = (char)dsep ;
for ( ; i < etab[eid].td[j].Osize - 2 ; i++ )
num[i] = '0' ;
num[i] = '\0';
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "TRIM(TO_CHAR(%s,\'%s\'))", etab[eid].td[j].Oname, num);
} else if ( ( etab[eid].type == 'd' && etab[eid].dbt == ORACLE ) &&
( etab[eid].flg2 & 01000000 ) && etab[eid].td[j].Otype == SQL_TYPE_TIMESTAMP ) {
/* Oracle ODBC data type for dates is SQL_TIMESTAMP. If odad (Oracle Dates As Dates) option is set we
* use TO_CHAR to select the Oracle dates as real dates in YYYY-MM-DD format */
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "TO_CHAR(%s,\'YYYY-MM-DD\')", etab[eid].td[j].Oname);
} else if ( ( ( etab[eid].type == 'd' && etab[eid].dbt == VERTICA ) ||
( etab[eid].type == 'D' && etab[fchild].dbt == VERTICA ) ) &&
( etab[eid].td[j].Otype == SQL_DECIMAL || etab[eid].td[j].Otype == SQL_NUMERIC ) &&
etab[eid].td[j].Odec > 0 ) {
/* Vertica - differently from MySQL, PostgreSQL... export NUMBER(x,y) fields without trailing
* zeroes and this creates "false differences" comparing - for example - 12.30 with 12.3. So I cast all
* SQL_DECIMAL (Oracle number) to char using the "right" number of digits before/after the decimal separator.
* Be aware, odb uses the decimal separator defined through -dsep command line option */
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "TRIM(TO_CHAR(%s))", etab[eid].td[j].Oname);
} else if ( ( etab[eid].flg & 0400000000 ) &&
( etab[eid].td[j].Otype == SQL_CHAR || etab[eid].td[j].Otype == SQL_WCHAR ) ) {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "TRIM(%s)", etab[eid].td[j].Oname);
} else if ( ( etab[eid].flg2 & 040000000 ) &&
( etab[eid].td[j].Otype == SQL_VARCHAR || etab[eid].td[j].Otype == SQL_WVARCHAR ) ) {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "TRIM(%s)", etab[eid].td[j].Oname);
} else if ( ( etab[eid].flg & 02000000 ) &&
( etab[eid].td[j].Otype == SQL_CHAR || etab[eid].td[j].Otype == SQL_WCHAR ) ) {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "RTRIM(%s)", etab[eid].td[j].Oname);
} else if ( ( etab[eid].flg & 04000000 ) &&
( etab[eid].td[j].Otype == SQL_VARCHAR || etab[eid].td[j].Otype == SQL_WVARCHAR ) ) {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl, "RTRIM(%s)", etab[eid].td[j].Oname);
} else {
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[j].Oname, cl, 0);
}
if ( cl - cmdl < CMD_CHUNK ) {
cl += CMD_CHUNK;
O = Ocmd ;
if ( ( O = (SQLCHAR *)realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Odiff(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
return;
}
Ocmd = O ;
}
#ifdef __hpux
/* length indicators have to be memory aligned to avoid bus errors */
if ( etab[eid].td[j].Osize % WORDSZ )
etab[eid].td[j].pad = WORDSZ - etab[eid].td[j].Osize % WORDSZ ;
#endif
if ( !etab[eid].td[j].dl && j && etab[eid].td[j-1].dl ) { /* no more order-by columns */
if ( etab[eid].s % WORDSZ ) /* Round-up (order-by only) rowset to wordsize */
etab[eid].s += WORDSZ - etab[eid].s % WORDSZ ;
etab[zchild].r = etab[eid].s ; /* and save it in the zchild structure */
}
etab[eid].td[j].start = (int)etab[eid].s;
etab[eid].s += (size_t)(etab[eid].td[j].Osize+etab[eid].td[j].pad+sizeof(SQLLEN));
}
/* Round-up rowset to wordsize to increase memcmp efficiency */
if ( etab[eid].s % WORDSZ )
etab[eid].s += WORDSZ - etab[eid].s % WORDSZ ;
etab[zchild].s = etab[eid].s ;
if ( etab[eid].flg2 & 0010 ) /* quick mode */
etab[zchild].r = etab[eid].s ;
/* Adjust count for single column tables */
if ( etab[eid].cmt == 1 )
etab[zchild].r = etab[zchild].s;
/* Build Extraction Command: add FROM/WHERE clause */
if ( etab[eid].ps ) {
if ( etab[eid].cr == 1 ) {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl,
" FROM %s WHERE %s < %ld",
etab[eid].tgt, etab[eid].sb, etab[eid].sbmax);
} else if ( etab[eid].cr == 2 ) {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl,
" FROM %s WHERE %s >= %ld",
etab[eid].tgt, etab[eid].sb, etab[eid].sbmin);
} else {
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl,
" FROM %s WHERE %s >= %ld AND %s < %ld",
etab[eid].type == 'd' ? etab[eid].src : etab[eid].tgt,
etab[eid].sb, etab[eid].sbmin, etab[eid].sb, etab[eid].sbmax);
}
if ( etab[eid].map )
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl,
" AND %s", etab[eid].map);
} else {
cmdl += snprintf((char *)(Ocmd + cmdl), cl - cmdl, " FROM %s",
etab[eid].type == 'd' ? etab[eid].src : etab[eid].tgt);
if ( etab[eid].map )
cmdl += snprintf((char *)(Ocmd+cmdl), cl - cmdl,
" WHERE %s", etab[eid].map);
}
/* Build Extraction Command: add ORDER BY columns */
for ( j = 0 ; etab[eid].td[j].dl && j < etab[eid].cmt ; j++ ) {
if ( j )
cmdl += strmcat ((char *)Ocmd, ",", cl, 0);
else
cmdl += strmcat ((char *)Ocmd, " ORDER BY ", cl, 0);
cmdl += strmcat ( (char *) Ocmd, (char *) etab[eid].td[j].Oname, cl, 0);
if ( cl - cmdl < CMD_CHUNK ) {
cl += CMD_CHUNK;
O = Ocmd ;
if ( ( O = (SQLCHAR *)realloc ( O, cl ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Odiff(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
return;
}
Ocmd = O ;
}
}
/* Print extract command if verbose */
if ( f & 020000 )
fprintf(stderr, "[%d] odb [Odiff(%d)] - %s EXTRACT statement: %s\n",
tid, __LINE__, etab[eid].type == 'd' ? "src" : "tgt", (char *)Ocmd);
/* Prepare command */
if ((Or=SQLPrepare(Ostmt, Ocmd, SQL_NTS)) != SQL_SUCCESS) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if ( Or != SQL_SUCCESS_WITH_INFO )
goto odiff_exit;
}
/* Execute command */
if (!SQL_SUCCEEDED(Or=SQLExecute(Ostmt))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto odiff_exit;
}
/* Calculate rowset if buffer size is set */
if ( etab[eid].rbs )
{
etab[eid].r = etab[eid].rbs / etab[eid].s;
etab[eid].r = etab[eid].r < 1 ? 1 : etab[eid].r; // at least one record at a time
}
/* Allocate memory for result set from source */
rbl = etab[eid].r * etab[eid].s ;
if ( (etab[eid].Orowsetl = (SQLCHAR *) calloc ( etab[eid].r, etab[eid].s )) == (void *)NULL ) {
fprintf(stderr, "odb [Odiff(%d)] - Error allocating source rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto odiff_exit;
}
/* Allocate memory for child rowset(s) */
if ( etab[eid].type == 'd' ) {
if ( (etab[zchild].Orowsetl = (SQLCHAR *) malloc (rbl)) == (void *)NULL ) {
fprintf(stderr, "odb [Odiff(%d)] - Error allocating target rowset memory: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto odiff_exit;
}
} else { /* type 'D' thread */
wava = 0002; /* used to check buffer write_available */
wbsy = 0040; /* used to mark buffer write_busy */
rava = 0010; /* used to mark buffer read_available */
epars = etab[eid].parent; /* used to point mutex and cond var */
if ( (etab[zchild].Orowsetl2 = (SQLCHAR *) malloc (rbl)) == (void *)NULL ) {
fprintf(stderr, "odb [Odiff(%d)] - Error allocating target rowset memory2: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto odiff_exit;
}
}
/* Initilize ODBC dump */
if ( fdmp ) {
for ( j = 0 ; j < etab[eid].cmt ; j++ ) {
fprintf(fdmp, "[%d] Column %d: name=%s, type=%d (%s), size=%lu, decimals=%d, nullable=%s\n",
tid, j, (char *)etab[eid].td[j].Oname, (int)etab[eid].td[j].Otype,
expandtype(etab[eid].td[j].Otype), (unsigned long)etab[eid].td[j].Osize,
(int)etab[eid].td[j].Odec, etab[eid].td[j].Onull ? "yes" : "no" ) ;
}
}
/* Set row-wise binding for source */
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_BIND_TYPE,
(SQLPOINTER)(etab[eid].s), 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto odiff_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_ARRAY_SIZE,
(SQLPOINTER)(etab[eid].r), 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto odiff_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROW_STATUS_PTR,
NULL, 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto odiff_exit;
}
if (!SQL_SUCCEEDED(Or=SQLSetStmtAttr(Ostmt, SQL_ATTR_ROWS_FETCHED_PTR,
&Orespl, 0))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto odiff_exit;
}
/* Bind Columns */
for (j = 0; j < etab[eid].cmt; j++) {
if (!SQL_SUCCEEDED(Or=SQLBindCol(Ostmt, (SQLUSMALLINT)j + 1, SQL_C_CHAR,
&etab[eid].Orowsetl[etab[eid].td[j].start], etab[eid].td[j].Osize,
(SQLLEN *)&etab[eid].Orowsetl[etab[eid].td[j].start+etab[eid].td[j].Osize+etab[eid].td[j].pad]))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
goto odiff_exit;
}
}
/* Register Diff Start Time */
gettimeofday(&tve, (void *)NULL);
tinit = 1000*(tve.tv_sec-tvi.tv_sec)+(tve.tv_usec-tvi.tv_usec)/1000;
/* Fetch data */
while ( go &&
SQL_SUCCEEDED(Or = SQLFetchScroll(Ostmt, SQL_FETCH_NEXT, 0))) {
while ( go ) {
MutexLock(&etab[epars].pmutex); /* lock shared mutex */
while ( go ) {
if ( etab[zchild].lstat & wava ) { /* if write available */
etab[zchild].lstat &= ~wava; /* reset write availability */
etab[zchild].lstat |= wbsy; /* set buffer write_busy */
MutexUnlock(&etab[epars].pmutex); /* unlock mutex */
break;
} else { /* if comp not available */
CondWait(&etab[epars].pcvp, &etab[epars].pmutex); /* wait */
}
}
MutexUnlock(&etab[epars].pmutex); /* unlock mutex */
if ( etab[eid].type == 'd' ) { /* if src reader... */
MEMCPY(etab[zchild].Orowsetl, etab[eid].Orowsetl, rbl);
etab[zchild].ar = (size_t)Orespl; /* inform loader about #rec to insert */
} else{ /* if tgt reader ... */
MEMCPY(etab[zchild].Orowsetl2, etab[eid].Orowsetl, rbl);
etab[zchild].rbs = (size_t)Orespl; /* inform loader about #rec to insert */
}
MutexLock(&etab[epars].pmutex); /* lock shared mutex */
etab[zchild].lstat &= ~wbsy; /* unset buffer write_busy */
etab[zchild].lstat |= rava; /* set buffer read_available */
MutexUnlock(&etab[epars].pmutex); /* unlock mutex */
CondWake(&etab[epars].pcvc); /* wake-up loader thread */
break; /* exit this loop and... */
}
if ( etab[eid].mr && (unsigned long) ( etab[zchild].nr + Orespl ) >= etab[eid].mr )
break;
memset ( etab[eid].Orowsetl, 0, rbl );
}
if ( !SQL_SUCCEEDED ( Or ) )
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
MutexLock(&etab[epars].pmutex);
etab[eid].flg |= ( etab[eid].type == 'd' ) ? 02000 : 0200000000 ; /* mark End Of Data */
MutexUnlock(&etab[epars].pmutex);
CondWake(&etab[epars].pcvc);
MutexLock(&etab[epars].pmutex);
while ( go ) { /* Wait child Z-thread */
if ( etab[zchild].lstat )
CondWait(&etab[epars].pcvp, &etab[epars].pmutex);
else
break;
}
MutexUnlock(&etab[epars].pmutex);
telaps -= 1000*tve.tv_sec + tve.tv_usec/1000;
gettimeofday(&tve, (void *)NULL); /* register end time */
telaps += 1000*tve.tv_sec + tve.tv_usec/1000;
/* End thread activities */
if ( etab[eid].type == 'd' ) {
MutexLock( &etab[gpar].gmutex );
if ( etab[gpar].ps ) { /* this is not the last "stream" */
etab[gpar].ps--; /* Decrease d-thread counter */
if ( etab[gpar].ps ) /* this is not the last thread */
goto odiff_exit;
}
if ( !etab[gpar].ps ) { /* this is the last thread */
MutexUnlock( &etab[gpar].gmutex );
for ( j = 0 ; j < no ; j++ ) {
if ( etab[j].type == 'Z' && etab[j].k == (unsigned int)gpar ) {
etab[eid].nr += etab[j].nr; /* records compared */
etab[eid].mr += etab[j].mr; /* records 'I' (no src) */
etab[eid].nrt += etab[j].nrt; /* records 'D' (no tgt) */
etab[eid].nbs += etab[j].nbs; /* records 'C' (same order-by - different content) */
}
}
if ( etab[eid].fo && etab[eid].fo != stdout ) /* close output file */
fclose ( etab[eid].fo );
fprintf(stderr,"[%d] %s Diff statistics:\n", tid, odbid);
fprintf(stderr,"\t[%d] Source: %s\n", gptid, etab[eid].src);
fprintf(stderr,"\t[%d] Target: %s\n", gptid, etab[eid].tgt);
fprintf(stderr,"\t[%d] Quick Diff: %s\n", gptid, ( etab[eid].flg2 & 0010 ) ? "Yes": "No" );
fprintf(stderr,"\t[%d] Record buffer size: %s bytes\n", gptid, strmnum((double)etab[eid].s,num,sizeof(num),0));
fprintf(stderr,"\t[%d] Rowset size: %s\n", gptid, strmnum((double)etab[eid].r,num,sizeof(num),0));
fprintf(stderr,"\t[%d] Rowset buffer size: %s KiB\n", gptid, strmnum((double)rbl/1024.0,num,sizeof(num),2));
seconds = (double)tinit/1000.0 ;
fprintf(stderr,"\t[%d] Pre-diff time: %s (%s s)\n", gptid,
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
seconds = (double)telaps/1000.0 ;
fprintf(stderr,"\t[%d] Diff time: %s (%s s)\n", gptid,
strmnum(seconds,num,sizeof(num),3), strmtime(seconds, tim));
fprintf(stderr,"\t[%d] Total comparisons: %s ", gptid, strmnum((double)etab[eid].nr,num,sizeof(num),0));
fprintf(stderr,"(%s kcomp/s)\n", strmnum((double)etab[eid].nr*1.0/telaps,num,sizeof(num),3));
if ( etab[eid].flg & 02000000000 ) /* print 'I' rows */
fprintf(stderr,"\t[%d] Total new records on tgt (I): %s\n", gptid, strmnum((double)etab[eid].mr,num,sizeof(num),0));
if ( etab[eid].flg & 04000000000 ) /* print 'D' rows */
fprintf(stderr,"\t[%d] Total missing records on tgt (D): %s\n", gptid, strmnum((double)etab[eid].nrt,num,sizeof(num),0));
if ( etab[eid].flg & 010000000000 ) /* print 'C' rows */
fprintf(stderr,"\t[%d] Total records modified (C): %s\n", gptid, strmnum((double)etab[eid].nbs,num,sizeof(num),0));
if ( j > 1 ) { /* more than one extract stream... */
for ( j = 0 ; j < no ; j++ ) {
if ( etab[j].type == 'Z' && etab[j].k == (unsigned int)gpar ) {
fprintf(stderr,"\t\t[%d:%d>%d] %s records compared: ",
j-2, j-1, j, strmnum((double)etab[j].nr,num,sizeof(num),0));
if ( etab[eid].flg & 02000000000 ) /* print 'I' rows */
fprintf(stderr,"I=%s ", strmnum((double)etab[j].mr,num,sizeof(num),0));
if ( etab[eid].flg & 04000000000 ) /* print 'D' rows */
fprintf(stderr,"D=%s ", strmnum((double)etab[j].nrt,num,sizeof(num),0));
if ( etab[eid].flg & 010000000000 ) /* print 'C' rows */
fprintf(stderr,"C=%s ", strmnum((double)etab[j].nbs,num,sizeof(num),0));
fputc('\n', stderr);
}
}
}
if ( etab[eid].td ) { /* last thread frees table desc structure memory */
for ( j = 0 ; j < etab[eid].cmt ; j++ )
free ( etab[eid].td[j].Oname );
free(etab[eid].td);
}
}
} else { /* this is a D-thread */
if ( etab[eid].post ) { /* SQL-POST cmd to run */
MutexLock( &etab[gpar+1].pmutex );
if ( etab[gpar+1].ps ) /* this is not the last D-thread */
etab[gpar+1].ps--; /* Decrease D-thread counter */
if ( !etab[gpar+1].ps ) { /* last D-thread */
MutexUnlock( &etab[gpar+1].pmutex );
(void)SQLFreeStmt(Ostmt, SQL_CLOSE); /* Close cursor */
(void)SQLFreeStmt(Ostmt, SQL_UNBIND); /* Unbind params */
var_set ( &thps[tid].tva, VTYPE_I, "tgt", etab[eid].tgt );
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
if ( etab[eid].pre[0] == '@' ) /* run a sql script */
(void)runsql(tid, eid, 0, (etab[eid].post + 1 ));
else /* Run single SQL command */
(void)Oexec(tid, eid, 0, 0, (SQLCHAR *)etab[eid].post, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
} else {
MutexUnlock( &etab[gpar+1].pmutex );
}
}
}
/* Clean & exit */
odiff_exit:
MutexUnlock( &etab[gpar].gmutex );
etab[eid].flg |= etab[eid].type == 'd' ? 02000 : 0200000000 ; /* mark End Of Data */
CondWake(&etab[epars].pcvc);
(void)SQLFreeStmt(Ostmt, SQL_CLOSE);
(void)SQLFreeStmt(Ostmt, SQL_UNBIND);
if (Ocmd)
free ( Ocmd );
if ( etab[eid].Orowsetl )
free (etab[eid].Orowsetl);
}
/* Ocompare:
* Compares two buffers received from src/tgt
*
* eid (I): etab entry ID to run
*
* return: void
*/
static void Ocompare(int eid)
{
unsigned int i = 0; /* loop variable */
int epars = etab[eid].parent; /* Parent 'd' (src) etab id shortcut */
int epart = etab[eid].child; /* Parent 'D' (tgt) etab id shortcut */
int ret = 0; /* memcmp ret value */
unsigned int csrn = 0; /* Current Source Record Number */
unsigned int ctrn = 0; /* Current Target Record Number */
unsigned int hsrn = 0; /* High Target Source Number (last row in src buffer)*/
unsigned int htrn = 0; /* High Target Record Number (last row in tgt buffer)*/
unsigned char cont = 0003 ; /* Main loop continue flag: 0001=src_cont 0002=tgt_cont */
unsigned char *ps = 0 ; /* Source buffer pointer */
unsigned char *pt = 0 ; /* Target buffer pointer */
char num[32]; /* used to build formatted numbers */
SQLLEN Old = 0; /* src-tgt length indicator difference */
etab[eid].mr = 0;
while ( go ) {
if ( ( cont & 0001 ) && ( csrn == hsrn ) ) {/* more data from src is needed and available */
MutexLock(&etab[epars].pmutex);
while ( go ) {
if ( etab[eid].lstat & 0004 ) { /* src buffer available */
etab[eid].lstat &= ~0004; /* unset src buffer ready status */
etab[eid].lstat |= 0100; /* set src buffer read busy */
break;
} else if ( etab[epars].flg & 02000 ){/* src EOD */
cont &= ~0001; /* no more data available from source */
break;
} else {
CondWait(&etab[epars].pcvc, &etab[epars].pmutex);
}
}
MutexUnlock(&etab[epars].pmutex);
hsrn += (unsigned int)etab[eid].ar ; /* set high source record number */
ps = (unsigned char *)etab[eid].Orowsetl; /* set src pointer */
}
if ( ( cont & 0002 ) && ( ctrn == htrn ) ) {/* more data from tgt is needed and available */
MutexLock(&etab[epars].pmutex);
while ( go ) {
if ( etab[eid].lstat & 0010 ) { /* tgt buffer available */
etab[eid].lstat &= ~0010; /* reset tgt buffer ready status */
etab[eid].lstat |= 0200; /* set src buffer read busy */
break;
} else if ( etab[epart].flg & 0200000000 ){/* tgt EOD */
cont &= ~0002; /* no more data available from target */
break;
} else {
CondWait(&etab[epars].pcvc, &etab[epars].pmutex);
}
}
MutexUnlock(&etab[epars].pmutex);
htrn += (unsigned int)etab[eid].rbs ; /* set high source record number */
pt = (unsigned char *)etab[eid].Orowsetl2; /* set tgt pointer */
}
if ( !cont ) { /* neither src nor tgt data available */
break; /* exit loop */
} else if ( ! ( cont & 0001 ) ) { /* no more data on src... */
for ( ; ctrn < htrn ; ctrn++ ) { /* all tgt rows were inserted */
if ( etab[eid].flg & 02000000000 ) /* print 'I' rows */
prec('I', pt, eid);
etab[eid].mr++;
pt += etab[eid].s;
}
hsrn = csrn ;
} else if ( ! ( cont & 0002 ) ) { /* no more data on tgt... */
for ( ; csrn < hsrn ; csrn++ ) { /* all src rows were deleted */
if ( etab[eid].flg & 04000000000 ) /* print 'D' rows */
prec('D', ps, eid);
etab[eid].nrt++;
ps += etab[eid].s;
}
htrn = ctrn ;
} else { /* data available on both src ad tgt */
while ( ( csrn < hsrn ) && ( ctrn < htrn ) ) { /* comparison loop */
etab[eid].nr++;
/* First compare via memcmp() the initial 'key' rowset component made
of both real data in SQL_C_CHAR format and length indicators */
if ( ( ret = memcmp ( ps, pt, etab[eid].r ) ) == 0 ) {
/* If the src/tgt key rowset component are identical (ret=0) and we have to
look for 'C'hanged rows, compare the remaining part of rowset via memcmp() */
if ( etab[eid].flg & 010000000000 ) { /* print 'C' rows */
if ( memcmp ( ps + etab[eid].r, pt + etab[eid].r, etab[eid].s - etab[eid].r ) ) {
prec('S', ps, eid);
prec('T', pt, eid);
etab[eid].nbs++;
}
}
csrn++;
ctrn++;
pt += etab[eid].s;
ps += etab[eid].s;
} else {
/* We're here if the src/tgt 'key' rowset component are different. In order to
understand if this is a new or deleted record we will:
- first compare the length indicators for the key columns
- if there are no differences we will use the "alphabetic' comparison
returned by the first memcmp() here above */
for ( i = 0 ; i < etab[eid].pc ; i++ ) {
Old = *((SQLLEN *)(ps + etab[eid].td[i].start + etab[eid].td[i].Osize + etab[eid].td[i].pad)) -
*((SQLLEN *)(pt + etab[eid].td[i].start + etab[eid].td[i].Osize + etab[eid].td[i].pad)) ;
if ( Old ) { /* src key component longer than tgt */
ret = (int)Old ;
break;
}
}
if ( ret > 0 ) {
if ( etab[eid].flg & 02000000000 )
prec('I', pt, eid);
etab[eid].mr++;
ctrn++;
pt += etab[eid].s;
} else if ( ret < 0 ) {
if ( etab[eid].flg & 04000000000 )
prec('D', ps, eid);
etab[eid].nrt++;
csrn++;
ps += etab[eid].s;
}
}
}
if ( etab[eid].flg & 0400 ) /* print mark */
fprintf(stderr, "[%d] %s comparisons (%lu I, %lu D, %lu C)\n",
etab[eid].id, strmnum((double)etab[eid].nr,num,sizeof(num),0),
etab[eid].mr, etab[eid].nrt, etab[eid].nbs);
}
if ( csrn == hsrn ) { /* mark src buffer write ready */
MutexLock(&etab[epars].pmutex); /* lock mutex */
etab[eid].lstat &= ~0100; /* unset src buffer read busy */
etab[eid].lstat |= 0001; /* set src buffer write available */
MutexUnlock(&etab[epars].pmutex);/* unlock mutex */
CondWakeAll(&etab[epars].pcvp); /* wake up waiting thread */
}
if ( ctrn == htrn ) { /* mark tgt buffer write ready */
MutexLock(&etab[epars].pmutex); /* lock mutex */
etab[eid].lstat &= ~0200; /* unset tgt buffer read busy */
etab[eid].lstat |= 0002; /* set tgt buffer write available */
MutexUnlock(&etab[epars].pmutex);/* unlock mutex */
CondWakeAll(&etab[epars].pcvp); /* wake up waiting thread */
}
}
MutexLock(&etab[epars].pmutex); /* lock mutex */
etab[eid].lstat = 0; /* set lstat to zero */
MutexUnlock(&etab[epars].pmutex);/* unlock mutex */
CondWakeAll(&etab[epars].pcvp); /* wake up waiting thread */
/* clean and exit */
if ( etab[eid].Orowsetl )
free ( etab[eid].Orowsetl );
if ( etab[eid].Orowsetl2 )
free ( etab[eid].Orowsetl2 );
}
/* decode_buf
* decode contents in buf to string and save in buf
*
* ibuf: input buffer
* type: input buffer C type
* obuf: output buffer
* size: output buffer dispaly size
* return obuf
*/
static char* decode_buf(char *ibuf, SQLSMALLINT type, char *obuf, SQLLEN size) {
switch (type)
{
case SQL_C_SHORT:
case SQL_C_SSHORT:
snprintf(obuf, size, "%hd", *((unsigned short*)ibuf));
return obuf;
case SQL_C_USHORT:
snprintf(obuf, size, "%hu", *((unsigned short*)ibuf));
return obuf;
case SQL_C_LONG:
snprintf(obuf, size, "%ld", *((long*)ibuf));
return obuf;
case SQL_C_ULONG:
snprintf(obuf, size, "%lu", *((unsigned long*)ibuf));
return obuf;
case SQL_C_FLOAT:
snprintf(obuf, size, "%f", *((float*)ibuf));
return obuf;
case SQL_C_DOUBLE:
snprintf(obuf, size, "%lf", *((double*)ibuf));
return obuf;
case SQL_C_STINYINT:
snprintf(obuf, size, "%hhd", *((char*)ibuf));
return obuf;
case SQL_C_UTINYINT:
snprintf(obuf, size, "%hhu", *((unsigned char*)ibuf));
return obuf;
case SQL_C_SBIGINT:
snprintf(obuf, size, "%lld", *((long long*)ibuf));
return obuf;
case SQL_C_UBIGINT:
snprintf(obuf, size, "%llu", *((long long*)ibuf));
return obuf;
default:
strncpy(obuf, ibuf, size);
return obuf;
}
}
/* prec:
* print rows
*
* type: 'L' (from loads) and 'I', 'D', 'S' or 'T' (from diff)
* podbc: pointer to the ODBC (row-wise bound) buffer
* eid: etab entry id
*
* return: void
*/
static void prec(char type, unsigned char *podbc, int eid)
{
unsigned int i = 0; /* loop variable */
int gpar = etab[eid].k; /* GrandParent 'd' (src) etab id shortcut */
unsigned int nfields = (unsigned int)etab[eid].cmt; /* Initialize number of fields */
unsigned char *p = 0; /* pointer to string to print */
SQLLEN Ofl = 0; /* Field Length */
SQLLEN bufl = 20;
SQLCHAR *tbuf = (SQLCHAR*)malloc(bufl);
if (!tbuf) {
fprintf(stderr, "prec(%d): alloc memory for tbuf failed\n", __LINE__);
}
if ( type == 'L' || type == 'C' ) {
gpar = etab[eid].parent;
/* nfields = (unsigned int)etab[gpar].sp ; */
/* fix jira 2034, write to bad_record if load with parameter 'bad', etab[gpar].sp equals 0 always */
nfields = (unsigned int)etab[eid].sp ;
}
MutexLock(&etab[gpar].pmutex); /* lock mutex */
if ( !(etab[gpar].flg2 & 0200) ) {
if ( type != 'L' && type != 'C' ) {
fputs(islower(etab[eid].td[0].Oname[0]) ? "dtype" : "DTYPE", etab[eid].fo);
for ( i = 0 ; i < nfields ; i++ ) {
fputc(etab[eid].fs, etab[eid].fo);
fputs((char *)etab[eid].td[i].Oname, etab[eid].fo);
}
fputc(etab[eid].rs, etab[eid].fo);
etab[gpar].flg2 |= 0200 ;
}
}
if ( type == 'L' || type == 'C' ) {
if ( etab[eid].fo == stderr ) {
fputs(">>> ", stderr);
}
} else {
fputc(type, etab[eid].fo);
fputc(etab[eid].fs, etab[eid].fo);
}
for ( i = 0 ; i < nfields ; i++ ) {
SQLULEN cdatal;
if (type == 'C') {
cdatal = etab[eid].td[i].Ocdatabufl;
}
else {
cdatal = etab[eid].td[i].Osize;
}
if ( i )
fputc(etab[eid].fs, etab[eid].fo);
if ( *((char *)podbc + etab[eid].td[i].start + cdatal + etab[eid].td[i].pad ) == SQL_NULL_DATA ) {
fputs(etab[eid].ns ? etab[eid].ns : "", etab[eid].fo);
} else if ( *(SQLLEN *)(podbc + etab[eid].td[i].start + cdatal + etab[eid].td[i].pad ) == 0 ) {
fputs(etab[eid].es ? etab[eid].es : "", etab[eid].fo);
} else {
p = podbc + etab[eid].td[i].start;
Ofl = *(SQLLEN *)(p + cdatal + etab[eid].td[i].pad);
if (type == 'C') {
if (bufl < etab[eid].td[i].OdisplaySize) {
bufl = etab[eid].td[i].OdisplaySize + 1;
tbuf = (SQLCHAR*)realloc(tbuf, bufl);
if (!tbuf) {
fprintf(stderr, "prec(%d): alloc memory for tbuf failed\n", __LINE__);
}
}
Ofl = etab[eid].td[i].OdisplaySize;
p = (unsigned char*)decode_buf((char*)p, (SQLSMALLINT)etab[eid].td[i].Octype, (char*)tbuf, (SQLLEN)Ofl);
}
for ( ; *p && Ofl ; p++, Ofl-- )
fputc( *p , etab[eid].fo );
}
}
fputc(etab[eid].rs, etab[eid].fo);
MutexUnlock(&etab[gpar].pmutex); /* unlock mutex */
}
/* strmcpy:
* copies source "src" into target "tgt" up to "len" chars in "tgt"
*
* tgt: pointer to the target string;
* src: pointer to the source string;
* len: max number of chars in t;
*
* return: void
*/
static void strmcpy(char *tgt, char *src, size_t len)
{
char *t = tgt;
char *s = src;
if ( s )
while ( len-- && ( *t++ = *s++ ) );
*t = '\0';
}
/* strmcat:
* concat source "src" to target "tgt" up to "len" chars in "tgt"
*
* tgt: pointer to the target string;
* src: pointer to the source string;
* len: max number of chars in tgt;
* cas: 0 = don't convert case, 1 = convert to upper cases, 2 convert to lower cases
*
* return: number of concatenated chars
*/
static unsigned int strmcat(char *tgt, char *src, size_t len, char cas)
{
char *t = tgt;
char *s = src;
unsigned int n = 0;
if ( s ) {
while ( *t && len-- )
t++;
switch ( cas ) {
case 1:
while ( len-- && ( *t++ = toupper((unsigned char)*s++ ) ) )
n++;
break;
case 2:
while ( len-- && ( *t++ = tolower((unsigned char)*s++ ) ) )
n++;
break;
default:
while ( len-- && ( *t++ = *s++ ) )
n++;
break;
}
}
*t = '\0';
return ( n );
}
/* strmicmp:
* compare source and target string ignoring case up to len chars
*
* tgt: pointer to the target string;
* src: pointer to the source string;
* len: max number of chars to compare: O means the whole string
*
* return: 0 if comp is ok, 1 otherwise
*/
static unsigned int strmicmp(char *tgt, char *src, size_t len)
{
unsigned char *t = (unsigned char *) tgt;
unsigned char *s = (unsigned char *) src;
do {
if ( toupper(*t) != toupper(*s) )
return (1);
} while ( --len && *s++ && *t++ );
return (0);
}
/* strmprint:
* return a string in input excluding portion included in ^A - ^A
* and non printable characters. Max length is 128 chars.
* NOTE: this function is not thread safe and should be used only for
* SQL Intepreter
*
* s(I): pointer to the string to b converted
*
* return: converted string
*/
static char *strmprint(char *s)
{
static char str[128];
char *t = &str[0];
register unsigned int c = f = 1;
while ( *s ) {
if ( *s == 1 ) /* ^A in input */
f = f ? 0 : 1;
else if ( isprint((unsigned char)*s) && f && c++ < sizeof(str))
*t++ = *s;
s++;
}
*t = '\0';
return(&str[0]);
}
/* strmnum: convert a double to a formatted string with decimal/thousands separators
* d = number to convert
* s = pointer string hosting the formatted number
* l = length of the hosting string
* dd = decimal digits
* ksep = thousand separator (global variable)
* dsep = decimal separator (global variable)
*
* return pointer to the formatted string
*/
static char *strmnum(double d, char *s, size_t l, unsigned int dd)
{
unsigned int i=0; /* loop variable */
double round = 5; /* add .5 after last decimal digit */
char *p = &s[l-dd-1];
unsigned long n = 0;
for ( i = 0 ; i < dd + 1 ; i++ )
round /= 10;
d += round ;
n = (unsigned long) d;
if ( isnan ( d ) )
return ( "NaN" );
if ( isinf ( d ) )
return ( "Inf" );
s[l-1] = '\0';
if ( dd )
for ( *--p = dsep, d = ( d - (double) n ) * 10 ; dd ; dd--, d *= 10 )
s[l-dd-1] = '0' + (unsigned int)d % 10;
i = 0 ;
do {
*--p = '0' + n % 10 ;
n /= 10;
if ( n && !(++i%3) )
*--p = ksep;
} while ( n );
return ( p );
}
/* splitcso:
* split input string using '.' a string separator
*
* s: string to split
* p: array of pointers to cat/sch/obj (to be defined as char *p[3];
* p[0] = catalog name or:
* NULL if 's' is NULL
* NULL if 's' contains 1 element and 'a' is true
* NULL if 's' contains 2 elements and 'no schema' is false and 'a' is true
* p[1] = schema name or:
* NULL if 's' is NULL
* NULL if 's' contains 1 element
* NULL if 's' contains 2 elements and no schema is true
* p[2] = object name or:
* NULL if 's' is NULL
* NULL if 's' contains 2 elements and 'no schema' is false and 'a' is false
* a: align elements to left (0) or right (1) when 's' has less than 3 elements:
* (a=0) left <-- catalog.schema.object --> right (a=1)
* p[0] p[1] p]2]
* return: void
*/
static void splitcso(char *s, SQLCHAR **p, int a)
{
int i=0;
unsigned char *st = (unsigned char *)s;
char q = 0; /* Quoted text flag. Quoted text is not UPPERCASEd */
p[0] = p[1] = p[2] = (unsigned char *)NULL;
if ( !st || !*st )
return;
do {
if ( *st == '\"' )
q = q ? 0 : 1 ;
if ( !q && !(f & 010000000000))
*st = toupper(*st);
} while ( *st++ );
st = (unsigned char *)s;
if ( *st )
p[i++] = st;
do {
if ( *st == '.' ) {
*st = '\0';
p[i++] = ++st;
}
} while ( *st++ && i < 3 );
switch (i) {
case 3:
if ( f & 0100000000 )
p[0] = (unsigned char *)NULL;
break;
case 2:
if ( f & 020000000 ) {
p[2] = p[1];
p[1] = (unsigned char *)NULL;
} else if ( a || ( f & 0100000000 ) ) {
p[2] = p[1];
p[1] = p[0];
p[0] = (unsigned char *)NULL;
}
break;
case 1:
if ( a ) {
p[2] = p[0];
p[0] = (unsigned char *)NULL;
}
break;
}
}
/* var_set:
* set a variable to a given array;
*
* chain: pointers to vars chain where the new variable should be added
* type: var type: VTYPE_A, VTYPE_U or VTYPE_I
* next: name of the variable to be added
* value: value of the variable to be added
*
* return:
* 0 = success
* -1 = error allocating new struct memory
* -2 = error allocating memory for var value
*/
static int var_set(struct ovar **chain, char type, char *name, char *value)
{
struct ovar *v,
*p,
*n;
size_t len = 0;
if ( ( v = var_idx ( chain, type, name ) ) ) {
if ( ( len = strlen ( value ) ) > v->vlen ) {
if ( ( v->value = realloc(v->value, len)) == (void *)NULL ) {
fprintf(stderr, "odb [var_set(%d)] - Error re-allocating value memory for var %s: [%d] %s\n",
__LINE__, name, errno, strerror(errno));
return ( -2 );
}
}
strmcpy(v->value, value, len);
v->vlen = len;
} else {
if ( (v = calloc (1, sizeof(struct ovar))) == (void *)NULL ) {
fprintf(stderr, "odb [var_set(%d)] - Error allocating struct var memory for %s: [%d] %s\n",
__LINE__, name, errno, strerror(errno));
return ( -1 );
}
len = strlen ( value ) ;
if ( (v->value = calloc (1, len + 1)) == (void *)NULL ) {
fprintf(stderr, "odb [var_set(%d)] - Error allocating value memory for var %s: [%d] %s\n",
__LINE__, name, errno, strerror(errno));
return ( -2 );
}
for ( p = n = *chain ; n ; p = n, n = n->next ) ;
n = v;
n->type = type;
strmcpy(n->name, name, sizeof(n->name));
strmcpy(n->value, value, len);
n->vlen = len;
n->nlen = strlen(name);
n->next = (struct ovar *) NULL;
n->prev = p;
if ( *chain )
p->next = n;
else
*chain = n;
}
return ( 0 );
}
/* var_idx:
* return a pointer to a "struct var" if the given variable exists or NULL
* if the variable does not exists in the given chain
*
* chain: pointers to vars chain where the new variable should be added
* type: var type: VTYPE_A, VTYPE_U, VTYPE_I or VTYPE_UI
* value: value of the variable to be added
*
* return:
* - pointer to the variable value
* - NULL if the given variable does not exists
*/
static struct ovar *var_idx(struct ovar **chain, char type, char *name)
{
struct ovar *p;
for ( p = *chain ; p ; p = p->next )
if ( p->type & type && !strcmp( name, p->name ) )
return ( p );
return ((struct ovar *)NULL);
}
/* var_del:
* delete a given entry from a var chain
*
* chain: pointers to vars chain where the new variable should be added
* type: var type: VTYPE_A, VTYPE_U, VTYPE_I or VTYPE_UI
* value: value of the variable to be added
*
* return:
* 0 = success
* -1 = entry not found
*/
static int var_del(struct ovar **chain, char type, char *name)
{
struct ovar *p;
for ( p = *chain ; p ; p = p->next )
if ( p->type == type && !strcmp( name, p->name ) ) {
if ( p->next )
p->next->prev = p->prev;
if ( p->prev )
p->prev->next = p->next;
if ( type != VTYPE_I )
free ( p->value );
free ( p );
return ( 0 );
}
return ( -1 );
}
/* var_exp:
* expand one or more variables in a string:
* - variables identified by '&' will be expanded with 'u'ser defined parameters
* - variables identified by '$' will be expanded with environment variables
* - to avoid expansion '&' and '$' have to be escaped by the "ec" character
*
* str: string to be expanded
* lp: pointer to a size_t var containing str length. When NULL string will be
* written to stderr.
* chain: chain of variables
*
* return:
* pointer to the expanded str
* NULL error expanding str
*/
static char *var_exp(char *str, size_t *lp, struct ovar **chain)
{
int i = 0, /* loop variable */
k = 0,
j = 0;
char vn[MAX_VNLEN];
char flag=0;
char *es = 0;
char *new = 0;
char *n = 0 ; /* temp realloc var */
size_t len = 0;
struct ovar *v=0;
if ( lp && (new = calloc ( 1, *lp ) ) == (void *)NULL ) {
fprintf(stderr, "odb [var_exp(%d)] - Error allocating new str in var_exp: [%d] %s\n",
__LINE__, errno, strerror(errno));
return ( (char *) NULL);
}
for ( i = j = 0 ; str[i] ; i++, j++ ) {
if ( ( str[i] == '&' || str[i] == '$' ) &&
( ( i && str[i-1] != ec ) || !i ) ) {
flag = str[i++];
for ( k = 0 ; str[i] && ( str[i] == '_' || isalnum((unsigned char)str[i]) ) ; i++, k++ )
vn[k] = str[i];
vn[k] = '\0';
if ( lp ) {
if ( flag == '&' && ( v = var_idx ( chain, VTYPE_UI, vn ) ) )
len = v->vlen;
else if ( flag == '$' && ( es = getenv ( vn ) ) )
len = strlen ( es );
if ( *lp < ( j + len + 1 ) ) {
*lp += CMD_CHUNK;
n = new ;
if ( ( n = realloc ( n, *lp ) ) == (void *)NULL ) {
fprintf(stderr, "odb [var_exp(%d)] - Error re-allocating memory for new str: [%d] %s\n",
__LINE__, errno, strerror(errno));
return ( (char *) NULL );
}
new = n ;
}
if ( flag == '&' && v ) {
j += strmcat ( new, v->value , *lp, 0);
} else if ( flag == '$' && es ) {
j += strmcat ( new, es , *lp, 0);
} else {
while ( j < i ) {
new[j] = str[j];
j++;
}
}
} else {
if ( flag == '&' && ( v = var_idx ( chain, VTYPE_UI, vn ) ) )
fputs (v->value, stderr);
else if ( flag == '$' && ( es = getenv ( vn ) ) )
fputs (es, stderr);
else
while ( j < i ) {
fputc(str[j++], stderr);
}
}
}
if ( lp )
new[j] = str[i];
else{
fputc (str[i], stderr);
}
if ( !str[i] )
i--;
}
if ( lp ) {
free ( str );
return ( new );
} else {
fputc ('\n', stderr);
return ( (char *)NULL );
}
}
/* dconv:
* convert input sring into an ISO8601 date/time/timestamp according to the
* format_string
*
* fmt: input format string made of the following characters:
* b = abbreviated month name
* B = full month name
* d = day of the month
* H = hour (24 hour format)
* m = month number
* M = minute
* S = second
* D = decimals
* y = year (four digits)
* . = ignore single char
* _ = ignore up to the next digit
* str: string to convert
* out: pointer to string where to write the output (*)
* t: conversion type (0=date, 1=timestamp(0), 2=time(0)
*
* (*) dconv can safely use in output the input string (if big enough)
* return:
* >1 = output string length
* 0 = conversion error
* 1 = invalid format string
*/
static unsigned int dconv ( char *fmt, char *str, char *out, size_t len, char t )
{
unsigned int year = 0, /* year */
mon = 0, /* month */
day = 0, /* day */
hour = 0, /* hours */
min = 0, /* minutes */
sec = 0, /* seconds */
dec = 0, /* decimals */
df = 0, /* decimal flag */
i = 0; /* loop variable */
char *p = str,
*ft = fmt;
char buf[10]; /* buffer */
static char *amn[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ;
static char *fmn[] = { "January", "February", "March", "April",
"May", "June", "July", "August", "September",
"October", "November", "December" } ;
while ( *p && *ft ) {
switch ( *ft++ ) {
case '.': /* skip single char */
p++;
break;
case '_': /* skip up to the next digit */
while ( *p < '0' || *p > '9' )
p++;
break;
case 'B':
for ( i = 0 ; ( buf[i] = *p++ ) && *p != ' ' && *p != '\t'; i++ );
buf[i] = '\0';
for ( i = 0 ; i < 12 && strcmp(buf, fmn[i]) ; i++ )
if ( i == 12 )
return (0);
else
mon = i+1;
break;
case 'b':
for ( i = 0 ; i < 3 && ( buf[i] = *p++ ) ; i++ );
buf[i] = '\0';
for ( i = 0 ; i < 12 && strcmp(buf, amn[i]) ; i++ );
if ( i == 12 )
return (0);
else
mon = i+1;
break;
case 'd':
for ( i = 0 ; i < 2 && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
buf[i] = '\0';
day = (unsigned int)strtol(buf, (char **)NULL, 10);
if ( !i || day > 31 )
return (0);
break;
case 'D':
df = 1;
for ( i = 0 ; i < (unsigned int)(*ft + 48) && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
ft++;
buf[i] = '\0';
dec = (unsigned int)strtol(buf, (char **)NULL, 10);
break;
case 'H':
for ( i = 0 ; i < 2 && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
buf[i] = '\0';
hour = (unsigned int)strtol(buf, (char **)NULL, 10);
if ( !i || hour > 23 )
return (0);
break;
case 'm':
for ( i = 0 ; i < 2 && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
buf[i] = '\0';
mon = (unsigned int)strtol(buf, (char **)NULL, 10);
if ( !i || mon > 12 )
return (0);
break;
case 'M':
for ( i = 0 ; i < 2 && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
buf[i] = '\0';
min = (unsigned int)strtol(buf, (char **)NULL, 10);
if ( !i || min > 59 )
return (0);
break;
case 'S':
for ( i = 0 ; i < 2 && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
buf[i] = '\0';
sec = (unsigned int)strtol(buf, (char **)NULL, 10);
if ( !i || sec > 59 )
return (0);
break;
case 'y':
for ( i = 0 ; i < 4 && *p >= '0' && *p <= '9' ; i++ )
buf[i] = *p++;
buf[i] = '\0';
year = (unsigned int)strtol(buf, (char **)NULL, 10);
if ( i < 4 )
return (0);
break;
default:
return ( 1 );
}
}
if ( df ) {
switch ( t ) {
case 0: /* return a date */
return ( snprintf (out, len, "%04u-%02u-%02u.%u", year, mon, day, dec ) );
case 1: /* return a time */
return ( snprintf (out, len, "%02u:%02u:%02u.%u", hour, min, sec, dec ) );
case 2: /* return a timestamp */
return ( snprintf (out, len, "%04u-%02u-%02u %02u:%02u:%02u.%u",
year, mon, day, hour, min, sec, dec ) );
}
} else {
switch ( t ) {
case 0: /* return a date */
return ( snprintf (out, len, "%04u-%02u-%02u", year, mon, day ) );
case 1: /* return a time */
return ( snprintf (out, len, "%02u:%02u:%02u", hour, min, sec ) );
case 2: /* return a timestamp */
return ( snprintf (out, len, "%04u-%02u-%02u %02u:%02u:%02u",
year, mon, day, hour, min, sec ) );
}
}
return ( 0 ) ;
}
/* tokenize:
* given a string in input returns an array of pointers to the substring
* separated by whitespaces (blank, tab, new lines). It manages quoted
* text and escape characters. The substrings are stored exactly in the
* same memory as the input string so there's no need for new memory.
*
* Exambles:
* Input >one two three< Ouput >one< >two< >three<
* Input >one "two three"< Ouput >one< >two three<
* Input >one 'two three'< Ouput >one< >two three<
* Input >one "two 'three"< Ouput >one< >two 'three<
* Input >one "two \"three"< Ouput >one< >two "three<
*
* s: string to tokenize
* l: length of string s
* ss: array of char* containing the pointers to the tokenized substrings
*
* return:
* number of tokenized substrings
*/
static int tokenize(char *s, int l, char *ss[])
{
int i,
j,
n = 0;
char qs = 0, /* single quote flag */
qd = 0; /* double quote flag */
for ( i = 0 ; i < l ; i++ ) {
switch ( s[i] ) {
case ' ': /* space */
case '\t': /* tab */
case '\n': /* tab */
if ( !qs && !qd )
s[i] = '\0';
break;
case '"': /* double quote */
if ( i && !qs && s[i-1] != ec ) {
qd = qd ? 0 : 1;
s[i]= '\0';
}
break;
case '\'': /* single quote */
if ( i && !qd && s[i-1] != ec ) {
qs = qs ? 0 : 1;
s[i]= '\0';
}
break;
default: /* any other char */
if ( s[i] == ec && s[i+1] != ec ) {
for ( j = i, l-- ; j < l; j++) {
s[j] = s[j+1];
}
s[l] = '\0';
}
if ( !i || !s[i-1] )
ss[n++] = &s[i];
if ( n == MAX_ARGS )
return ( n );
}
}
return ( n );
}
/* bcs:
* Build connection string for SQLDriverConnect
*
* Ocs: pointer to the connection string to fill
* csl: connection string length
* Ou: User Name
* Op Password
* Od: (local) DSN
* ca: extra connection attributes
* ctn: if >0 will transform <dsn> into <dsn><number>
*
* return:
* void
*/
static void bcs(SQLCHAR *Ocs, size_t csl, SQLCHAR *Ou, SQLCHAR *Op, SQLCHAR *Od, char *ca, int ctn)
{
char buff[8];
Ocs[0] = '\0';
if ( ca ) {
(void) strmcat((char *)Ocs, ca, csl, 0);
}
if ( Od[0] ) {
if (Ocs[0])
(void) strmcat((char *)Ocs, ";", csl, 0);
(void) strmcat((char *)Ocs, "DSN=", csl, 0);
(void) strmcat((char *)Ocs, (char *)Od, csl, 0);
if ( ctn && ndsn ) {
if ( f & 02000000000 )
snprintf(buff, sizeof(buff), "%d", 1 + ( ctn - 1 ) * ndsn / no );
else
snprintf(buff, sizeof(buff), "%d", ( ctn % ndsn ) ? ctn % ndsn : ndsn );
(void) strmcat((char *)Ocs, buff, csl, 0);
}
}
if ( Ou[0] ) {
if (Ocs[0])
(void) strmcat((char *)Ocs, ";", csl, 0);
(void) strmcat((char *)Ocs, "UID=", csl, 0);
(void) strmcat((char *)Ocs, (char *)Ou, csl, 0);
}
if ( Op[0] ) {
if (Ocs[0])
(void) strmcat((char *)Ocs, ";", csl, 0);
(void) strmcat((char *)Ocs, "PWD=", csl, 0);
(void) strmcat((char *)Ocs, (char *)Op, csl, 0);
}
return;
}
/* mfprintf:
* Multiple fprintf: write on two file pointers
*
* fl1: file pointer to the first output file
* fl2: file pointer to the second output file
* fmt: vfprintf() format string
*
* return:
* the number of written characters on fl1/fl2 or -1 if they differ
*/
static int mfprintf( FILE *fl1, FILE *fl2, char *fmt, ... )
{
int nc1 = 0,
nc2 = 0;
va_list al;
va_start ( al, fmt );
if ( fl1 )
nc2 = nc1 = vfprintf ( fl1, fmt, al );
va_start ( al, fmt );
if ( fl2 ) {
nc2 = vfprintf ( fl2, fmt, al );
fflush ( fl2 );
}
va_end ( al );
return ( nc1 == nc2 ? nc1 : -1 );
}
/* mfputc:
* Multiple fputc: write on two file pointers
*
* fl1: file pointer to the first output file
* fl2: file pointer to the second output file
* ch: char to write
*
* return:
* the character written on fl1/fl2 or -1 if they differ
*/
static int mfputc( FILE *fl1, FILE *fl2, int ch )
{
int nc1 = 0,
nc2 = 0;
if ( fl1 )
nc1 = fputc ( ch, fl1 );
if ( fl2 )
nc2 = fputc ( ch, fl2 );
return ( nc1 == nc2 ? nc1 : -1 );
}
/* checkdb:
* check target database returns database type and fills:
*
* eid: etab idx
* Ocn: connection handle pointer
* c: pointer to string used to return initial catalog if not NULL
* s: pointer to string used to return the initial schema if not NULL
*
* return:
* dbt: database type: 0=unknown/generic, 1=PostgreSQL, 2=Vertica,
* 3=Microsoft SQL Server, 4=Teradata, 5=NonStop SQL/MX,
* 6=Oracle, 7=MySQL, 8=Trafodion, 255=Error
*/
static unsigned int checkdb(int eid, SQLHDBC *Ocn, char *c, char *s)
{
char *p = 0; /* strstr output pointer */
unsigned int i = 0; /* loop variable */
unsigned int dbt = 0; /* returned Database ID */
SQLHSTMT Ostmt=0; /* ODBC Statement handle */
int tid = eid < 0 ? -1 : etab[eid].id; /* Thread ID */
SQLCHAR Obuff[128]; /* ODBC buffer */
SQLINTEGER Obatch = 0; /* ODBC to get SQL_PARAM_ARRAY_ROW_COUNTS value */
if ( !SQL_SUCCEEDED(Oret=SQLAllocHandle(SQL_HANDLE_STMT, *Ocn, &Ostmt))){
Oerr(eid, tid, __LINE__, *Ocn, SQL_HANDLE_DBC);
return(255);
}
if (SQL_SUCCEEDED(Oret=SQLGetInfo(*Ocn, SQL_DBMS_NAME,
(SQLPOINTER)Obuff, (SQLSMALLINT)sizeof(Obuff), NULL))) {
if(Oret == SQL_SUCCESS_WITH_INFO)
fprintf(stderr, "odb [checkdb(%d)] - Warning data truncation\n", __LINE__);
}
if(!SQL_SUCCEEDED(Oret))
Oerr(eid, tid, __LINE__, *Ocn, SQL_HANDLE_DBC);
if ( ! ( f & 00040 ) )
fprintf(stderr, "Connected to %s\n", (char *)Obuff);
if ( !strcmp((char *)Obuff, dbscmds[1].dbname) ) { /* PostgreSQL */
dbt = 1;
f |= 010000000000; /* Postgres uses lowercase table names if not quoted */
if ( c )
if (!SQL_SUCCEEDED(Oret=SQLGetInfo(*Ocn, SQL_DATABASE_NAME,
(SQLPOINTER)c, (SQLSMALLINT)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, *Ocn, SQL_HANDLE_DBC);
if ( s ) {
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Ostmt,
(SQLCHAR *)"SELECT CURRENT_SCHEMA()", SQL_NTS)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 1,
SQL_C_CHAR, (SQLCHAR *)s, (SQLLEN)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret=SQLFetch(Ostmt)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
} else if ( !strcmp((char *)Obuff, dbscmds[2].dbname) ) { /* Vertica */
dbt = 2;
if ( c ) { /* Read Calatog from Output Connection String */
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Ostmt,
(SQLCHAR *)"SELECT CURRENT_DATABASE()", SQL_NTS)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 1,
SQL_C_CHAR, (SQLCHAR *)c, (SQLLEN)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret=SQLFetch(Ostmt)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
(void)SQLFreeStmt(Ostmt, SQL_CLOSE);
(void)SQLFreeStmt(Ostmt, SQL_UNBIND);
}
if ( s ) {
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Ostmt,
(SQLCHAR *)"SELECT CURRENT_SCHEMA()", SQL_NTS)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 1,
SQL_C_CHAR, (SQLCHAR *)s, (SQLLEN)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret=SQLFetch(Ostmt)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
} else if ( !strcmp((char *)Obuff, dbscmds[3].dbname) ) { /* Microsoft SQL Server */
dbt = 3;
if ( c )
if (!SQL_SUCCEEDED(Oret=SQLGetInfo(*Ocn, SQL_DATABASE_NAME,
(SQLPOINTER)c, (SQLSMALLINT)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, *Ocn, SQL_HANDLE_DBC);
if ( s ) {
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Ostmt,
(SQLCHAR *)"SELECT SCHEMA_NAME()", SQL_NTS)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 1,
SQL_C_CHAR, (SQLCHAR *)s, (SQLLEN)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret=SQLFetch(Ostmt)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
} else if ( !strcmp((char *)Obuff, dbscmds[4].dbname) ) { /* Teradata */
dbt = 4;
if ( c )
*c = '\0'; /* empty string */
if ( s )
if (!SQL_SUCCEEDED(Oret=SQLGetInfo(*Ocn, SQL_DATABASE_NAME,
(SQLPOINTER)s, (SQLSMALLINT)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, *Ocn, SQL_HANDLE_DBC);
} else if ( !strcmp((char *)Obuff, dbscmds[5].dbname) ) { // NonStop SQL/MX
dbt = 5;
if ( Oocs[0] ) {
if ( c ) { // Read Calatog from Output Connection String
for ( i = 0, p = strstr((char *)Oocs, "CATALOG=") + 8 ; *p != ';' && i < sizeof(Oocs) ; p++, i++ )
c[i] = *p;
c[i] = '\0';
}
if ( s ) { // Read Schema from Output Connection String
for ( i = 0, p = strstr((char *)Oocs, "SCHEMA=") + 7 ; *p != ';' && i < sizeof(Oocs) ; p++, i++ )
s[i] = *p;
s[i] = '\0';
}
}
} else if ( !strcmp((char *)Obuff, dbscmds[6].dbname) ) { /* Oracle */
dbt = 6;
f |= 04000000000 ; /* Catalog as NULL */
if ( c )
*c = '\0';
if ( s ) {
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Ostmt,
(SQLCHAR *)"SELECT SYS_CONTEXT('userenv','current_schema') x FROM DUAL", SQL_NTS)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 1,
SQL_C_CHAR, (SQLCHAR *)s, (SQLLEN)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret=SQLFetch(Ostmt)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
} else if ( !strcmp((char *)Obuff, dbscmds[7].dbname ) ) { /* MySQL */
dbt = 7;
if ( c )
*c = '\0';
if ( s ) {
if (!SQL_SUCCEEDED(Oret=SQLExecDirect (Ostmt,
(SQLCHAR *)"SELECT SCHEMA() FROM DUAL", SQL_NTS)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 1,
SQL_C_CHAR, (SQLCHAR *)s, (SQLLEN)MAXOBJ_LEN, NULL)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if (!SQL_SUCCEEDED(Oret=SQLFetch(Ostmt)))
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
} else if ( !strcmp((char *)Obuff, dbscmds[8].dbname) )
{ /* Trafodion */
dbt = 8;
if ( Oocs[0] )
{
if ( c )
{ /* Read Calatog from Output Connection String */
for ( i = 0, p = strstr((char *)Oocs, "CATALOG=") + 8 ; *p != ';' && i < sizeof(Oocs) ; p++, i++ )
c[i] = *p;
c[i] = '\0';
}
if ( s )
{ /* Read Schema from Output Connection String */
for ( i = 0, p = strstr((char *)Oocs, "SCHEMA=") + 7 ; *p != ';' && i < sizeof(Oocs) ; p++, i++ )
s[i] = *p;
s[i] = '\0';
}
}
}
/* If this is the Interpreter get the object types list */
if ( eid >= 0 && etab[eid].type == 'I' ) {
(void)SQLFreeStmt(Ostmt, SQL_CLOSE);
(void)SQLFreeStmt(Ostmt, SQL_UNBIND);
if (!SQL_SUCCEEDED(Oret=SQLTables(Ostmt,
(SQLCHAR *)"", 0,(SQLCHAR *)"", 0, (SQLCHAR *)"", 0,
(SQLCHAR *)SQL_ALL_TABLE_TYPES, (SQLSMALLINT)1))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
} else {
if (!SQL_SUCCEEDED(Oret = SQLBindCol(Ostmt, (SQLUSMALLINT) 4,
SQL_C_CHAR, Obuff, (SQLLEN)sizeof(Obuff), &Olen[0]))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
}
while (SQL_SUCCEEDED(Oret=SQLFetch(Ostmt))) {
if ( Oaot[0] )
strmcat ( (char *) Oaot, ",", sizeof(Oaot), 0);
strmcat((char *) Oaot,(char *) Obuff, sizeof(Oaot), 0);
}
}
}
(void)SQLFreeHandle(SQL_HANDLE_STMT, Ostmt);
/* Next find if row counts are available for parameterized executions
* Alias: can we use SQLRowCount() after an array INSERT/DELETE/UDATE? */
if ( eid > 0 ) {
if (SQL_SUCCEEDED(Oret=SQLGetInfo(*Ocn, SQL_PARAM_ARRAY_ROW_COUNTS,
(SQLPOINTER)&Obatch, 0, NULL))) {
switch ( Obatch ) {
case SQL_PARC_BATCH:
etab[eid].flg2 &= ~01000 ;
break;
case SQL_PARC_NO_BATCH:
etab[eid].flg2 |= 01000 ;
break;
default:
fprintf(stderr, "odb [checkdb(%d)] - Invalid SQL_PARAM_ARRAY_ROW_COUNTS\n", __LINE__);
}
} else {
Oerr(eid, tid, __LINE__, *Ocn, SQL_HANDLE_DBC);
}
}
return ( dbt );
}
/* mchar:
* return char from strings, decimal/hex/octal numbers. Example:
* "," "44" "x2c" "054" all return comma
* str: input string
* return: char
*/
static unsigned char mchar ( char *s )
{
long l;
while( *s == ' ' || *s == '\t' ) /* skip leading white spaces */
s++;
if ( *s == '0' ) {
l=strtol(++s, (char **)NULL, 8);
} else if ( *s == 'x' || *s == 'X' ) {
l=strtol(++s, (char **)NULL, 16);
} else if ( *s > '0' && *s <= '9' ) {
l=strtol( s, (char **)NULL, 10);
} else {
return ( *s );
}
return( (unsigned char)l );
}
/* hextobin:
* convert c from hexadecimal char to binary
*/
char hextobin(char c) {
switch(c) {
default: return c - '0';
case 'a': case 'A': return '\10';
case 'b': case 'B': return '\11';
case 'c': case 'C': return '\12';
case 'd': case 'D': return '\13';
case 'e': case 'E': return '\14';
case 'f': case 'F': return '\15';
}
}
/* replace_escapes:
* replace escape sequence like "\r" "\n" "\t" "\xA0"
* str: input string
* return: str
*/
static char* replace_escapes ( char *str ) {
char *p = str;
char *q = str;
while (*q) {
if (*q == '\\' && *(q + 1)) {
switch (*++q) {
case 'n': *p++ = '\n'; break;
case 'r': *p++ = '\r'; break;
case 't': *p++ = '\t'; break;
case 'x': case 'X':
if (!isxdigit(q[1]))
goto not_escape;
*p = hextobin(*++q);
if (isxdigit(q[1]))
*p = *p * 16 + hextobin(*++q);
++p;
break;
default: // no need to be converted
not_escape:
*p++ = '\\';
*p++ = *q;
break; // just escape
}
++q;
}
else {
*p++ = *q++;
}
}
*p = '\0';
return str;
}
/* strup:
* convert a source string (s) to uppercase
*
* s: null terminated string to uppercase
*
* return: the conveterd string
*/
static char *strup ( char *s )
{
char *save = s;
while ( *s ) {
*s = toupper((unsigned char) *s);
s++;
}
return(save);
}
/* strlo:
* convert a source string (s) to lowercase
*
* s: null terminated string to lowercase
*
* return: the conveterd string
*/
static char *strlo ( char *s )
{
char *save = s;
while ( *s ) {
*s = tolower((unsigned char) *s);
s++;
}
return(save);
}
static char *strtrim(char *str)
{
// trim tailing space
size_t i = strlen(str) - 1;
while (str[i] == ' ') --i;
str[i + 1] = '\0';
// trim heading space
for (i = 0; str[i] == ' '; ++i);
if (i > 0)
strcpy(str, str + i);
return str;
}
/* expandtype:
* return SQL type string associated with a given data type
*
* Odt: SQL data type code
* return: pointer to SQL type string
*/
static char *expandtype( SQLSMALLINT Odt )
{
switch ( Odt ) {
case SQL_SMALLINT: return("SQL_SMALLINT");
case SQL_INTEGER: return("SQL_INTEGER");
case SQL_TINYINT: return("SQL_TINYINT");
case SQL_BIGINT: return("SQL_BIGINT");
case SQL_REAL: return("SQL_REAL");
case SQL_DOUBLE: return("SQL_DOUBLE");
case SQL_FLOAT: return("SQL_FLOAT");
case SQL_NUMERIC: return("SQL_NUMERIC");
case SQL_DECIMAL: return("SQL_DECIMAL");
case SQL_CHAR: return("SQL_CHAR");
case SQL_WCHAR: return("SQL_WCHAR");
case SQL_VARCHAR: return("SQL_VARCHAR");
case SQL_WVARCHAR: return("SQL_WVARCHAR");
case SQL_LONGVARCHAR: return("SQL_LONGVARCHAR");
case SQL_WLONGVARCHAR: return("SQL_WLONGVARCHAR");
case SQL_TIME: return("SQL_TIME");
case SQL_TYPE_TIME: return("SQL_TYPE_TIME");
case SQL_TYPE_DATE: return("SQL_TYPE_DATE");
case SQL_TIMESTAMP: return("SQL_TIMESTAMP");
case SQL_TYPE_TIMESTAMP: return("SQL_TYPE_TIMESTAMP");
case SQL_BIT: return("SQL_BIT");
case SQL_BINARY: return("SQL_BINARY");
case SQL_VARBINARY: return("SQL_VARBINARY");
case SQL_LONGVARBINARY: return("SQL_LONGVARBINARY");
case SQL_INTERVAL_MONTH: return("SQL_INTERVAL_MONTH");
case SQL_INTERVAL_YEAR: return("SQL_INTERVAL_YEAR");
case SQL_INTERVAL_YEAR_TO_MONTH: return("SQL_INTERVAL_YEAR_TO_MONTH");
case SQL_INTERVAL_DAY: return("SQL_INTERVAL_DAY");
case SQL_INTERVAL_HOUR: return("SQL_INTERVAL_HOUR");
case SQL_INTERVAL_MINUTE: return("SQL_INTERVAL_MINUTE");
case SQL_INTERVAL_SECOND: return("SQL_INTERVAL_SECOND");
case SQL_INTERVAL_DAY_TO_HOUR: return("SQL_INTERVAL_DAY_TO_HOUR");
case SQL_INTERVAL_DAY_TO_MINUTE: return("SQL_INTERVAL_DAY_TO_MINUTE");
case SQL_INTERVAL_DAY_TO_SECOND: return("SQL_INTERVAL_DAY_TO_SECOND");
case SQL_INTERVAL_HOUR_TO_MINUTE: return("SQL_INTERVAL_HOUR_TO_MINUTE");
case SQL_INTERVAL_HOUR_TO_SECOND: return("SQL_INTERVAL_HOUR_TO_SECOND");
case SQL_INTERVAL_MINUTE_TO_SECOND: return("SQL_INTERVAL_MINUTE_TO_SECOND");
case SQL_GUID: return("SQL_GUID");
default: return("UNKNOWN");
}
}
/* runsql:
* executes SQL scripts for a given thread.
*
* tid: thread id
* eid: execution number id
* ten: thread execution number
* script: script to run
*
* return: 0 OK, 1 runsql() error, 2 Omexec() error and SOE is set
*/
static int runsql(int tid, int eid, int ten, char *script)
{
register unsigned int j = 0; /* loop variable */
int n = 0, /* command/script no for this thread */
k = 1, /* command no in a given script */
q = 0, /* flag for quoted string */
ret = 0, /* return value from Oexec */
rv = 0, /* return value from this function */
ch = 0, /* char read from file */
cp = 0, /* previous char read from file */
nrag=0; /* Number of rag[] arguments */
unsigned int lcd = 0; /* delay to start next command within a thread */
FILE *fr = 0; /* FILE pointer */
SQLCHAR *Ocmd = 0; /* Ocmd buffer */
SQLCHAR *O = 0; /* ODBC temp variable for realloc */
size_t bs = CMD_CHUNK; /* Ocmd buffer size */
size_t bs1 = CMD_CHUNK; /* line buffer size */
char *line = 0, /* line to tokenize */
*l = 0 , /* temp realloc var */
*rag[MAX_ARGS]; /* arguments in a row */
char ql[QLABEL_LEN]; /* Query label */
unsigned long smr = etab[eid].mr; /* save max records to be fetched */
size_t sr = etab[eid].r; /* save rowset */
char stype = etab[eid].type; /* save execution type */
char *sns = etab[eid].ns; /* save original null string */
unsigned int sflg = etab[eid].flg; /* save eid flg */
/* Initialize Query Label */
ql[0] = '\0';
/* Initial Ocmd allocation */
if ( (Ocmd = malloc ( bs ) ) == (void *)NULL ||
(line = malloc ( bs1 ) ) == (void *)NULL ) {
fprintf(stderr, "odb [runsql(%d)] - Error allocating memory buffers: [%d] %s\n",
__LINE__, errno, strerror(errno));
rv = 1;
goto runsql_exit;
}
/* read from file and run commands */
if ( ! strcmp ( script, "-" ) ) {
fr = stdin;
} else if ( (fr = fopen (script, "r")) == (FILE *) NULL ) {
fprintf(stderr, "odb [runsql(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, script, errno, strerror(errno));
rv = 1;
goto runsql_exit;
}
for ( ch = cp = j = k = 0; ( ch = getc(fr) ) != EOF ; cp = ch ) {
if ( !(f2 & 0001) && ch == 42 && cp == 47 && q == 0 ) { /* remove C style comments comments */
do {
cp = ch ;
ch = getc ( fr ) ;
} while ( ch != 47 || cp != 42 );
j--;
} else if ( ch == '-' && cp == '-' && q == 0 ) { /* remove douple hyphen comments */
while ( ( ch = getc(fr) ) != EOF && ch != '\n');
j--; /* first '-' is already in Ocmd */
} else if ( ch == ';' ) { /* command end */
Ocmd[j++] = (SQLCHAR)ch;
Ocmd[j] = (SQLCHAR)'\0';
Ocmd = (SQLCHAR *)var_exp ( (char *)Ocmd, &bs, &thps[tid].tva);
if ( ! ( etab[eid].flg & 010000 ) ) /* if not quiet cmd on */
mfprintf(stderr, etab[eid].fso , "[%d.%d.%d]%s: \'%s\'\n",
tid, n, k, ( etab[eid].flg & 0010 ? "Preparing": "Executing" ), (char *)Ocmd);
if ( thps[tid].cd2 ) /* random delay between cd and cd2 */
lcd = (unsigned int) ( thps[tid].cd + rand() / ( RAND_MAX + 1.0 ) * ( thps[tid].cd2 - thps[tid].cd + 1 ) );
else
lcd = thps[tid].cd;
if ( k && lcd )
Sleep (lcd);
if ( ( ret = Omexec( tid, eid, ten, k++, Ocmd, ql, (char *)NULL, (char *)NULL) ) < 0 ) {
if ( etab[eid].flg & 0004 ) {
rv = 2; /* error during execution & exit on error */
goto runsql_exit;
} else {
fprintf ( stderr,
"odb [runsql(%d)] - [%d.%d.%d] Error received while executing %s\n",
__LINE__, tid, n, k, script);
fprintf ( stderr, "Offending Command: >%s<\n", Ocmd);
}
}
j = 0; /* reset command position */
ql[0]='\0'; /* clean query label */
} else if ( ch == '\n' || ch == '\r' ) {
if ( j ) {
if ( (size_t) j >= bs1 ) { /* line requires more memory */
bs1 = (size_t)j + 1;
l = line ;
if ( ( l = realloc ( l, bs1 ) ) == (void *)NULL ) {
fprintf(stderr, "odb [runsql(%d)] - Error re-allocating memory for line: [%d] %s\n",
__LINE__, errno, strerror(errno));
rv = 1;
goto runsql_exit;
}
line = l ;
}
strmcpy ( line, (char *)Ocmd, j );
// eliminate left spaces
unsigned int alphaIndex = 0;
while (alphaIndex < j && (line[alphaIndex] == ' ' || line[alphaIndex] == '\t')) ++alphaIndex;
if (alphaIndex != j) { // skip blank line
if (!strmicmp(line, "odb ", 4)) {
snprintf((char *)Ocmd, bs, "%s -u %s -p %s %s%s %s%s %s",
odbcmd, (char *)Ouser, (char *)Opwd, Odsn[0] ? "-d " : "", Odsn[0] ? (char *)Odsn : "",
clca ? "-ca" : "", clca ? clca : "", line + 4);
#ifdef _WIN32
_spawnlp(_P_WAIT, "cmd.exe", "cmd.exe", "/c", var_exp((char *)Ocmd, &bs, &thps[tid].tva), NULL);
#else
if (system((const char *)var_exp((char *)Ocmd, &bs, &thps[tid].tva)) < 0)
fprintf(stderr, "odb [runsql(%d)] - Error running %s\n", __LINE__, &Ocmd[1]);
#endif
j = 0;
}
else {
nrag = tokenize(line, j, rag);
if (nrag && !strmicmp(rag[0], "set", 0)) {
setan(eid, tid, nrag, rag, ql);
j = 0;
}
else if (!strmicmp(rag[0], "print", 0)) {
var_exp(nrag ? rag[1] : "", 0, &thps[0].tva);
j = 0;
}
else {
Ocmd[j++] = (SQLCHAR)ch;
}
}
}
}
} else { /* ch is not a space or new line char */
Ocmd[j++] = (SQLCHAR)ch;
if ( ( ch == 34 || ch == 39 ) && cp != ec )
q = q ? 0 : 1;
}
if ( ( j + 1 ) >= bs ) { /* we need more memory for Ocmd */
bs += CMD_CHUNK;
O = Ocmd ;
if ( ( O = realloc ( O, bs ) ) == (void *)NULL ) {
fprintf(stderr, "odb [runsql(%d)] - Error re-allocating memory for Ocmd: [%d] %s\n",
__LINE__, errno, strerror(errno));
break;
}
Ocmd = O ;
}
}
runsql_exit:
if ( line )
free ( line ) ;
if ( Ocmd )
free ( Ocmd ) ;
if ( fr && fr != stdin )
fclose(fr);
/* restore original eid values */
etab[eid].mr = smr; /* reset max fetch records */
etab[eid].r = sr; /* reset rowset */
etab[eid].flg = sflg; /* reset command flag */
etab[eid].type = stype; /* reset execution type */
if ( sns != etab[eid].ns ) { /* if nullstring was changed */
free ( etab[eid].ns ); /* free \"new\" allocated memory */
etab[eid].ns = sns; /* restore original nullstring */
}
return(rv);
}
/* ifempty:
* check if the table is empty
*
* eid: etab idx
* table: table name
* return: 0 (table is empty), 1 (table is not empty)
*/
static unsigned int ifempty(int eid, char *table)
{
SQLCHAR Ocmd[CMD_CHUNK]; /* ODBC Command buffer */
unsigned int sflg = etab[eid].flg; /* save eid flg */
unsigned long smr = etab[eid].mr; /* save max records to be fetched */
size_t sr = etab[eid].r; /* save rowset */
int tid = etab[eid].id; /* thread ID */
Ocmd[0] = '\0'; /* initialize Ocmd */
/* build select command */
(void) strmcat ( (char *) Ocmd, "SELECT * FROM ", sizeof(Ocmd), 0);
(void) strmcat ( (char *) Ocmd, table, sizeof(Ocmd), 0);
/* alter eid values */
etab[eid].mr = (unsigned long)1 ; /* fetch one row */
etab[eid].r = (size_t)1; /* use single row rowset */
etab[eid].flg |= 0100000000; /* set silent mode */
/* run command */
etab[eid].flg2 |= 020000000 ; /* Oexec to allocate/use its own handle */
(void) Oexec(tid, eid, 0, 0, Ocmd, "");
etab[eid].flg2 &= ~020000000 ; /* reset Oexec to allocate/use its own handle */
/* reset eid values */
etab[eid].mr = smr ; /* set original maxfetch */
etab[eid].r = sr; /* set original rowset */
etab[eid].flg = sflg ; /* set original flg */
/* return */
return(etab[eid].nr ? 1 : 0 );
}
static int comp2asc(char *srcfld, char *tgtfld,unsigned int srclen, unsigned int maxlen)
{
unsigned int i = 0;
unsigned int j = 0;
unsigned int k = 0;
unsigned int tgtlen = 0;
unsigned char tmpx = 0;
unsigned char tmpy = 0;
unsigned long long result = 0;
/* First we need to calculate target size */
switch (srclen) {
case (2):
tgtlen = 5 + 1; /* num chars + sign */
break;
case (4):
tgtlen = 10 + 1; /* num chars + sign */
break;
case (8):
tgtlen = 19 + 1; /* num chars + sign */
break;
default:
fprintf(stderr, "odb [comp2asc(%d)] - Invalid COMP field size (%u). Suitable size 2, 4 or 8 bytes\n",
__LINE__, srclen);
return (-2);
}
if (tgtlen > maxlen)
return (-1);
/* Now the real conversion */
for ( i = srclen ; i > 0 ; i-- ) {
for ( k = 8, tmpy = 1 ; k > 0 ; k--, j++, tmpy <<= 1 ) {
tmpx = (unsigned char)(srcfld[i-1] & tmpy);
if ( i == 1 && k == 1 ) /* sign always is the most significant bit */
break;
if (tmpx != 0) /* it is not necessary to sum */
#ifdef _WIN32
result += 1i64<<j;
#else
result += 1<<j;
#endif
}
}
if ( srclen == 8 ) {
#ifdef _WIN32
result += 1i64<<j;
#else
result += 1<<j;
#endif
result >>= 1;
}
/* Fill output buffer & return */
snprintf(tgtfld, tgtlen+1, "%c%0*llu", tmpx ? '-' : '+', tgtlen - 1 , result ); /* it is tgtlen +1 because of the last \0 */
return ((int)tgtlen);
}
static int comp32asc(char * srcfld, char *tgtfld, unsigned int srclen, unsigned int maxlen, int precision, int scale)
{
unsigned int i = 0;
int numdec = 0;
char sign = 0;
unsigned char tmpc = 0;
unsigned int tgtlen = 0;
/* default values precision 5 and scale 0*/
if (scale < 0)
scale = 0;
if (precision < 0)
precision = 5;
tgtlen = precision + 1 + 1; /* sign + decimal separator */
if (tgtlen % 2 == 0 ) /* if odd number always plus 1 */
tgtlen++;
if (scale == 0) /* if there are no decimals remove "." char */
tgtlen = tgtlen - 1;
if (tgtlen > maxlen)
return(-1);
for (i=srclen; i>0; i--) {
if (i==srclen) { /* first iteration. sign + less valuable digit */
tmpc = (unsigned char)(srcfld[i-1] & 15 ); /* 15: 0000 FFFF */
switch (tmpc) {
case 10:
case 12:
case 14:
case 15:
sign = '+';
break;
case 11:
case 13:
sign = '-';
break;
default:
fprintf(stderr, "odb [comp32asc(%d)] - Invalid COMP3 sign format b[%x]i[%d]\n",
__LINE__, srcfld[i-1], i-1 );
return (-2);
}
tmpc = (unsigned char)(srcfld[i-1] & 240); /*240: FFFF 0000 */
tmpc = (unsigned char)(tmpc >> 4);
tgtfld[tgtlen-numdec-1] = tmpc + 48;
} else {
if (numdec == scale) {
tgtfld[tgtlen-numdec-1] = '.';
numdec++;
}
/* low */
tmpc = 15;
tmpc = (unsigned char)(srcfld[i-1] & tmpc);
tgtfld[tgtlen-numdec-1] = tmpc + 48;
numdec++;
if (numdec == scale) {
tgtfld[tgtlen-numdec-1] = '.';
numdec++;
}
/* high */
tmpc = 240;
tmpc = (unsigned char)(srcfld[i-1] & tmpc);
tmpc = (unsigned char)(tmpc >> 4);
tgtfld[tgtlen-numdec-1] = tmpc + 48;
}
numdec++;
}
tgtfld[0] = sign;
tgtfld[tgtlen] = 0;
return(tgtlen);
}
static int zoned2asc(char * srcfld, char *tgtfld, unsigned int srclen, unsigned int maxlen, int precision, int scale)
{
unsigned int i = 0;
unsigned int tgtlen = 0;
unsigned char tmpc = 128;
unsigned int count = 0;
if (precision < 0)
precision = (int)srclen;
if (scale <0)
scale = 0;
if (srclen != (unsigned int)precision) {
fprintf(stderr, "odb [zoned2asc(%d)] - Error: ZONED field length different from precision\n",
__LINE__);
return -2;
}
tgtlen = (int)srclen + 1;
if (scale > 0)
tgtlen++;
if (tgtlen > maxlen)
return -1;
/* Nonstop implementation highest bit on the last byte = sign */
/* sign */
tmpc = (char)(tmpc & srcfld[srclen-1]);
if (tmpc > 0) {
tgtfld[count++]='-'; /* F000 0000 */
} else {
tgtfld[count++]='+'; /* 0000 0000 */
}
for (i=0;i<srclen;i++) {
if (count == (unsigned int)(precision - scale + 1))
tgtfld[count++] = '.';
if (i == srclen-1 ) {
tgtfld[count++] = (char)(srcfld[i] & 127); /* 0FFF FFFF */
} else {
if (srcfld[i] < 48 || srcfld[i] > 57) {
fprintf(stderr, "odb [zoned2asc(%d)] - Error: invalid ZONED character (%d)\n",
__LINE__, srcfld[i]);
return -2;
}
tgtfld[count++] = srcfld[i];
}
}
tgtfld[tgtlen] = 0;
return((int)tgtlen);
}
/* Otcol:
* analyze table in etab[eid].Ocso[0-2] and fills etab[eid].td[]
*
* eid: etab[] index
*
* return: number of columns in table or -1 if errors
*/
static int Otcol(int eid, SQLHDBC *Ocn)
{
SQLCHAR Osel[512]; /* Select statement buffer */
SQLHSTMT Ostmt=0; /* ODBC Statement Handle */
SQLCHAR Ocname[MAXCOL_LEN]; /* Temporary Column Name pointer */
SQLSMALLINT Onamel; /* ODBC column name length returned by SQLDescribeCol */
SQLUSMALLINT Oncol; /* ODBC number of columns in the result set */
unsigned int i = 0; /* loop variable */
SQLRETURN Or; /* ODBC return value */
int tid = eid < 0 ? -1 : etab[eid].id; /* Thread ID */
int ret = 0; /* return value */
/* Build select statement */
(void)snprintf((char *)Osel, sizeof(Osel), "SELECT * FROM %s%s%s%s%s WHERE 1 = 0",
etab[eid].Ocso[0] ? (char *)etab[eid].Ocso[0] : "", (char *)etab[eid].Ocso[0] ? "." : "",
etab[eid].Ocso[1] ? (char *)etab[eid].Ocso[1] : "", (char *)etab[eid].Ocso[1] ? "." : "",
(char *)etab[eid].Ocso[2] );
/* Allocate statement handle */
if (!SQL_SUCCEEDED(Or=SQLAllocHandle(SQL_HANDLE_STMT, *Ocn, &Ostmt))){
Oerr(0, 0, __LINE__, *Ocn, SQL_HANDLE_DBC);
ret = -1;
goto otcol_exit;
}
/* Prepare dummy select */
if ((Or=SQLPrepare(Ostmt, Osel, SQL_NTS)) != SQL_SUCCESS) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
if ( Or != SQL_SUCCESS_WITH_INFO ) {
ret = -1;
goto otcol_exit;
}
}
/* Get number of resulting cols */
if (!SQL_SUCCEEDED(Or=SQLNumResultCols(Ostmt, (SQLSMALLINT *)&Oncol))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
ret = -1;
goto otcol_exit;
}
/* Allocate struct tdesc array */
if ( ( etab[eid].td = calloc ( (size_t)Oncol, sizeof(struct tdesc) ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Otcol(%d)] - Error allocating %u memory elements for struct tdesc\n",
__LINE__, (unsigned int)Oncol);
ret = -1;
goto otcol_exit;
}
/* Get column attributes and fill td structure */
for ( i = 0 ; i < (unsigned int)Oncol ; i++ ) {
if ( !SQL_SUCCEEDED(Or=SQLDescribeCol(Ostmt, (SQLUSMALLINT)(i+1),
Ocname, (SQLSMALLINT)MAXCOL_LEN, &Onamel,
&etab[eid].td[i].Otype, &etab[eid].td[i].Osize,
&etab[eid].td[i].Odec, &etab[eid].td[i].Onull)) ) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
ret = -1;
goto otcol_exit;
}
if(!SQL_SUCCEEDED(Or=SQLColAttribute(Ostmt, (SQLUSMALLINT)(i+1),
SQL_DESC_DISPLAY_SIZE, (SQLPOINTER) NULL, (SQLSMALLINT) 0,
(SQLSMALLINT *) NULL, (SQLPOINTER) &etab[eid].td[i].Osize))) {
Oerr(eid, tid, __LINE__, Ostmt, SQL_HANDLE_STMT);
ret = -1;
goto otcol_exit;
}
switch ( etab[eid].td[i].Otype ) {
case SQL_TYPE_TIME:
break;
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
if ( etab[eid].Omaxl && etab[eid].Omaxl < etab[eid].td[i].Osize )
etab[eid].td[i].Osize = etab[eid].Omaxl;
break;
}
if ( ( etab[eid].td[i].Oname = (SQLCHAR *)malloc((size_t)Onamel+1) ) == (void *)NULL ) {
fprintf(stderr, "odb [Otcol(%d)] - Error allocating %d bytes as column name buffer for %s: [%d] %s\n",
__LINE__, (int)Onamel+1, (char *)Ocname, errno, strerror(errno));
goto otcol_exit;
}
strmcpy((char *)etab[eid].td[i].Oname, (char *)Ocname, (size_t)Onamel);
}
ret = (int)Oncol;
/* Free handle and exit */
otcol_exit:
(void)SQLFreeHandle(SQL_HANDLE_STMT, Ostmt);
return ( ret );
}
/* parseopt:
* parse options for copy/extract/load/diff and fill etab[no] structure
*
* type(I): type of action
* str(I): string to parse
* len(I): string length
* rp(I): remember position (0=no, 1=yes)
*
* return: 0=parse ok, 1=unknown parameter, 2=no more options to parse
*/
static unsigned int parseopt(char type, char *str, unsigned int len, unsigned int rp)
{
unsigned int j = 0, /* loop variable */
l = 0, /* loop variable */
n = 0, /* loop variable */
i = 0, /* loop variable */
o = 0, /* loop variable */
k = 0, /* loop variable */
eol = 0; /* end of line indicator */
char q = 0; /* quoted pwhere: [...] */
char *p = 0; /* loop variable */
static unsigned int pos = 0; /* next string start when parsing file with lines */
while (isspace((int)*(str+pos)) || *(str+pos)=='#') {
while (isspace((int)*(str+pos)))/* skip spaces and empty lines */
pos++;
while ( *(str+pos) == '#' ) { /* skip comments */
while ( *(str + pos) && *(str + (++pos)) != '\n' ) ;
if ( *(str+pos) == '\n' )
pos++;
}
}
if ( pos >= len ) /* no more options to parse */
return ( 2 ) ;
tmpre = 0 ; /* reset tmpre */
for ( q = l = n = o = 0, j=pos; j < len ; j++) { /* n points param name, l points value */
switch ( o ) {
case 0: /* looking for param name */
for ( n = j ; str[j] && str[j] != '=' && str[j] != ':' && str[j] != '\n'; j++);
if ( str[j] == ':' ) { /* param with no value */
str[j]='\0';
o = 2;
} else if ( str[j] == '\n' ) { /* param with no value && EOL */
str[j]='\0';
eol = 1;
o = 2;
for ( k = n ; k < j ; k++ ) { /* remove trailing spaces/comments */
if ( isspace((int)str[k]) || str[k]=='#' ) {
str[k] = '\0';
break;
}
}
} else { /* param with value */
o = 1;
}
/* FALLTHRU */
case 1: /* looking for value */
if ( str[j+1] == '[' ) {
j++;
q = 1;
}
#ifdef _WIN32
for (l = j; (str[j] && str[j] != '\n') && (q || str[j] != ':') || str[j + 1] == '/'; j++) {
#else
for (l = j; (str[j] && str[j] != '\n') && (q || str[j] != ':'); j++) {
#endif
if ( q ) {
if ( str[j] == ']' && ( str[j+1] == ':' || str[j+1] == 0 || isspace((int)str[j+1]) ) ) {
q = 0;
str[j]= '\0';
}
} else {
if ( isspace((int)str[j]) || str[j]=='#' )
str[j] = '\0';
}
}
if ( str[j] == ':' ) {
str[j]='\0';
} else if ( str[j] =='\n' ) {
eol = 1;
str[j]='\0';
}
o = 2;
/* FALLTHRU */
case 2: /* fill etab structure */
if ( str[l] == '=' ) {
str[l++] = '\0';
} else if ( str[l] == '[' && str[l-1] == '=' ) {
str[l-1] = '\0';
str[l++] = '\0';
}
if ( !strcmp(&str[n], "src") ) {
if ( !strncmp(&str[l], "hdfs", 4) || !strncmp(&str[l], "mapr", 4) ) {
#ifdef HDFS
if ( str[l+4] == '@' )
etab[no].src = parsehdfs(no, &str[l] + 5) ;
else
etab[no].src = &str[l] + 5;
etab[no].flg2 |= 0100;
if ( !strncmp(&str[l], "mapr", 4 ) )
etab[no].flg2 |= 01000000000 ;
#else
fprintf(stderr, "odb [parseopt(%d)] - HDFS not supported by this odb executable\n", __LINE__);
return ( 1 ) ;
#endif
} else {
etab[no].src = &str[l];
}
} else if ( !strcmp(&str[n], "tgt") ) {
if ( !strncmp(&str[l], "hdfs", 4) || !strncmp(&str[l], "mapr", 4) ) {
#ifdef HDFS
if ( str[l+4] == '@' )
etab[no].tgt = parsehdfs(no, &str[l] + 5) ;
else
etab[no].tgt = &str[l] + 5;
etab[no].flg2 |= 0100;
if ( !strncmp(&str[l], "mapr", 4 ) )
etab[no].flg2 |= 01000000000 ;
#else
fprintf(stderr, "odb [parseopt(%d)] - HDFS not supported on this odb executable\n", __LINE__);
return ( 1 ) ;
#endif
} else {
etab[no].tgt = &str[l];
}
} else if ( !strcmp(&str[n], "pre") ) {
etab[no].pre = &str[l];
etab[no].lstat |= 0004; /* this works for both octal mask & decimal lstat interpretation */
} else if ( !strcmp(&str[n], "post") ) {
etab[no].post = &str[l];
} else if ( !strcmp(&str[n], "time") ) {
etab[no].flg2 |= 020000 ;
} else if ( ( type == 'e' || type == 'c' || type == 'p' ) && !strcmp(&str[n], "mpre") ) {
etab[no].mpre = &str[l];
} else if ( ( type == 'c' || type == 'p' ) && !strcmp(&str[n], "tmpre") ) {
tmpre = &str[l];
} else if ( ( type == 'e' || type =='c' || type == 'p' ) && !strcmp(&str[n], "sql") ) {
etab[no].sql = &str[l];
} else if ( type == 'l' && !strcmp(&str[n], "map") ) {
etab[no].map = &str[l];
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( (type == 'l' || type == 'c') && !strcmp(&str[n], "bad") ) {
etab[no].bad = &str[l];
} else if ( type == 'c' && !strcmp(&str[n], "errdmp") ) {
if ( !(strcmp(&str[l], "stdout") ) ) {
etab[no].fdmp = stdout ;
} else if ( !(strcmp(&str[l], "stderr") ) ) {
etab[no].fdmp = stderr ;
} else {
if ( ( etab[no].fdmp = fopen(&str[l], "w") ) == (FILE *)NULL )
fprintf(stderr, "odb [parseopt(%d)] - Cannot open %s: [%d] %s\n",
__LINE__, &str[l], errno, strerror(errno));
}
} else if ( type != 'c' && !strcmp(&str[n], "fs") ) {
etab[no].fs = mchar(&str[l]);
} else if ( type != 'c' && !strcmp(&str[n], "mcfs") ) {
etab[no].mcfs = replace_escapes(&str[l]);
etab[no].flg |= 01000000; /* complex load. Use Oload */
} else if ( type != 'c' && !strcmp(&str[n], "rs") ) {
etab[no].rs = mchar(&str[l]);
} else if ( type != 'c' && !strcmp(&str[n], "mcrs") ) {
etab[no].mcrs = replace_escapes(&str[l]);
etab[no].flg |= 01000000; /* complex load. Use Oload */
} else if ( ( type == 'e' || type == 'l' ) && !strcmp(&str[n], "ec") ) {
etab[no].ec = mchar(&str[l]);
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( type == 'l' && !strcmp(&str[n], "pc") ) {
etab[no].pc = mchar(&str[l]);
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( type == 'l' && !strcmp(&str[n], "em") ) {
etab[no].em = mchar(&str[l]);
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( ( type == 'e' || type == 'l' ) && !strcmp(&str[n], "sq") ) {
etab[no].sq = mchar(&str[l]);
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( !strcmp(&str[n], "soe") ) {
etab[no].flg |= 0004;
} else if ( type == 'c' && !strcmp(&str[n], "roe") ) {
if (str[l]) {
etab[no].roe = atoi(&str[l]);
} else {
etab[no].roe = 3 ;
}
} else if ( type == 'c' && !strcmp(&str[n], "roedel") ) {
etab[no].roedel = atoi(&str[l]);
} else if ( !strcmp(&str[n], "nomark") ) {
etab[no].flg &= ~0400;
} else if ( !strcmp(&str[n], "max") ) {
etab[no].mr = strtol(&str[l], (char **)NULL, 10);
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( !strcmp(&str[n], "errmax") ) {
etab[no].mer = atoi(&str[l]);
} else if ( !strcmp(&str[n], "tpar") ) {
if ( !rp )
tn = atoi(&str[l]);
} else if ( !strcmp(&str[n], "parallel") ) {
if ( !rp ) /* this is a command-line only option */
etab[no].ps = (unsigned int) atoi(&str[l]);
} else if ( ( type == 'c' || type == 'p' ) && !strcmp(&str[n], "loaders") ) {
if ( !rp ) /* this is a command-line only option */
etab[no].nloader = (unsigned int) atoi(&str[l]);
} else if ( !strcmp(&str[n], "bpwc") ) {
etab[no].bpwc = (unsigned int) atoi(&str[l]);
} else if ( !strcmp(&str[n], "bpc") ) {
etab[no].bpc = (unsigned int) atoi(&str[l]);
} else if ( type == 'e' && !strcmp(&str[n], "multi") ) {
etab[no].flg |= 01000;
} else if ( type == 'l' && !strcmp(&str[n], "skip") ) {
etab[no].k = atoi(&str[l]);
} else if ( type == 'l' && !strcmp(&str[n], "fieldtrunc") ) {
etab[no].fldtr = (char)atoi(&str[l]);
if ( etab[no].fldtr > 5 ) {
etab[no].fldtr = 0;
fprintf(stderr, "odb [parseopt(%d)] - Invalid fieldtrunc (%s) reset to 0\n",
__LINE__, &str[l] );
}
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if (!strcmp(&str[n], "ns") ) {
etab[no].ns = &str[l];
etab[no].nl = (unsigned int ) strlen( etab[no].ns );
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( ( type == 'e' || type == 'd' ) && !strcmp(&str[n], "es") ) {
etab[no].es = &str[l];
etab[no].el = (unsigned int ) strlen( etab[no].es );
} else if ( ( type == 'l' || type == 'c' ) && !strcmp(&str[n], "norb") ) {
etab[no].flg2 |= 0002;
} else if ( ( type == 'c' || type == 'l' ) && !strcmp(&str[n], "truncate") ) {
etab[no].flg |= 0002;
etab[no].lstat = 4;
} else if ( ( type == 'c' || type == 'l' ) && !strcmp(&str[n], "ifempty") ) {
etab[no].flg |= 0200000;
etab[no].lstat = 4;
} else if ( type == 'l' && !strcmp(&str[n], "full") ) {
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( type != 'l' && !strcmp(&str[n], "uncommitted") ) {
etab[no].flg |= 0001;
} else if ( type == 'e' && !strcmp(&str[n], "gzip") ) {
etab[no].flg |= 0020;
if ( str[l] ) {
if ( str[l] >= '0' && str[l] <= '9' ) {
etab[no].gzlev = (int) ( str[l] - '0' ) ;
} else {
fprintf(stderr, "odb [parseopt(%d)] - Invalid gzip level (%s) reset to default\n",
__LINE__, &str[l] );
etab[no].gzlev = Z_DEFAULT_COMPRESSION ;
}
} else {
etab[no].gzlev = Z_DEFAULT_COMPRESSION ;
}
} else if ( ( type == 'c' || type == 'e' || type == 'd' ) && !strncmp(&str[n], "trim", 4) ) {
etab[no].flg |= 0400000000;
if ( str[n+4] == '+' )
etab[no].flg2 |= 040000000 ;
} else if ( ( type == 'c' || type == 'e' || type == 'd' ) && !strncmp(&str[n], "rtrim", 5) ) {
etab[no].flg |= 02000000 ;
if ( str[n+5] == '+' ) {
etab[no].flg |= 04000000 ;
}
} else if ( ( type == 'e' || type == 'c' ) && !strcmp(&str[n], "cast") ) {
etab[no].flg |= 01000000000;
} else if ( type == 'e' && !strcmp(&str[n], "binary") ) {
etab[no].flg |= 0100;
} else if ( type != 'l' && !strcmp(&str[n], "splitby") ) {
etab[no].sb = &str[l];
} else if ( type == 'd' && !strcmp(&str[n], "quick") ) {
etab[no].flg2 |= 0010;
} else if ( ( type == 'c' || type == 'p' ) && !strcmp(&str[n], "bind") ) {
if ( !strcmp(&str[l], "auto") ) {
etab[no].flg2 |= 0200000;
} else if ( !strcmp(&str[l], "char") ) {
etab[no].flg2 &= ~0200000; /* auto off */
etab[no].flg2 |= 04000; /* char on */
} else if ( !strcmp(&str[l], "cdef") ) {
etab[no].flg2 &= ~0200000; /* auto off */
etab[no].flg2 &= ~04000; /* char off */
} else {
fprintf(stderr, "odb [parseopt(%d)] - Invalid bind (%s) reset to auto\n",
__LINE__, &str[l] );
etab[no].flg2 |= 0200000;
}
} else if ( type == 'd' && !strcmp(&str[n], "key") ) {
etab[no].pc = 1 ;
p = &str[l];
while ( *p++ )
if ( *p == ',' )
etab[no].pc++;
etab[no].key[0] = p = &str[l] ;
for ( i = 1 ; *p && i < MAX_PK_COLS; p++ ) {
if ( *p == ',' && *(p+1) != ':' ) {
*p++ = '\0';
etab[no].key[i++] = p;
}
}
} else if ( type == 'd' && !strcmp(&str[n], "output") ) {
etab[no].run = &str[l];
} else if ( type == 'd' && !strcmp(&str[n], "print") ) {
etab[no].flg &= ~016000000000; /* reset default flags (IDC) */
p = &str[l];
if ( *p && l < j ) {
do {
switch ( *p ) {
case 'c': /* Print 'C'hanged records */
case 'C':
etab[no].flg |= 010000000000;
break;
case 'd': /* Print 'D'eleted source records */
case 'D':
etab[no].flg |= 04000000000;
break;
case 'i': /* Print 'I'nserted target records */
case 'I':
etab[no].flg |= 02000000000;
break;
default: /* invalid character */
fprintf(stderr, "odb [parseopt(%d)] - Invalid print diff option \'%c\'\n",
__LINE__, *p );
}
} while ( *++p ) ;
}
} else if ( type != 'l' && !strcmp(&str[n], "pwhere") ) {
etab[no].map = &str[l];
} else if ( type == 'e' && !strcmp(&str[n], "gzpar") ) {
etab[no].gzp = &str[l];
} else if ( type == 'e' && !strcmp(&str[n], "pcn") ) {
etab[no].flg |= 0200;
} else if ( ( type == 'l' || type == 'e' ) && !strcmp(&str[n], "iobuff") ) {
etab[no].flg2 |= 0400000000 ; /* iobuff has been set */
switch(str[l]){
case 'k':
case 'K':
etab[no].iobuff = (size_t)( 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case 'm':
case 'M':
etab[no].iobuff = (size_t)( 1024 * 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
etab[no].iobuff = (size_t)strtol(&str[l], (char **)NULL, 10);
break;
default:
fprintf(stderr, "odb [parseopt(%d)] - Invalid iobuff \'%s\'\n",
__LINE__, &str[l] );
no = tn = 0; /* reset tn */
return(1);
}
} else if ( !strcmp(&str[n], "rows") ) {
switch(str[l]){
case 'k':
case 'K':
etab[no].rbs = (size_t)( 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case 'm':
case 'M':
etab[no].rbs = (size_t)( 1024 * 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
etab[no].r = (size_t)strtol(&str[l], (char **)NULL, 10);
break;
default:
fprintf(stderr, "odb [parseopt(%d)] - Invalid rows \'%s\'\n",
__LINE__, &str[l] );
no = tn = 0; /* reset tn */
return(1);
}
} else if ( ( type == 'l' || type == 'e' ) && !strcmp(&str[n], "buffsz") ) {
switch(str[l]){
case 'k':
case 'K':
etab[no].buffsz = (size_t)( 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case 'm':
case 'M':
etab[no].buffsz = (size_t)( 1024 * 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
etab[no].buffsz = strtol(&str[l], (char **)NULL, 10) ;
break;
default:
fprintf(stderr, "odb [parseopt(%d)] - Invalid buffsz \'%s\'\n",
__LINE__, &str[l] );
no = tn = 0; /* reset tn */
return(1);
}
#ifdef HDFS
} else if ( !strcmp(&str[n], "hblock") ) {
switch(str[l]){
case 'k':
case 'K':
etab[no].hblock = (tSize)( 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case 'm':
case 'M':
etab[no].hblock = (tSize)( 1024 * 1024 * strtol(&str[l+1], (char **)NULL, 10) );
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
etab[no].hblock = (tSize)strtol(&str[l], (char **)NULL, 10) ;
break;
default:
fprintf(stderr, "odb [parseopt(%d)] - Invalid hblock \'%s\'\n",
__LINE__, &str[l] );
no = tn = 0; /* reset tn */
return(1);
}
if ( etab[no].r <=0 && etab[no].rbs <=0 ) {
fprintf(stderr, "odb [parseopt(%d)] - Warning: invalid parameter \"%s\"; rows set to 100\n",
__LINE__, &str[n]);
etab[no].r = 100;
}
#endif
} else if ( (type == 'e' || type == 'l') && !strcmp(&str[n], "maxlen") ) {
etab[no].Omaxl = (SQLULEN)atoi(&str[l]);
} else if ( ( type == 'c' || type == 'l' || type == 'p' ) && !strcmp(&str[n], "commit") ) {
if ( !strcmp(&str[l], "auto") ) {
etab[no].cmt = 0;
} else if ( !strcmp(&str[l], "end") ) {
etab[no].cmt = -1;
} else if ( str[l] == 'x' && ( str[l+1] >= '0' && str[l+1] <= '9' ) ) {
etab[no].flg2 |= 0001 ;
etab[no].cmt = atoi(&str[l+1]);
} else {
etab[no].cmt = atoi(&str[l]);
}
} else if ( ( type == 'c' || type == 'l' ) && !strcmp(&str[n], "direct") ) {
etab[no].flg |= 040000000;
} else if ( ( type == 'c' || type == 'e' ) && !strcmp(&str[n], "ucs2toutf8") ) {
f |= 0400000000; /* set global flag ucs2toutf8 */
if ( !strcmp(&str[l], "skip") ) {
etab[no].bucs2 = 0;
} else if ( !strcmp(&str[l], "force") ) {
etab[no].bucs2 = 1;
} else if ( !strcmp(&str[l], "cpucs2") ) {
etab[no].bucs2 = 2;
} else if ( !strcmp(&str[l], "qmark") ) {
etab[no].bucs2 = 3;
} else if ( str[l] ) {
} else if ( str[l] ) {
fprintf(stderr, "odb [parseopt(%d)] - Warning: invalid ucs2toutf8 parameter \"%s\"; reset to \"skip\"\n",
__LINE__, &str[l] );
}
etab[no].flg |= 010000000;
etab[no].flg2 |= 010000; /* forcing wide char */
} else if ( type == 'l' && !strcmp(&str[n], "show") ) {
etab[no].flg2 |= 0020;
etab[no].flg |= 01000000 ; /* complex load. Use Oload */
} else if ( ( type == 'e' || type == 'c' ) && !strcmp(&str[n], "cols") ) {
if ( str[l] == '-' ) {
etab[no].flg2 |= 0040 ;
etab[no].cols = &str[l+1];
} else {
etab[no].cols = &str[l];
}
} else if ( type == 'p' && !strcmp(&str[n], "tgtsql") ) {
etab[no].ns = &str[l];
} else if ( type == 'd' && !strcmp(&str[n], "odad") ) {
etab[no].flg2 |= 01000000 ;
#ifdef XML
} else if ( type == 'l' && !strcmp(&str[n], "xmltag") ) {
if ( str[l] == '+' ) { /* xml fields as attributes */
etab[no].flg2 |= 02000000 ;
etab[no].xrt = &str[l+1] ;
} else { /* xml fields as text elements */
etab[no].xrt = &str[l] ;
}
} else if ( type == 'l' && !strcmp(&str[n], "xmlord") ) {
etab[no].flg2 |= 010000000 ;
} else if ( type == 'l' && !strcmp(&str[n], "xmldump") ) {
etab[no].flg2 |= 0200000000 ;
#endif
} else if ( type == 'e' && !strcmp(&str[n], "xml") ) {
etab[no].flg2 |= 04000000 ;
etab[no].lstat |= 0001 ;
} else if ( ( type == 'e' || type == 'l' ) && !strcmp(&str[n], "xmltype") ) {
if ( !strcmp(&str[l], "attr") ) {
etab[no].flg2 |= 02000000;
} else if ( !strcmp(&str[l], "text") ) {
etab[no].flg2 &= ~02000000;
} else {
fprintf(stderr, "odb [parseopt(%d)] - Warning: invalid xmltype \"%s\"; reset to \"text\"\n",
__LINE__, &str[l]);
etab[no].flg2 &= ~02000000;
}
} else if ( type == 'c' && !strcmp(&str[n], "seq") ) {
p = &str[l];
etab[no].seqp = (unsigned int)strtol(p, (char **)NULL, 10);
while ( *p && *p != ',' && *p != ':' ) p++ ;
if ( p )
etab[no].seq = (unsigned long)strtol(++p, (char **)NULL, 10);
} else if ( type == 'c' && !strcmp(&str[n], "seqstart") ) {
etab[no].seq = (unsigned long long int)strtol(&str[l], (char **)NULL, 10);
} else if ( (type == 'l' || type == 'c') && !strcmp(&str[n], "loadcmd") ) {
//etab[no].loadcmd = &str[l];
strncpy(etab[no].loadcmd, &str[l], 2);
etab[no].loadcmd[2] = '\0';
} else if (type == 'l' && !strcmp(&str[n], "jsonkey")) {
fprintf(stderr, "odb [parseopt(%d)] - Warning: this function is not fully tested \"%s\"\n", __LINE__, &str[n]);
etab[no].jsonKey = &str[n];
}
else {
fprintf(stderr, "odb [parseopt(%d)] - Error: unknown parameter \"%s\"\n",
__LINE__, &str[n]);
no = tn = 0; /* reset tn */
return (1);
}
o = 0; /* now look for a param name */
}
if ( eol )
break;
}
if ( rp ) /* if "remember position" is set */
pos = j + eol; /* save string position */
return(0);
}
#ifdef HDFS
/* parsehdfs:
* parse HDFS src/tgt string "host,port,user.file" and fill variables hdfshost, hdfsport,hdfsuser
* in the execution table returning the pointer to "file"
*
* eid(I): execution table index
* hdfs(I): string to parse
* return: pointer to file name
*/
static char *parsehdfs(int eid, char *hdfs)
{
unsigned int i = 0 ; /* loop variable */
char *s = hdfs ; /* bookmark */
unsigned int uf = 0; /* user string flag: 1=present, 0=not present */
/* look for host */
for ( i = 0 ; hdfs[i] && hdfs[i] != ',' ; i++ ) ;
if ( hdfs[i] == ',' ) {
hdfs[i] = '\0' ; /* replace ',' with NULL to terminate hdfshost */
etab[eid].hdfshost = s; /* save hdfshost */
} else {
return ( 0 ) ;
}
/* look for port */
for ( s = &hdfs[++i] ; hdfs[i] && hdfs[i] != ',' && hdfs[i] != '.' ; i++ ) ;
switch ( hdfs[i] ) {
case ',' :
uf = 1 ; /* optional user string present */
/* FALLTHRU */
case '.':
hdfs[i] = '\0' ; /* replace ',' with NULL to terminate hdfsport */
etab[eid].hdfsport = (tPort)atoi(s); /* save hdfsport */
break;
default:
return ( 0 ) ;
}
/* look for user */
if ( uf ) {
for ( s = &hdfs[++i] ; hdfs[i] && hdfs[i] != '.' ; i++ ) ;
if ( hdfs[i] == '.' ) {
hdfs[i] = '\0' ; /* replace '.' with NULL to terminate hdfsuser */
etab[eid].hdfsuser = s; /* save hdfsuser */
} else {
return ( 0 ) ;
}
}
return(&hdfs[++i]);
}
#endif
/* excol:
* expand column table column names for :cast, :cols and :trim options
*
* return: pointer to the string containing the columns separated by comma
*/
static void excol(int eid, SQLHDBC *Ocn)
{
char *cols = 0; /* pointer to the exclude column */
char *p = 0; /* pointer to single exclude col name in col */
size_t sz = LINE_CHUNK; /* size of the memory allocated for the included column */
size_t used = 0 ; /* space used for the included columns */
size_t clen = 0; /* single exclkuded column name length */
unsigned int flg = 0, /* 1 if a tdesc structure is allocated */
i = 0, /* loop variable */
j = 0; /* loop variable */
if ( !etab[eid].td ) { /* get tdesc structure for this eid if not available */
if ( ( i = Otcol(eid, Ocn) ) <= 0 ) {
fprintf(stderr, "odb [excol(%d)] - Error analyzing source table %s\n",
__LINE__, etab[eid].src);
goto excol_exit;
}
flg = 1 ;
} else {
i = etab[no].cmt ;
}
if ( ( cols = malloc ( sz ) ) == (void *) NULL ) {
fprintf(stderr, "odb [excol(%d)] - Error Allocating LINE_CHUNK memory for colnames: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto excol_exit;
}
cols[0] = '\0' ; /* clean cols */
for ( j = 0 ; j < i ; j++ ) {
if ( ( sz - used ) < MAXCOL_LEN ) { /* We do need more memory */
sz += LINE_CHUNK;
if ( ( cols = realloc ( cols, sz ) ) == (void *)NULL ) {
fprintf(stderr, "odb [Oload(%d)] - Error re-allocating memory for colnames: [%d] %s\n",
__LINE__, errno, strerror(errno));
goto excol_exit;
}
}
if ( etab[eid].flg & 01406000000 ) { /* cast or trim */
if ( j )
used += strmcat ( cols, ",", sz, 0);
switch ( etab[eid].td[j].Otype ) {
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_CHAR:
case SQL_WCHAR:
if ( etab[eid].flg2 & 040000000 ) {
used += snprintf(cols + used, sz, "TRIM(%s)", (char *)etab[eid].td[j].Oname);
} else if ( etab[eid].flg & 04000000 ) {
used += snprintf(cols + used, sz, "RTRIM(%s)", (char *)etab[eid].td[j].Oname);
} else {
used += strmcat ( cols, (char *) etab[eid].td[j].Oname, sz, 0);
}
break;
default:
if ( etab[eid].flg & 01000000000 ) { /* cast */
used += snprintf( ( cols + used ), sz, "CAST(%s AS VARCHAR(%u))",
(char *)etab[eid].td[j].Oname, (unsigned int)etab[eid].td[j].Osize);
} else {
used += strmcat ( cols, (char *) etab[eid].td[j].Oname, sz, 0);
}
break;
}
} else {
clen = strlen((char *)etab[eid].td[j].Oname) ;
if ( ( p = strstr ( etab[eid].cols, (char *)etab[eid].td[j].Oname ) ) == ( char * ) NULL ) {
/* column included: cat its name */
if ( used )
strmcat(cols, ",", sz, 0 );
used += strmcat(cols, (char *)etab[eid].td[j].Oname, sz, 0 ) + 1;
} else { /* Colname found */
if ( p == etab[eid].cols && ( *(p + clen) == ',' || !*(p + clen) ) ) {
/* column excluded: do nothing */
} else if ( *( p - 1) == ',' && ( *(p + clen) == ',' || !*(p + clen) ) ) {
/* column excluded: do nothing */
} else {
/* column included: cat its name */
if ( used )
strmcat(cols, ",", sz, 0 );
used += strmcat(cols, (char *)etab[eid].td[j].Oname, sz, 0 ) + 1;
}
}
}
if ( flg )
free ( etab[eid].td[j].Oname );
}
excol_exit:
if ( flg )
free(etab[eid].td);
etab[eid].cols = cols ;
}
/* strmtime:
* format time in HH:MM:SS.000 starting from SSS.000
*
* input: secs seconds (double)
* input: str string to write to (14 bytes)
* return: formatted string
*/
static char *strmtime ( double secs, char *str )
{
unsigned long ts;
unsigned int ms;
unsigned char flg = 0;
if ( secs < 0) {
flg = 1;
secs *= -1;
}
ts = (unsigned long)((secs+0.0005) * 1000) ;
ms = ts % 1000 ;
ts /= 1000 ;
sprintf(str, "%s%02lu:%02lu:%02lu.%03u",
flg ? "-" : "", ts / 3600, ts % 3600 / 60, ts % 60, ms);
return(str);
}
/* dumpbuff:
* dump ODBC (or any other) buffer in ASCII/HEX format
*
* input: tid thread id
* input: buff pointer to buffer
* input: buffl buffer length
* input: v verbosity ( 0 / 1 )
*
* return: void
*/
static void dumpbuff ( FILE * fd, int eid, unsigned char *buff, size_t buffl, unsigned int v )
{
size_t i = 0; /* loop variable */
unsigned int j = 0; /* loop variable */
if ( v )
fprintf(fd, "[%d] Dumping %zu bytes\n", etab[eid].id, buffl ) ;
for ( i = 0 ; i < buffl ; i += 16 ) {
fprintf ( fd, "%06zu: " , i );
for ( j = 0 ; j < 16 ; j++ ) {
if ( ( i + j ) < buffl )
fprintf ( fd, "%02x ", buff[i+j]);
else
fputs ( " " , fd ) ;
}
fputc ( ' ' , fd );
for ( j = 0 ; j < 16 ; j++ )
if ( ( i + j ) < buffl )
fputc ( isprint ( buff[i+j] ) ? buff[i+j] : '.', fd );
fputc ('\n' , fd );
}
}
#ifdef ODB_PROFILE
/* tspdiff:
* returns nanoseconds between two struct timespec(s). Used to profile odb execution
*
* input: start struct timespac pointer
* input: end struct timespac pointer
*
* return: unsigned long nanoseconds
*/
unsigned long tspdiff ( struct timespec *start, struct timespec *end )
{
return ((((unsigned long)end->tv_sec * 1000000000) + end->tv_nsec) -
(((unsigned long)start->tv_sec * 1000000000) + start->tv_nsec));
}
#endif
/* ucs2toutf8:
* Convert UCS-2 to UTF-8 encoding: both UCS-2 string and its length will be overwritten. Buffers should be big enough
*
* input: 'str': pointer to UCS-2 encoded string
* input: 'len': pointer to UCS-2 encoded string length
* input: 'bsz': buffer size
* input: 'bu2': bad ucs2 conversion management
*
* return: 0=conversion OK, 1=conversion with errors
*/
static int ucs2toutf8 ( SQLCHAR *str, SQLLEN *len, SQLULEN bs, int bu2 )
{
uint16_t u2 = 0 ; /* UCS-2 character container */
SQLLEN l2 = *len ; /* UCS-2 string length */
SQLLEN l8 = 0 ; /* UTF-8 string length */
SQLCHAR *u8 = 0 ; /* UTF-8 string buffer */
SQLCHAR buff[256]; /* avoid using malloc/free for short fields */
SQLLEN i = 0; /* loop variable */
unsigned int fb = 0 ; /* free buffer flag */
int ret = 0 ; /* return value */
if ( l2 == 0 || l2 ==SQL_NULL_DATA )
return (ret); /* nothing to convert for empty or NULL strings */
if ( bs > 256 ) {
if ( ( u8 = malloc ( (size_t)bs ) ) == (void *) NULL ) {
fprintf(stderr, "odb [ucs2toutf8(%d)] - Error Allocating %zu bytes of memory\n", __LINE__, (size_t)bs);
return(1);
}
fb = 1 ;
} else {
u8 = &buff[0] ;
}
for ( i = 0 ; i <= ( l2 - 2 ) ; i += 2 ) {
u2 = str[i+1] << 8 | str[i] ; /* get and swap next two bytes from UCS-2 string */
if ( u2 < 0x80 ) { /* single byte UTF-8 (ASCII 7 bits) */
u8[l8++] = (SQLCHAR)u2 ;
} else if ( u2 >= 0x80 && u2 < 0x800 ) { /* double byte UTF-8 */
u8[l8++] = ( u2 >> 6 ) | 0xC0 ;
u8[l8++] = ( u2 & 0x3F ) | 0x80 ;
} else if (u2 >= 0x800 && u2 < 0xFFFF) { /* three bytes UTF-8 */
if (u2 >= 0xD800 && u2 <= 0xDFFF) { /* Bad UCS-2 encoding: surrogate pair */
ret = 1; /* set return value */
fprintf(stderr, "odb [ucs2toutf8(%d)] - Bad UCS-2 character 0x%x found in position %ld\n", __LINE__, u2, (long)i);
switch ( bu2 ) {
case 0: /* skip */
continue ;
case 1: /* force */
break;
case 2: /* cpucs2 */
if ( fb )
free ( u8 ) ;
return(ret);
case 3: /* qmark */
u8[l8++] = '?' ;
continue ;
}
}
u8[l8++] = ( u2 >> 12) | 0xE0;
u8[l8++] = ( ( u2 >> 6 ) & 0x3F) | 0x80;
u8[l8++] = ( u2 & 0x3F) | 0x80;
/*} else if (u2 >= 0x10000 && u2 < 0x10FFFF) { four bytes UTF-8
u8[l8++] = 0xF0 | ( u2 >> 18 );
u8[l8++] = 0x80 | ( ( u2 >> 12 ) & 0x3F ) ;
u8[l8++] = 0x80 | ( ( u2 >> 6) & 0x3F ) ;
u8[l8++] = 0x80 | ( u2 & 0x3F ) ;*/
}
}
MEMCPY ( str , u8 , (size_t)l8 ) ; /* overwrite UCS-2 buffer with UTF-8 encoded string */
*len = l8 ; /* update string length */
if ( fb )
free ( u8 ) ; /* free memory if allocated */
return(ret);
}
/* addGlobalPointer:
* add ptr globalPointers buffer
*
* return: no return, exit on error
*/
static void addGlobalPointer(void *ptr)
{
if (nGlobalPointers >= (naGlobalPointers * ETAB_CHUNK - 2)) { /* need new memory */
if ((globalPointers = realloc(globalPointers, ++naGlobalPointers*ETAB_CHUNK * sizeof(void *))) == (void *)NULL) {
fprintf(stderr, "%s [etabnew(%d)] - Error allocating %dth block of etab[] memory: [%d] %s\n",
__FILE__, __LINE__, naGlobalPointers, errno, strerror(errno));
exit(EX_OSERR);
}
}
globalPointers[nGlobalPointers++] = ptr;
}
/* is_valid_numeric:
* check if the string is valid numeric
*
* Input: str: string to be validate.
* Input: n: length of str
*
* return: if str is valid numeric return 1 else return 0
*/
static int is_valid_numeric(const char* str, size_t n) {
int s = 1;
for (size_t i = 0; i < n; ++i) {
switch (s) {
case 1: // expect a sign or digit
if (str[i] == '+' || str[i] == '-') s = 2;
else if (isdigit(str[i])) s = 3;
else return 0;
break;
case 2: // expect a digit
if (!isdigit(str[i])) return 0;
s = 3;
break;
case 3: // expect option digit or dot or 'e/E'
if (str[i] == '.') s = 4;
else if (str[i] == 'e' || str[i] == 'E') s = 6;
else if (!isdigit(str[i])) return 0;
break;
case 4: // expect digit after a dot
if (!isdigit(str[i])) return 0;
s = 5;
break;
case 5: // now expect optional digit or 'e/E'
if (str[i] == 'e' || str[i] == 'E') s = 6;
else if (!isdigit(str[i])) return 0;
break;
case 6: // expect a sign or digit after 'e/E'
if (str[i] == '+' || str[i] == '-') s = 7;
else if (isdigit(str[i])) s = 8;
else return 0;
break;
case 7: // expect a digit after a sign after 'e/E'
if (!isdigit(str[i])) return 0;
s = 8;
break;
case 8: // expect optional digit
if (!isdigit(str[i])) return 0;
break;
default:
return 0;
}
}
if (s == 3 || s == 4 || s == 5 || s == 8) return 1;
return 0;
}
/* usagexit:
* print usage message and exit
*
* return: void
*/
static void usagexit()
{
/* Ruler 1 2 3 4 5 6 7 8*/
fprintf(stderr, "%s\nBuild: %s [%s %s]\n", odbid, ODBBLD, __DATE__, __TIME__);
fprintf(stderr, " -h: print this help\n"
" -version: print odb version and exit\n"
" -lsdrv: list available drivers @ Driver Manager level\n"
" -lsdsn: list available Data Sources\n"
"Connection related options. You can connect using either:\n"
" -u User: (default $ODB_USER variable)\n"
" -p Password: (default $ODB_PWD variable)\n"
" -d Data_Source_Name: (default $ODB_DSN variable)\n"
" -ca Connection_Attributes (normally used instead of -d DSN)\n"
" -U sets SQL_TXN_READ_UNCOMMITTED isolation level\n"
" -ndsn [+]<number>: adds 1 to <number> to DSN\n"
" -nps <nbytes>[:<nbytes>]: specify source[:target] network packet size\n"
"SQL interpreter options:\n"
" -I [$ODB_INI SECTION]: interactive mode shell\n"
" -noconnect: do not connect on startup\n"
"General options:\n"
" -q [cmd|res|all|off]: do not print commands/results/both\n"
" -i [TYPE[MULT,WIDE_MULT]:CATALOG.SCHEMA[.TABLE]]: lists following object types:\n"
" (t)ables, (v)iews, s(y)nonyns, (s)chemas, (c)atalogs, syst(e)m tables\n"
" (l)ocal temp, (g)lobal temp, (m)at views, (M)mat view groups, (a)lias\n"
" (A)ll object types, (T)table desc, (D)table DDL, (U) table DDL with multipliers\n"
" -r #rowset: rowset to be used insert/selects (default 100)\n"
" -soe: Stop On Error (script execution/loading task)\n"
" -N : Null run. Doesn't SQLExecute statements\n"
" -v : be verbose\n"
" -vv : Print execution table\n"
" -noschema : do not use schemas: CAT.OBJ instead of CAT.SCH.OBJ\n"
" -nocatalog : do not use catalogs: SCH.OBJ instead of CAT.SCH.OBJ\n"
" -nocatnull : like -nocatalog but uses NULL instead of empty CAT strings\n"
" -ucs2toutf8 : set UCS-2 to UTF-8 conversion in odb\n"
" -var var_name var_value: set user defined variables\n"
" -ksep char/code: Thousands Separator Character (default \',\')\n"
" -dsep char/code: Decimal Separator Character (default \'.\')\n"
"SQL execution options [connection required]:\n"
" -x [#inst:]'command': runs #inst (default 1) command instances\n"
" -f [#inst:]'script': runs #inst (default 1) script instances\n"
" -P script_path_regexp: runs in parallel scripts_path_regexp\n"
" if script_path_regexp ends with / all files in that dir\n"
" -S script_path_regexp: runs serially scripts_path_regexp\n"
" if script_path_regexp ends with / all files in that dir\n"
" -L #loops: runs everything #loops times\n"
" -T max_threads: max number of execution threads\n"
" -dlb: use Dynamic Load Balancing\n"
" -timeout #seconds: stops everything after #seconds (no Win32)\n"
" -delay #ms: delay (ms) before starting next thread\n"
" -ldelay #ms: delay (ms) before starting next loop in a thread\n"
" -ttime #ms[:ms]: delay (ms) before starting next command in a thread\n"
" random delay if a [min:max] range is specified\n"
" -F #records: max rows to fetch\n"
" -c : output in csv format\n"
" -b : print start time in the headers when CSV output\n"
" -pcn: Print Column Names\n"
" -plm: Print Line Mode\n"
" -fs char/code: Field Sep <char> ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" -rs char/code: Rec Sep <char> ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" -sq char/code: String Qualifier (default none)\n"
" -ec char/code: Escape Character (default \'\\\')\n"
" -ns nullstring: print nullstring when a field is NULL\n"
" -trim: Trim leading/trailing white spaces from txt cols\n"
" -drs: describe result set (#cols, data types...) for each Q)\n"
" -hint: do not remove C style comments (treat them as hints)\n"
" -casesens: set case sensitive DB\n"
" -Z : shuffle the execution table randomizing Qs start order\n");
fprintf(stderr, "Data loading options [connection required]:\n"
" -l src=[-]file:tgt=table[:map=mapfile][:fs=fieldsep|:mcfs=fieldsep][:rs=recsep|:mcrs=recsep]\n"
" [:soe][:skip=linestoskip][:ns=nullstring][:ec=eschar][:sq=stringqualifier]\n"
" [:pc=padchar][:em=embedchar][:errmax=#max_err][:commit=auto|end|#rows|x#rs]\n"
" [:rows=#rowset][:norb][:full][:max=#max_rec][:truncate][:show][:bpc=#][:bpwc=#]\n"
" [:nomark][:parallel=number][:iobuff=#size][:buffsz=#size]][:fieldtrunc={0-4}]\n"
" [:pre={@sqlfile|sqlcmd}][:post={@sqlfile|sqlcmd}][:ifempty]\n"
" [:direct][:bad=[+]badfile][:tpar=#tables][:maxlen=#bytes][:time][:loadcmd=IN|UP|UL]\n"
#ifdef XML
" [:xmltag=[+]element][:xmlord][:xmldump]\n"
#endif
" Defaults/notes:\n"
" * src file: local file or {hdfs,mapr}[@host,port[,huser]].<HDFS_PATH>\n"
" * fs: default ','. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * mcfs: default use fs as field separator. If mcfs was set then use mcfs as field\n"
" separator. mcfs means multi characters separator and support escape sequence:\n"
" \\n: new line\n"
" \\r: return\n"
" \\t: tab\n"
" \\xhh: ascii code in hex, example: \\x41 is 'A'\n"
" * rs: default '\\n'. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * mcrs: default use rs as record separator. If mcrs was set then use mcrs as record\n"
" separator. Please refer to mcfs"
" * ec: default '\\'. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * pc: no default. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * direct: only for Vertica databases\n"
" * bpc: default 1,bpwc: default 4 \n"
" * loadcmd: default IN. only for Trafodion databases\n"
"Data extraction options [connection required]:\n"
" -e {src={table|-file}|sql=<custom sql>}:tgt=[+]file[:pwhere=where_cond]\n"
" [:fs=fieldsep|:mcfs=fieldsep][:rs=recsep|:mcrs=recsep][:sq=stringqualifier]\n"
" [:ec=escape_char][:soe][:ns=nullstring][:es=emptystring][:rows=#rowset][:nomark]\n"
" [:binary][:bpc=#][:bpwc=#][:max=#max_rec][:[r]trim[+]][:cast][:multi][:parallel=number]\n"
" [:gzip[=lev]][:splitby=column][:uncommitted][:iobuff=#size][:hblock=#size][:ucs2toutf8]\n"
" [:pre={@sqlfile|sqlcmd}[:mpre={@sqlfile|sqlcmd}[:post={@sqlfile|sqlcmd}]\n"
" [:tpar=#tables][:time][:cols=[-]columns]][:maxlen=#bytes][:xml]\n"
" Defaults/notes:\n"
" * tgt file: local file or {hdfs,mapr}.[@host,port[,huser]].<HDFS_PATH>\n"
" * fs: default ','. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * mcfs: default use fs as field separator. If mcfs was set then use mcfs as field\n"
" separator. mcfs means multi characters separator and support escape sequence:\n"
" \\n: new line\n"
" \\r: return\n"
" \\t: tab\n"
" \\xhh: ascii code in hex, example: \\x41 is 'A'\n"
" * rs: default '\\n'. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * mcrs: default use rs as record separator. If mcrs was set then use mcrs as record\n"
" separator. Please refer to mcfs"
" * ec: default '\\'. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * sq: no default. Also <ASCII_dec> 0<ASCII_OCT> X<ASCII_HEX>\n"
" * gzip compression level between 0 and 9\n"
" * bpc: default 1,bpwc: default 4 \n");
fprintf(stderr, "Data copy options [connection required]:\n"
" -cp src={table|-file:tgt=schema[.table][pwhere=where_cond][:soe][:roe=#][:roedel=#ms]\n"
" [:truncate][:rows=#rowset][:nomark][:max=#max_rec][:bpc=#][:bpwc=#][:[r]trim[+]]\n"
" [:parallel=number][:errmax=#max_err][:commit=auto|end|#rows|x#rs][:time][:cast]\n"
" [:direct][:uncommitted][:norb][:splitby=column][:pre={@sqlfile|sqlcmd}]\n"
" [:post={@sqlfile|sqlcmd}][:mpre={@sqlfile|sqlcmd}][:ifempty]\n"
" [:loaders=#loaders][:tpar=#tables][:cols=[-]columns][:errdmp=file]\n"
" [:sql={sqlcmd|@sqlfile|-file}[:bind=auto|char|cdef][:seq=field#[,start]]\n"
" [:tmpre={@sqlfile|sqlcmd}][:ucs2toutf8=[skip,force,cpucs2,qmark]][:loadcmd=IN|UP|UL]\n"
" Defaults/notes:\n"
" * loaders: default 2 load threads for each 'extractor'\n"
" * direct: only work if target database is Vertica\n"
" * ucs2toutf8: default is 'skip'\n"
" * roe: default 3 if no arguments\n"
" * bpc: default 1,bpwc: default 4 \n"
"Data pipe options [connection required]:\n"
" -pipe sql={sqlcmd|@sqlscript|-file}:tgtsql={@sqlfile|sqlcmd}[:soe]\n"
" [:rows=#rowset][:nomark][:max=#max_rec][:bpc=#][:bpwc=#][:errdmp=file]\n"
" [:parallel=number][:errmax=#max_err][:commit=auto|end|#rows|x#rs][:time]\n"
" [:pre={@sqlfile|sqlcmd}][:post={@sqlfile|sqlcmd}]\n"
" [:mpre={@sqlfile|sqlcmd}][:tmpre={@sqlfile|sqlcmd}]\n"
" [:loaders=#loaders][:tpar=#tables][:bind=auto|char|cdef]\n"
" Defaults/notes:\n"
" * loaders: default 1 load threads for each extraction thread\n"
" * bpc: default 1,bpwc: default 4 \n"
"Table diff options [connection required]:\n"
" -diff src={table|-file}:tgt=table[:key=columns][:output=[+]file][:pwhere=where_cond]\n"
" [:pwhere=where_cond][:nomark][:rows=#rowset][:odad][:fs=fieldsep][:time][:trim[+]]\n"
" [:rs=recsep][:quick][:splitby=column][:parallel=number][:max=#max_rec]\n"
" [:print=[I][D][C]][:ns=nullstring][:es=emptystring][:bpc=#][:bpwc=#][:uncommitted]\n"
" [:pre={@sqlfile|sqlcmd}][:post={@sqlfile|sqlcmd}][:tpar=#tables]\n"
" Defaults/notes:\n"
" * bpc: default 1,bpwc: default 4 \n"
" * print: default is Inserted Deleted Changed\n");
/* Ruler 1 2 3 4 5 6 7 8*/
exit( EX_OK );
}