/*------v---v--------v----------------------------------------------------------
 * @(#) Mysql test dynamic sql using prepare, bind, execute
 * @(#) cPgmMain     2022-02 by juerg.oehler@ois-net.org
 *------------------------------------------------------------------------------
 * compile: first set your HOST, DB, USER, PWD
 *			CFLAGS =-Wall -g -O2 -D _XOPEN_SOURCE=700 -D _GNU_SOURCE
 *			LDLIBS =-lmysqlclient
 * docs:	use MysqlDynamic -[?|h] to get help
 * refs:	https://dev.mysql.com/doc/c-api/5.7/en/c-api-prepared-statement-type-codes.html
 *			https://mariadb.com/kb/en/mariadb-connector-c/
 *			https://mariadb.com/kb/en/connectorc-types-and-definitions/
 * comment:	-this program has built to find a way to use one dataset for
 *			 internal calculations and database interactions. it must be
 *			 possible prepare cursors and bind them once and execute often.
 *			 this is possible by using pointer arithmethics, often causing
 *			 bad headaches.
 *			-using c-connectors based on documentation is a hazle something like
 *			 experimental programming. library calls end up in errorstate but
 *			 with missing hint to it's cause. implicit conversions such as
 *			 datestring are not documented.
 *			-this program is written in an unusual way <all in one> in order to
 *			 easy share with other programmers.
 * select number, name, stamp, birthtm, price, precise, smally, tiny, epoch, length (x509cert), length (picture) from t_test;
 *------------------------------------------------------------------------------
 * this program is distributed under GPL license in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Library General Public License for more details at
 * free software foundation.
 *
 * (c) Copyright 2022- by OIS-net.org
 ---------------------------------------------------------------------------- */
static char *sccs = "@(#) 1.0.0 - 2022-05-12 - OisTest - jgor";
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <mysql/mysql.h>

/*------------------------------------------------------------------------------
 * declaration / prototypes
 *--------------------------------------------------------------------------- */
#define VERBOSE 1
#define CREATE	2
#define INSERT	4
#define SELECT	8
#define DROP	128

#define HOST "localhost"
#define DB "test"
#define USER "testA"
#define PWD "Atest"
#define StmtCREATE "create table t_test (number integer primary key, name varchar(30), stamp datetime, birthtm datetime(6), price float, precise double, smally smallint, tiny tinyint unsigned, epoch bigint unsigned, x509cert varchar(3072), picture blob(65535)) comment 'test table created by MysqlDynamic'"
#define StmtSEL1 "select number, name from t_test"
#define StmtDROP "drop table t_test"
#define PICTURE "./ch.png"
#define CERT "./cert.pem"

typedef struct s_dbinfo {							// MARIADB_CONNECTION
	char *host;										// HOST
	int port;										// PORT
	char *user;										// USER
} dbinfo;				// see https://mariadb.com/kb/en/mariadb_get_infov
typedef struct test {	// input / output buffer structure
    int number;			// -2147483648 to 2147483647
    char numberI; 		// null indicator
	char name[31];		// for character buffers db fieldsize + 1!
    char nameI;
	char stamp[30];
	char stampI;
	MYSQL_TIME birthtm;
	char birthtmI;
	float price;
	char priceI;
	double precise;		//
	char preciseI;
	short smally;		// -32768 to 32767
	char smallyI;
	unsigned char tiny;	// 0 to 255
	char tinyI;
	unsigned long long epoch; // -2^63 to 2^63-1
	char epochI;
	char *x509cert;
	char x509certI;
	char *picture;
	char pictureI;
	unsigned long pictureL;
} test;
test InpTest[] = {
	{0, 0, "gugus", 0, "1988-10-12T18:43:36", 0,
		{2000, 10, 12, 23, 1, 28}, 0, 0, 1, -0.00000486, 0,
		-12, 0, 0, 1, 922337203685477580, 0, 0, 1, 0, 1, 0
	},
	{ 0, 0, "", 1, "2020-04-30", 0,
		{}, 1, -28.30, 0, -0.00000486, 0,
		0, 1, 128, 0, 1650707701123, 0, 0, 1, 0, 1, 0
	},
	{ 0, 0, "master", 0, "", 1,
		{1854, 2, 12, 9, 13, 8, 234765}, 0, 395.938656, 0, -1, 0,
		32767, 0, 255, 0, -1, 0, 0, 1, 0, 1, 0
	}
};
typedef struct s_stmtb { //generic buffer for statements we prepare and work
	MYSQL_STMT *stmt;
	char *query;
	short nfld;
	int nrow;
	MYSQL_BIND *bind;
	MYSQL_RES *rslt;
	void *BufPara;
	void *BufRslt;
} stmtb;
typedef struct s_pgm {
	int flag;
	MYSQL *con;
	dbinfo dbi;
	stmtb *InsTest;
	stmtb *SelTest;
	unsigned int MysqlErr;
	int status;
} pgm;

test OutTest;	// databuffer, for selected results

void usage (char *);
int loadfile (pgm *, char *, char **, int);
void prthex (char *, int);
int init(pgm *);
int ExecQuery (pgm *, char *); //execute statement without in- or output
int PrepStmt (pgm *, char);
int bindInsTest (pgm *);
int FreeStmt (pgm *, stmtb *);
void finish (pgm *);

/*------------------------------------------------------------------------------
 * begin coding
 *--------------------------------------------------------------------------- */
int main (int argc, char** argv) {
extern char *optarg;
extern int optind;
int c, retc;
pgm PgmHdl = { .flag = 0, .con = 0, .InsTest = 0, .SelTest = 0, .MysqlErr = 0, .status = 0 };

	if (argc < 2) {
		fputs ("err: no options given\n", stderr);
		usage (argv[0]);
		exit (2);
	}
	while ((c = getopt(argc, argv, "?hvcisd")) != -1) {
		switch (c) {
			case 'h':
			case '?':	usage (argv[0]);
						exit (0);
			case 'v':   PgmHdl.flag |= VERBOSE;
						break;
			case 'c':   PgmHdl.flag |= CREATE;	// create table and insert values
						break;
            case 'i':   PgmHdl.flag |= INSERT;	// inserting some content
                        break;
            case 's':   PgmHdl.flag |= SELECT;	// select values
                        break;
            case 'd':   PgmHdl.flag |= DROP;	// drop table
                        break;
            default:    fprintf (stderr, "unknown option <%c>\n", c);
						usage (argv[0]);
                        exit (1);
        }
    }
	if ((argc - optind) > 0 ) {
		fputs ("err: to many parameters, confused\n", stderr);
		usage (argv[0]);
		exit (2);
	}
	if (init (&PgmHdl)) {
		PgmHdl.status = 11;
		finish (&PgmHdl);
	}
	if ((PgmHdl.flag & CREATE) == CREATE) {
		if (ExecQuery (&PgmHdl, StmtCREATE ) < 0) {
			PgmHdl.status = 12;
			finish (&PgmHdl);
		}
		puts ("table t_test created");
	}
	if ((PgmHdl.flag & INSERT) == INSERT) {
		srand(time(NULL));
		if (PrepStmt (&PgmHdl, 'I')) {
			PgmHdl.status = 13;
			finish (&PgmHdl);
		}
		short cnt = (sizeof(InpTest) / sizeof(test)-1);
		short base = (rand() % 100);
		for (; cnt > -1; cnt--) {
			InpTest[cnt].number = (cnt+base);
			if (InpTest[cnt].epoch == -1 && InpTest[cnt].epochI == 0) {
				InpTest[cnt].epoch = (time(NULL) * 1000);
			}
//			printf ("input %d <%s> <%s>\n", InpTest[cnt].number,
//				InpTest[cnt].name, InpTest[cnt].stamp);
			memcpy (PgmHdl.InsTest->BufPara, &InpTest[cnt], sizeof(test));
			test *tst = (test*) PgmHdl.InsTest->BufPara;
			printf ("ins%d: BufPara <%d> name<%s> stamp<%s> epoch<%lld>\n",
				cnt, tst->number, tst->name, tst->stamp, tst->epoch);
#ifdef CERT 
			if (cnt == 1) {
				retc = loadfile (&PgmHdl, CERT , &tst->x509cert, 3072);
				tst->x509certI = retc > 0 ? 0 : 1;
			}
#endif
#ifdef PICTURE 
			if (cnt == 2) {
				retc = loadfile (&PgmHdl, PICTURE , &tst->picture, 65534);
				tst->pictureI = retc > 0 ? 0 : 1;
				tst->pictureL = retc;
			}
#endif
			if (bindInsTest (&PgmHdl)) {
				PgmHdl.status = 14;
				finish (&PgmHdl);
			}
			if (mysql_stmt_execute (PgmHdl.InsTest->stmt)) {
				PgmHdl.MysqlErr = mysql_stmt_errno (PgmHdl.InsTest->stmt);
				if (PgmHdl.MysqlErr == 1062) {
					puts ("duplicate entry, jumped");
					continue;
				}
				fprintf (stderr, "InsTest execute failure err:%d <%s>\n",
					PgmHdl.MysqlErr, mysql_stmt_error(PgmHdl.InsTest->stmt));
				PgmHdl.status = 14;
				finish (&PgmHdl);
			}
		}
		mysql_commit(PgmHdl.con);
	}
	if ((PgmHdl.flag & DROP) == DROP) {
		if (ExecQuery (&PgmHdl, StmtDROP ) < 0) {
			PgmHdl.status = 13;
			finish (&PgmHdl);
		}
		puts ("table t_test droped");
	}
	finish (&PgmHdl);
}

/*------------------------------------------------------------------------------
 * PrepStmt: prepare requested statement
 *   return: 0 or any value on error
 *--------------------------------------------------------------------------- */
int PrepStmt (pgm *pHdl, char what)  {
char verbose;
stmtb *stbuf;
int i;
unsigned long flds;
//MYSQL_FIELD *fld; needed later on for selects

	verbose = ((pHdl->flag & VERBOSE) == VERBOSE);
	if (what == 'I') {
		if (pHdl->InsTest) {
			fputs ("InsTest statement alreay prepared\n", stderr);
			return (-1);
		}
		i = (sizeof (stmtb));
		pHdl->InsTest = malloc (i);
		if (!pHdl->InsTest) {
			fputs ("InsTest can't alloc memory\n", stderr);
			return (-1);
		}
		memset (pHdl->InsTest, 0, i);
		stbuf = pHdl->InsTest;
		stbuf->stmt = mysql_stmt_init (pHdl->con);
		if (!stbuf->stmt) {
			fputs ("InsTest mysql_stmt_init() out of memory\n", stderr);
			return (-1);
		}
		if (verbose) { puts ("InsTest stmt initialyzed"); }
// table test (number integer primary key, name varchar(30), stamp datetime, birthtm datetime, price float, precise double, smally smallint, tiny tinyint unsigned, epoch bigint, x509cert varchar(3072), picture blob(65535))"
		stbuf->query = strdup ("insert into t_test (number, name, stamp, birthtm, price, precise, smally, tiny, epoch, x509cert, picture) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); 
		stbuf->nfld = 11;
		if (mysql_stmt_prepare (stbuf->stmt, stbuf->query, -1)) {
			pHdl->MysqlErr = mysql_stmt_errno (stbuf->stmt);
			fprintf (stderr, "InsTest prepare <%s> failed err:%d <%s>\n",
				stbuf->query, pHdl->MysqlErr, mysql_stmt_error(stbuf->stmt));
			return (-1);
		}
		flds = mysql_stmt_param_count (stbuf->stmt);
		if (flds != stbuf->nfld) {
			fprintf (stderr, "InsTest param field mismatch %ld, expected %d\n",
				flds, stbuf->nfld);
			return (-1);
		}
		i = sizeof(test);
		stbuf->BufPara = (void*) malloc (i);
		if (! stbuf->BufPara) {
			fputs ("InsTst can't alloc memory for BufPara\n", stderr);
			return (-1);
		}
		memset (stbuf->BufPara, 0, i);

		if (verbose) { puts ("InsTest prepared, need binding"); }
	} else if (what == 'S') {
		if (pHdl->SelTest->stmt) {
			fputs ("select statement alreay prepared\n", stderr);
			return (-1);
		}
		pHdl->SelTest = malloc (sizeof (stmtb));
		if (!pHdl->InsTest) {
			fputs ("can't alloc memory for SelTest\n", stderr);
			return (-1);
		}
		pHdl->SelTest->stmt = mysql_stmt_init (pHdl->con);
		if (!pHdl->SelTest->stmt) {
			fputs ("mysql_stmt_init() SelTest, out of memory\n", stderr);
			return (-1);
		}
		if (verbose) { puts ("stmt for selects initialyzed"); }
		unsigned long c = CURSOR_TYPE_READ_ONLY;
		mysql_stmt_attr_set (pHdl->SelTest->stmt, STMT_ATTR_CURSOR_TYPE, &c);
		my_bool b = 1;
		mysql_stmt_attr_set (pHdl->SelTest->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &b);
	} else {
		fprintf (stderr, "do not know what to prepare for <%c>\n", what);
		return (-1);
	}
	return (0);
}

/*------------------------------------------------------------------------------
 * bindInsTest
 *	static or dynamic binding
 *--------------------------------------------------------------------------- */
int bindInsTest(pgm *pHdl) {
char verbose;
stmtb *stbuf;
test *para;
int i;
MYSQL_BIND *bnd;

	verbose = ((pHdl->flag & VERBOSE) == VERBOSE);
	stbuf = pHdl->InsTest;
	if (!stbuf->nfld) {
		fputs ("bindInsTest: no fields\n", stderr);
		return (-1);
	}
	if (stbuf->BufPara) {
		para = (test*) stbuf->BufPara;
	} else {
		fputs ("bindInsTest: no parameter buffer\n", stderr);
		return (-2);
	}
	if (stbuf->bind) {
		if (verbose) {
			puts ("bindInsTest: rebind parameters");
		}
		bnd = stbuf->bind;
	} else { //prepare and do static binds
		if (verbose) {
			puts ("bindInsTest: setup bind parameters");
		}
		i = (sizeof (MYSQL_BIND) * stbuf->nfld);
		stbuf->bind = malloc (i);
		if (! stbuf->bind) {
			fputs ("InsTest can't alloc memory for bind\n", stderr);
			return (-1);
		}
		memset (stbuf->bind, 0, i);
		bnd = stbuf->bind;
		bnd[0].buffer_type = MYSQL_TYPE_LONG;
		bnd[0].is_unsigned = 0;
		bnd[0].buffer = &para->number;
		bnd[0].is_null = &para->numberI;
		
		bnd[1].buffer_type = MYSQL_TYPE_STRING;
		bnd[1].buffer_length = STMT_INDICATOR_NTS;
		bnd[1].buffer = &para->name;
		bnd[1].is_null = &para->nameI;

		bnd[2].buffer_type = MYSQL_TYPE_STRING;
		bnd[2].buffer_length = STMT_INDICATOR_NTS;
		bnd[2].buffer = &para->stamp;
		bnd[2].is_null = &para->stampI;

		bnd[3].buffer_type = MYSQL_TYPE_DATETIME;
		bnd[3].buffer = &para->birthtm;
		bnd[3].is_null = &para->birthtmI;

		bnd[4].buffer_type = MYSQL_TYPE_FLOAT;
		bnd[4].is_unsigned = 0;
		bnd[4].buffer = &para->price;
		bnd[4].is_null = &para->priceI;

		bnd[5].buffer_type = MYSQL_TYPE_DOUBLE;
		bnd[5].is_unsigned = 0;
		bnd[5].buffer = &para->precise;
		bnd[5].is_null = &para->preciseI;

		bnd[6].buffer_type = MYSQL_TYPE_SHORT;
		bnd[6].is_unsigned = 0;
		bnd[6].buffer = &para->smally;
		bnd[6].is_null = &para->smallyI;

		bnd[7].buffer_type = MYSQL_TYPE_TINY;
		bnd[7].is_unsigned = 1;
		bnd[7].buffer = &para->tiny;
		bnd[7].is_null = &para->smallyI;

		bnd[8].buffer_type = MYSQL_TYPE_LONGLONG;
		bnd[8].is_unsigned = 1;
		bnd[8].buffer = &para->epoch;
		bnd[8].is_null = &para->epochI;

		bnd[9].buffer_type = MYSQL_TYPE_STRING;
		bnd[9].buffer_length = STMT_INDICATOR_NTS;
		bnd[9].is_null = &para->x509certI;

		bnd[10].buffer_type = MYSQL_TYPE_BLOB;
		bnd[10].buffer_length = 0;
		bnd[10].is_null = &para->pictureI;
	}
	// dynamic bindings ... each call rebind, might be a other buffer
	bnd[9].buffer = (void*) para->x509cert;
printf ("cert is cert<%p> buf<%p> null<%d>\n",
		para->x509cert, bnd[9].buffer, para->x509certI);
//printf ("cert is <%s>\n", (char*)bnd[9].buffer);
	bnd[10].buffer = (void*) para->picture;
	bnd[10].buffer_length = para->pictureL;
printf ("picure is pic<%p> buf<%p> null<%d> length<%ld>\n",
		para->picture, bnd[10].buffer, para->pictureI, para->pictureL);
//prthex (para->picture, para->pictureL);

	if (mysql_stmt_bind_param (stbuf->stmt, stbuf->bind)) {
		pHdl->MysqlErr = mysql_stmt_errno (stbuf->stmt);
		fprintf (stderr, "bindInsTest bind_para failed err:%d <%s>\n",
			pHdl->MysqlErr, mysql_stmt_error(stbuf->stmt));
		return (-1);
	}
//	if (verbose) { puts ("bindInsTest: parameters bound"); }
	return (0);
}

/*------------------------------------------------------------------------------
 * ExecQuery: just execute statement, not for selects
 *   return: error as negative value, result as positive value
 *--------------------------------------------------------------------------- */
int ExecQuery (pgm *pHdl, char *qry)  {
my_ulonglong rslt = -1;

	if (mysql_query (pHdl->con, qry)) {
		pHdl->MysqlErr = mysql_errno (pHdl->con);
		fprintf (stderr, "query <%s> failed err: %d <%s>\n", qry,
			pHdl->MysqlErr, mysql_error (pHdl->con));
		return (-1);
	}
	rslt = mysql_affected_rows (pHdl->con);
	return ((int)rslt); // will cut huge numbers, that's fine for test
}

/*------------------------------------------------------------------------------
 * init
 *--------------------------------------------------------------------------- */
int init(pgm *pHdl) {
MYSQL *con;
my_bool autocommit = 0;
unsigned int sver, cver;
char verbose;

	verbose = ((pHdl->flag & VERBOSE) == VERBOSE);
    if (! (con = mysql_init (NULL))) {
		fprintf (stderr, "cant't initialize mariadb\n");
		return (1);
	}
	if (mysql_optionsv (con, MYSQL_READ_DEFAULT_FILE)) {
		fprintf (stderr, "cant't get config files\n");
		return (1);
	}
    mysql_real_connect (con, HOST , USER , PWD , DB,  0, NULL, 0);
	if ((pHdl->MysqlErr = mysql_errno (con))) {
		fprintf (stderr, "can't connect to test err:%d <%s>\n",
			pHdl->MysqlErr, mysql_error (con));
		return (1);
	}
	mysql_autocommit (con, autocommit);
	if (verbose) {
		printf ("session set autocommit to <%d>\n", autocommit);
	}
	pHdl->con = con;
	if (verbose) {
		printf ("connection id: %ld\n", mysql_thread_id (con));
		sver = mysql_get_server_version (con);
		cver = mysql_get_client_version ();
		printf ("mariadb server ver:<%d>, client ver:<%d>\n", sver, cver);
		mariadb_get_infov (con, MARIADB_CONNECTION_HOST, (void *)&pHdl->dbi.host);
		mariadb_get_infov (con, MARIADB_CONNECTION_USER, (void *)&pHdl->dbi.user);
		printf ("connected on %s to db test by %s\n>> if program get stuck - table is locked\n",
			pHdl->dbi.host, pHdl->dbi.user);
	}
	return (0);
}

/*------------------------------------------------------------------------------
 * FreeStmt
 *--------------------------------------------------------------------------- */
int FreeStmt (pgm *pHdl, stmtb *stmtb){

	if (!stmtb) {
		fputs ("statement buffer not allocated\n", stderr);
		return (-1);
	}
	if (stmtb->bind) { free (stmtb->bind); stmtb->bind = 0; }
	if (stmtb->rslt) { mysql_free_result (stmtb->rslt); stmtb->rslt = 0; }
	if (stmtb->query) { free (stmtb->query); stmtb->query = 0; }
	if (stmtb->stmt) {
		if (mysql_stmt_close (stmtb->stmt)) {
        	fprintf	(stderr, "close statement failed err: %s\n",
				mysql_stmt_error(stmtb->stmt));
			pHdl->status = 99;
			return (1);
		}
		stmtb->stmt = 0;
	}
	free (stmtb); stmtb = 0;
	return (0);
}

/*------------------------------------------------------------------------------
 * loadfile into char buffer
 *	return length or neg on error
 *--------------------------------------------------------------------------- */
int loadfile (pgm *pHdl, char *file, char **tgt, int size) {
char verbose;
FILE *fh;
int fsz;

	verbose = ((pHdl->flag & VERBOSE) == VERBOSE);
	if (*tgt) { free (*tgt); *tgt = 0; }
	if (verbose) { printf ("loading file %s\n", file); }
	if (! (fh = fopen (file, "r"))) {
		fprintf (stderr, "can't open <%s>, jumping\n", file);
		return (-1);
	}
	fseek (fh, 0L, SEEK_END);
	fsz = ftell (fh);
	if (! fsz) {
		fprintf (stderr, "<%s> empty, jumping\n", file);
		fclose (fh);
		return (-2);
	}
	fseek (fh, 0L, SEEK_SET);
	if (fsz > size) {
		fprintf (stderr, "content of <%s> to big (%d), jumping\n", file, fsz);
		fclose (fh);
		return (-3);
	}
	*tgt = calloc ((fsz + 1), sizeof (char));
	if (! *tgt) {
		fprintf (stderr, "can't malloc space for <%s>, jumping\n", file);
		fclose (fh);
		return (-4);
	}
	fread (*tgt, sizeof (char), fsz, fh);
	if (verbose) {
		printf ("file <%s>: %dB slurped in char buffer\n", file, fsz);
	}
	fclose (fh);
	return (fsz);
}

/*------------------------------------------------------------------------------
 * prthex
 *--------------------------------------------------------------------------- */
void prthex (char *str, int sz) {
unsigned char *c;
int i;

	if (! sz) { return; }
	for (c = (unsigned char*)str, i = 0; i < sz ; c++, i++) {
		if (i == 0) { printf ("%3d\t: ", i); }
		else { if (! (i % 16)) { printf ("\n%3d\t: ", i); } }
		printf ("%2hx-", *c);
	}
	putc ('\n', stdout);
	return;
}

/*------------------------------------------------------------------------------
 * finish
 *--------------------------------------------------------------------------- */
void finish(pgm *pHdl){
char verbose;

	verbose = ((pHdl->flag & VERBOSE) == VERBOSE);
	if (pHdl->InsTest) {
		if (FreeStmt(pHdl, pHdl->InsTest)) {
			fputs ("free InsTest failed\n", stderr);
		} else {
			if (verbose) { puts ("InsTest freed / reset"); }
		}
	}
	if (pHdl->SelTest) {
		if (FreeStmt(pHdl, pHdl->SelTest)) {
			fputs ("free InsTest failed\n", stderr);
		} else {
			if (verbose) { puts ("SelTest freed / reset"); }
		}
	}
	if (pHdl->con) {
		if (pHdl->status) {
			mysql_rollback (pHdl->con);
			if (verbose) { puts ("mysql rollback unfinished DML work"); }
		}
		mysql_close (pHdl->con); pHdl->con = 0;
		if (verbose) { puts ("mysql connection closed"); }
	}
	if (pHdl->status) {
		printf ("pgm aborted with status <%d>\n", pHdl->status);
	} else {
		puts ("pgm ended normally");
	}
	exit (pHdl->status);
}

/*------------------------------------------------------------------------------
 * usage
 *--------------------------------------------------------------------------- */
void usage (char *pgm) {

	fprintf (stderr, "usage: %s [-h?vcisd]\n\
       -h|? : this help\n\
       -v   : verbose\n\
       -c   : create table test\n\
       -i   : insert random content, can result in duplicates\n\
       -s   : select from table test\n\
       -d   : drop table test\n\
\n\
    this is a simple test program that ahows how to use c-connector for\n\
    mariadb. <%s -cisd> will create the table, insert content,\n\
    do a select and finally drop the table.\n\
    the program also use direct execute of sql statement, which do not need\n\
    further input or output data.\n\
    the program focus on using one dataset for internal calculation and DB\n\
    operation avoiding exhausted memory and cpu usage.\n\n\
    version %s\n", pgm, pgm, sccs);
    return;

}
