// <Scol Technologies License>
//
// 'Source code' license (BSD license) :
// Copyright (c) 2003, organization : Scol Technologies Association, owner : Sylvain Huet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
// following conditions are met:
// - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 
// disclaimer. 
// - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. 
// - Neither the name of the 'Scol Technologies Association' nor the names of its contributors may be used to endorse or 
// promote products derived from this software without specific prior written permission. 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// 'Scol Technologies' is a trademark. Therefore :
// - The names 'Scol' and 'Scol Technologies' must not be used to endorse or promote products derived from this software 
// without prior written permission. For written permission, please contact Scol Technologies Association 
// (www.scol-technologies.org).
// - Products derived from this software may not be called 'Scol', nor may 'Scol' appear in their name, without prior 
// written permission. For written permission, please contact Scol Technologies Association (www.scol-technologies.org).
//
// 'DMS' is a patented technology.
// - The owner provides unlimited and free rights to implement this patent with the Scol programming language, and to use 
// these implementations.
// - Other kinds of implementation or use of the patent require prior written permission. For written permission, please 
// contact Scol Technologies Association (www.scol-technologies.org).

/* Plugin SQL    - Sylvain Huet */

#include <string.h>
#include <stdlib.h>
#include "x/scolplugin.h"
#if VERSIONPC
#include <windows.h>
#include <windowsx.h>
#include <Odbcinst.h>
#include <sqlext.h>
#else
#include <isqlext.h>
#endif

#define AUTOCOMMIT_ON	0
#define AUTOCOMMIT_OFF	1

#define INFO_OK	0
#define INFO_KO	1

#define Sql_SUCCESS		0
#define Sql_ERROR		1
#define Sql_NO_DATA		2

cbmachine ww;

int sqltype;

struct DBscol {
	HENV henv;
	HDBC hdbc;
	short sqlcod;
	UCHAR sqlstate[6];
	SDWORD native;
	UCHAR msg[SQL_MAX_MESSAGE_LENGTH];
	SDWORD numrows;
};
typedef struct DBscol *dbscol;

struct SQLtype {
	int typ;
	char *str;
};
struct SQLtype convertsql[]=
{
	{SQL_BIGINT,"SQL_BIGINT"},{SQL_BINARY,"SQL_BINARY"},{SQL_BIT,"SQL_BIT"},{SQL_CHAR,"SQL_CHAR"},
	{SQL_DATE,"SQL_DATE"},{SQL_DECIMAL,"SQL_DECIMAL"},{SQL_DOUBLE,"SQL_DOUBLE"},{SQL_FLOAT,"SQL_FLOAT"},
	{SQL_INTEGER,"SQL_INTEGER"},{SQL_LONGVARBINARY,"SQL_LONGVARBINARY"},{SQL_LONGVARCHAR,"SQL_LONGVARCHAR"},
	{SQL_NUMERIC,"SQL_NUMERIC"},{SQL_REAL,"SQL_REAL"},{SQL_SMALLINT,"SQL_SMALLINT"},{SQL_TIME,"SQL_TIME"},
	{SQL_TIMESTAMP,"SQL_TIMESTAMP"},{SQL_TINYINT,"SQL_TINYINT"},{SQL_VARBINARY,"SQL_VARBINARY"},
	{SQL_VARCHAR,"SQL_VARCHAR"} 
};
#define NB_CONVERT (sizeof(convertsql)/sizeof(struct SQLtype))


void _clrError(dbscol d)
{
	d->sqlcod = SQL_SUCCESS;
	strcpy(d->sqlstate, "00000");
	d->native = 0;
	d->msg[0] = '\0';
	d->numrows = 0;
}

int setError(char *fct,RETCODE rc,short info,dbscol d,HSTMT hstmt)
{
	UCHAR szSqlState[6];
	SDWORD pfNativeError[100];
	UCHAR szErrorMsg[SQL_MAX_MESSAGE_LENGTH-1];
	SWORD pcbErrorMsg;

	_clrError(d);

	if (rc==SQL_SUCCESS || (rc==SQL_SUCCESS_WITH_INFO && info==INFO_OK)) {
		d->sqlcod = Sql_SUCCESS;
		SQLRowCount(hstmt, &d->numrows);
	}
	else
		if (rc==SQL_NO_DATA_FOUND)
			d->sqlcod = Sql_NO_DATA;
		else {
			d->sqlcod = Sql_ERROR;
			szSqlState[0]='\0';
			szErrorMsg[0]='\0';
			SQLError(d->henv,d->hdbc, hstmt, szSqlState, pfNativeError, szErrorMsg,
				sizeof(szErrorMsg), &pcbErrorMsg);
/*			SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, szSqlState, &pfNativeError[0], szErrorMsg,
				sizeof(szErrorMsg), &pcbErrorMsg);*/
			strcpy(d->sqlstate, szSqlState);
			d->native = pfNativeError[0];
			strcpy(d->msg, szErrorMsg);
			MMechostr(MSKFOO,"fct=%s rc=%d sqlstate=%s native=%d (0x%x) msg=<%s>\n",
				fct, rc, szSqlState, pfNativeError[0], pfNativeError[0], szErrorMsg);
		}
	return 0;
}

int connectDB(dbscol d,char *name,char *login,char *passwd)
{
	RETCODE retcode;

	retcode=SQLAllocEnv(&d->henv);
	setError("SQLAllocEnv",retcode,INFO_KO,d,NULL);
	if (retcode!=SQL_SUCCESS) return -1;

	retcode=SQLAllocConnect(d->henv,&d->hdbc);
	setError("SQLAllocConnect",retcode,INFO_KO,d,NULL);
	if (retcode!=SQL_SUCCESS) {
		SQLFreeEnv(d->henv);
		return -1;
	}

	retcode=SQLSetConnectOption(d->hdbc,SQL_LOGIN_TIMEOUT,5);
	setError("SQLSetConnectOption(SQL_LOGIN_TIMEOUT)",retcode,INFO_KO,d,NULL);
	if (retcode!=SQL_SUCCESS) {
		SQLFreeEnv(d->henv);
		return -1;
	}
	retcode=SQLSetConnectOption(d->hdbc,SQL_AUTOCOMMIT,SQL_AUTOCOMMIT_ON);
	setError("SQLSetConnectOption(SQL_AUTOCOMMIT)",retcode,INFO_KO,d,NULL);
	if (retcode!=SQL_SUCCESS) {
		SQLFreeEnv(d->henv);
		return -1;
	}

	retcode=SQLConnect(d->hdbc,name,SQL_NTS,login,SQL_NTS,passwd,SQL_NTS); 
	setError("SQLConnect",retcode,INFO_OK,d,NULL);
	if ((retcode!=SQL_SUCCESS)&&(retcode!=SQL_SUCCESS_WITH_INFO)) {
		SQLFreeConnect(d->hdbc); 
		SQLFreeEnv(d->henv); 
		return -1;
	}
	return 0;
}


//$BLG: v4.6a5
int connectDBext(dbscol d, char *data)
{
	char outputstr[2048];
	short outputlen;
	RETCODE retcode;

	retcode = SQLAllocEnv(&d->henv);
	setError("SQLAllocEnv", retcode, INFO_KO, d, NULL);
	if (retcode != SQL_SUCCESS) return -1;

	retcode = SQLAllocConnect(d->henv, &d->hdbc);
	setError("SQLAllocConnect", retcode, INFO_KO, d, NULL);
	if (retcode != SQL_SUCCESS) 
	{
		SQLFreeEnv(d->henv);
		return -1;
	}

	retcode = SQLSetConnectOption(d->hdbc, SQL_LOGIN_TIMEOUT, 5);
	setError("SQLSetConnectOption(SQL_LOGIN_TIMEOUT)", retcode, INFO_KO, d, NULL);
	if (retcode != SQL_SUCCESS) 
	{
		SQLFreeEnv(d->henv);
		return -1;
	}
	retcode = SQLSetConnectOption(d->hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
	setError("SQLSetConnectOption(SQL_AUTOCOMMIT)", retcode, INFO_KO, d, NULL);
	if (retcode != SQL_SUCCESS) 
	{
		SQLFreeEnv(d->henv);
		return -1;
	}

	retcode = SQLDriverConnect(d->hdbc, NULL, data, strlen(data), outputstr, 2048, &outputlen, SQL_DRIVER_NOPROMPT);
	setError("SQLConnectExt", retcode, INFO_OK, d, NULL);
	if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) 
	{
		SQLFreeConnect(d->hdbc); 
		SQLFreeEnv(d->henv); 
		return -1;
	}
	return 0;
}


int disconnectDB(dbscol d)
{
	SQLDisconnect(d->hdbc); 
	SQLFreeConnect(d->hdbc); 
	SQLFreeEnv(d->henv); 
	return 0;
}

char *typtostr(int typ)
{
	int i,fin;

	for (fin=0,i=0; !fin && i<NB_CONVERT; i++)
		if (convertsql[i].typ == typ) fin=1;
	if (!fin) return NULL;
	else return convertsql[i-1].str;
}

int requestDB(mmachine m,dbscol d,char* req,int reqlen,int listp)
{
	HSTMT hstmt;
	RETCODE retcode;
	SDWORD cbName[100];
	SWORD pccol;
	SDWORD lgval;
	int n,p,k,l,i,s,cb,col,pp;
	char *val;
	int len;
	char data[256];
	char *tmp;


	pccol = 0;
	cb = 0;

	retcode=SQLAllocStmt(d->hdbc,&hstmt);
	setError("SQLAllocStmt",retcode,INFO_KO,d,NULL);
	if (retcode!=SQL_SUCCESS) return -1;

	n=1;
	while(listp!=NIL) {
		p=MMfetch(m,listp,0)>>1;
		if (p!=NIL) {
			k=MMfetch(m,p,0)>>1;
			l=MMfetch(m,p,1)>>1;
			val=MMstartstr(m,l);
			len=MMsizestr(m,l);
			if ((k>=0)&&(k<NB_CONVERT)) {
				if (l!=NIL && len>0) {
					MMechostr(MSKTRACE,"parameter %d : <%s> %d len=%d\n",n,val,k,len);
					cbName[n-1] = len;
					retcode=SQLBindParameter(hstmt,(UWORD)n,SQL_PARAM_INPUT,SQL_C_CHAR,
					                         (SWORD)convertsql[k].typ,len,0,val,len,&cbName[n-1]);
				}
				else {
					MMechostr(MSKTRACE,"parameter %d : NIL %d\n",n,k);
					cbName[n-1] = SQL_NULL_DATA;
					retcode=SQLBindParameter(hstmt,(UWORD)n,SQL_PARAM_INPUT,SQL_C_CHAR,
							                     (SWORD)convertsql[k].typ,0,0,NULL,0,&cbName[n-1]);
				}
				setError("SQLBindParameter",retcode,INFO_KO,d,NULL);
				if (retcode!=SQL_SUCCESS) {
					SQLFreeStmt(hstmt, SQL_DROP);
					return -1;
				}
				n++;
			}
		}
		listp=MMfetch(m,listp,1)>>1;
	}

	/* on enlève l'éventuel ";" pour etre conforme ODBC */
	if (req[reqlen-1]==';') req[reqlen-1]='\0';

	MMechostr(MSKTRACE,"REQUETE=<%s>\n",req);
	/* récupération de toutes les tables */
	if (!strcmp(req,"GET_TABLES")) {
		retcode=SQLTables(hstmt,NULL,0,NULL,0,NULL,0,"TABLE",SQL_NTS);
		setError("SQLTables",retcode,INFO_KO,d,hstmt);
	}
	else
	/* récupération de toutes les colonnes d'une table */
	if (!strcmp(req,"GET_COLUMNS")) {
		retcode=SQLColumns(hstmt,NULL,0,NULL,0,val,SQL_NTS,NULL,0);
		setError("SQLColumns",retcode,INFO_KO,d,hstmt);
	}
	/* autre requête */
	else {
		retcode=SQLExecDirect(hstmt,req,SQL_NTS);
		setError("SQLExecDirect",retcode,INFO_KO,d,hstmt);
	}
	if (retcode!=SQL_SUCCESS) {
		SQLFreeStmt(hstmt, SQL_DROP);
		return -1;
	}

	n = 0;
	len = 0;
	while (1)
	{
		retcode=SQLFetch(hstmt);
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO && retcode != SQL_NO_DATA_FOUND)
		{
			/* Cas de UPDATE, DELETE : on veut calculer numrows */
			if (n==0)
				setError("SQLFetch",SQL_SUCCESS,INFO_OK,d,hstmt);
			else
				setError("SQLFetch",retcode,INFO_OK,d,hstmt);
			SQLFreeStmt(hstmt, SQL_DROP);
			return -1;
		}

		/* Recherche callback */
		if (!cb) {
			pp=MMgetPP(m);
			cb=OBJbeginreflex(m, sqltype, (int)d->henv, 0);
		}

		/* Si premier fetch, prise en compte du retour de SQLFetch */
		if (n==0) {
			setError("SQLFetch",retcode,INFO_OK,d,hstmt);
			if (retcode==SQL_NO_DATA_FOUND)
				MMechostr(MSKTRACE,"NO_DATA_FOUND\n"); 
		}

		if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
			/* recherche du nbre de colonnes la 1ere fois */
			if (n==0) {
				/* Si requête "GET_TABLES", 1 seule colonne récupérée */
				if (!strcmp(req,"GET_TABLES"))
					pccol=1;
				else
				/* Si requête "GET_COLUMNS", 3 colonnes récupérées */
				if (!strcmp(req,"GET_COLUMNS"))
					pccol=3;
				else {
					retcode=SQLNumResultCols(hstmt,&pccol);
					setError("SQLNumResultCols",retcode,INFO_KO,d,hstmt);
					if (retcode!=SQL_SUCCESS) {
						SQLFreeStmt(hstmt, SQL_DROP);
						return -1;
					}
				}
			}
			/* récupération des données de chaque colonne */
			for (i=0;i<pccol;i++) {
				val = NULL;
				lgval = 0;

				/* Si requête "GET_TABLES", colonne NAME récupérée */
				if (!strcmp(req,"GET_TABLES"))
					col=3;
				else
				/* Si requête "GET_COLUMNS", colonnes NAME,TYPE,SIZE récupérées */
				if (!strcmp(req,"GET_COLUMNS"))
					col=(i==2? 7 : i+4);
				else
					col=i+1;

				/* On boucle par blocs de 255 pour les valeurs plus longues */
				while ((retcode=SQLGetData(hstmt,(UWORD)col,SQL_C_CHAR,data,sizeof(data),&cbName[0]))
						== SQL_SUCCESS_WITH_INFO) {
					setError("SQLGetData",retcode,INFO_KO,d,hstmt);
					if (strcmp(d->sqlstate,"01004") && strcmp(d->sqlstate,"S1004") && strcmp(d->sqlstate,"00000")) {
						if (val) free(val);
						SQLFreeStmt(hstmt, SQL_DROP);
						return -1;
					}
					lgval += sizeof(data)-1;
					if ((tmp=(char*)realloc(val,lgval+1))==NULL) {
						if (val) free(val);
						SQLFreeStmt(hstmt, SQL_DROP);
						return -1;
					}
					val=tmp;
					memcpy(val+lgval-sizeof(data)+1,data,sizeof(data));
				}

				setError("SQLGetData",retcode,INFO_KO,d,hstmt);
				if (retcode!=SQL_SUCCESS) {
					if (val) free(val);
					SQLFreeStmt(hstmt, SQL_DROP);
					return -1;
				}

				if (cbName[0] == SQL_NULL_DATA) {
					if (MMpush(m,NIL)) return MERRMEM;
				}
				else {
					/* Si requête "GET_COLUMNS", colonne TYPE convertie en chaîne */
					if (!strcmp(req,"GET_COLUMNS") && (col==5)) {
						if ((val=typtostr(atoi(data)))==NULL) {
							if (MMpush(m,NIL)) return MERRMEM;
						}
						else {
							l=strlen(val);
							if (l<32) l=32;
							l=(l+4)>>2;
							s=MMmallocCLR(m,l+1,TYPEBUF); if (s==NIL) return MERRMEM;
							strcpy(MMstartstr(m,s),val);
							MMsetsizestr(m,s,strlen(val));
							len += strlen(val);
							if (MMpush(m,s+s+1)) return MERRMEM;
						}
					}
					else {
						l=lgval+cbName[0];
						if (l<32) l=32;
						l=(l+4)>>2;
						s=MMmallocCLR(m,l+1,TYPEBUF); if (s==NIL) return MERRMEM;
						if (val) {
							memcpy(MMstartstr(m,s),val,lgval);
							free(val);
						}
						memcpy(MMstartstr(m,s)+lgval,data,cbName[0]+1);
						MMsetsizestr(m,s,lgval+cbName[0]);
						len += lgval+cbName[0];
						if (MMpush(m,s+s+1)) return MERRMEM;
					}
				}
			}
			if (MMpush(m,NIL)) return MERRMEM;
			for (i=0;i<pccol;i++) {
				if (MMpush(m,2*2)) return MERRMEM;
				if ((k=MBdeftab(m))) return k;
			}
			n++;

			/* Si callback existe, on l'appelle */
			if (!cb) {
				if ((k=OBJcallreflex(m, 1))) return k;
				/* Si retour callback != 0 => on ferme le curseur et fin */
				if (MMget(m, -2)) {
					MMechostr(MSKTRACE,"lgdata=%d\n",len); 
					if (MMpush(m,NIL)) return MERRMEM;
					SQLFreeStmt(hstmt, SQL_DROP); 
					return 0;
				}
			}
		}
		else {
			/* Fin du curseur : si callback trouvée, on enlève les param mis
			   par OBJbeginreflex */
			if (!cb) {
				MMsetPP(m,pp);
			}
			break;
		}
	}
	MMechostr(MSKTRACE,"lgdata=%d\n",len); 
	if (MMpush(m,NIL)) return MERRMEM;
	/* Si pas de callback, on renvoie tout */
	if (cb) {
		for (i=0;i<n;i++)
		{
			if (MMpush(m,2*2)) return MERRMEM;
			if ((k=MBdeftab(m))) return k;
		}
	}
	SQLFreeStmt(hstmt, SQL_DROP);
	return 0;
}

int endTran(dbscol d, int tran)
{
	RETCODE retcode;

	if (tran==1) {
		retcode=SQLTransact(SQL_NULL_HENV, d->hdbc, SQL_COMMIT);
		setError("SQLTransact",retcode,INFO_OK,d,NULL);    
		if ((retcode!=SQL_SUCCESS)&&(retcode!=SQL_SUCCESS_WITH_INFO)) {
			SQLTransact(SQL_NULL_HENV, d->hdbc, SQL_ROLLBACK);
		}
	}
	else {
		retcode=SQLTransact(SQL_NULL_HENV, d->hdbc, SQL_ROLLBACK);
		setError("SQLTransact",retcode,INFO_OK,d,NULL);
	}
	return 0;
}

int MSqlCreate(mmachine m)
{
	dbscol d;
	int l,o,p,q,r;

	l=(sizeof(d)+3)>>2;
	o=MMmalloc(m,l,TYPEBUF); if (o==NIL) return MERRMEM;

	r=MMpull(m)>>1;
	q=MMpull(m)>>1;
	p=MMpull(m)>>1;
	d=(dbscol)malloc(sizeof(struct DBscol));
	if ((p==NIL)||(q==NIL)||(r==NIL)||(d==NULL)) {
		if (d) free(d);
		MMset(m,0,NIL);
		return 0;
	}

	*(dbscol*)MMstart(m,o)=d;
	_clrError(d);

	if (connectDB(d,MMstartstr(m,p),MMstartstr(m,q),MMstartstr(m,r))) {
		if (d) free(d);
		MMset(m,0,NIL);
		return 0;
	}
	MMechostr(MSKFOO,"DB connected\n");
	if (MMpush(m,o+o+1)) return MERRMEM;
	return OBJcreate(m,sqltype,(int)d->henv,-1,0);
}


//$BLG: v4.6a5
int MSqlCreateEx(mmachine m)
{
  dbscol d;
	int l, o, p;

	l = (sizeof(d) + 3) >> 2;
	o = MMmalloc(m, l, TYPEBUF); 
	if (o == NIL) return MERRMEM;

	p = MMpull(m) >> 1;
	//$BLG Note: The _channel parameter isn't used
	d = (dbscol)malloc(sizeof(struct DBscol));
	if ((p == NIL) || (d == NULL)) 
	{
		if (d) free(d);
		MMset(m, 0, NIL);
		return 0;
	}

	*(dbscol*)MMstart(m, o) = d;
	_clrError(d);

	if (connectDBext(d, MMstartstr(m, p))) 
	{
		if (d) free(d);
		MMset(m, 0, NIL);
		return 0;
	}
	MMechostr(MSKFOO, "DB connected\n");
	if (MMpush(m, o+o+1)) return MERRMEM;
	return OBJcreate(m, sqltype, (int)d->henv, -1, 0);
}


int MSqlDestroy2(mmachine m,int q,int p)
{
	dbscol d;

	MMechostr(MSKFOO,"destroyDB %x, %x\n",q,p);

	d=*(dbscol*)MMstart(m,p>>1);
	_clrError(d);
	disconnectDB(d);
	free(d);
	*MMstart(m,p>>1)=NIL;
	return 0;
}

int MSqlDestroy(mmachine m)
{
	int p,q;
	dbscol d;

	p=MMget(m,0);
	q=p>>1;
	if (q==NIL) return 0;
	d=*(dbscol*)MMstart(m,q);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlDestroy: object DB already destroyed\n");
		return 0;
	}
	OBJdelTM(m,sqltype,p);
	MMset(m,0,0);
	return 0;
}

int MSqlFetch(mmachine m)
{
	int p;
	dbscol d;

	p=MMget(m,2)>>1;
	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlFetch: object DB already destroyed\n");
		MMpull(m);
		MMpull(m);
		return 0;
	}
	return OBJaddreflex(m, sqltype, 0);
}

int MSqlRequest(mmachine m)
{
	int listp,r,p,pp,rc,h;
	dbscol d;
	char *req;

	listp=MMpull(m)>>1;
	r=MMpull(m)>>1;
	p=MMpull(m)>>1;
	if ((p==NIL)||(r==NIL)) return MMpush(m,NIL);

	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlRequest: object DB already destroyed\n");
		return MMpush(m,NIL);
	}
	if ((req=(char*)malloc(MMsizestr(m,r)+1))==NULL)
		return MMpush(m,NIL);
	memset(req,0,MMsizestr(m,r)+1);
	memcpy(req,(char*)MMstartstr(m,r),MMsizestr(m,r));
	_clrError(d);
	h=(int)d->henv;
	pp=MMgetPP(m);
	if (requestDB(m,d,req,MMsizestr(m,r),listp))
	{
		MMsetPP(m,pp);
		free(req);
		req=NULL;
		if (MMpush(m,NIL)) return MERRMEM;
	}
	if (req) free(req);

	/* Si callback, on l'enlève de l'objet */
	pp=MMgetPP(m);
	if (!OBJbeginreflex(m, sqltype, h, 0)) {
		MMset(m,2,MMget(m,1));
		MMset(m,1,NIL);
		MMset(m,0,NIL);
		if ((rc=MSqlFetch(m))) return rc;
		MMsetPP(m,pp);
	}
	return 0;
}

int MSqlCommit(mmachine m)
{
	int p;
	dbscol d;

	p=MMget(m,0)>>1;
	if (p==NIL) return 0;

	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlCommit: object DB already destroyed\n");
		return 0;
	}
	_clrError(d);

	endTran(d, 1);

	return 0;
}

int MSqlRollback(mmachine m)
{
	int p;
	dbscol d;

	p=MMget(m,0)>>1;
	if (p==NIL) return 0;

	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlRollback: object DB already destroyed\n");
		return 0;
	}
	_clrError(d);

	endTran(d, 0);

	return 0;
}

int MSqlSetAttr(mmachine m)
{
	int p,a;
	dbscol d;
	RETCODE retcode;

	a=MMpull(m)>>1;
	p=MMget(m,0)>>1;
	if (p==NIL) return 0;

	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlSetAttr: object DB already destroyed\n");
		return 0;
	}
	_clrError(d);

	switch (a) {
	case AUTOCOMMIT_ON : {
		retcode=SQLSetConnectOption(d->hdbc,SQL_AUTOCOMMIT,SQL_AUTOCOMMIT_ON);
		break;
		}
	case AUTOCOMMIT_OFF : {
		retcode=SQLSetConnectOption(d->hdbc,SQL_AUTOCOMMIT,SQL_AUTOCOMMIT_OFF);
		break;
		}
	default :
		retcode = SQL_SUCCESS;
		break;
	}
	setError("SQLSetConnectOption",retcode,INFO_KO,d,NULL);

	return 0;
}

int MSqlCod(mmachine m)
{
	int p;
	dbscol d;

	p=MMget(m,0)>>1;
	if (p==NIL) return 0;

	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlCod: object DB already destroyed\n");
		return 0;
	}

	MMset(m,0,(d->sqlcod)<<1);

	return 0;
}

int MSqlDescErr(mmachine m)
{
	int p;
	dbscol d;
	struct DBscol D;

	p=MMpull(m)>>1;
	if (p==NIL) return MMpush(m,NIL);

	d=*(dbscol*)MMstart(m,p);
	if ((int)d==NIL) {
		MMechostr(MSKFOO,"SqlDescErr: object DB already destroyed\n");
		return MMpush(m,NIL);
	}
	memcpy(&D, d, sizeof(struct DBscol));

	if (Mpushstrbloc(m,D.sqlstate)) return MERRMEM;
	if (MMpush(m,(D.native)<<1)) return MERRMEM;
	if (Mpushstrbloc(m,D.msg)) return MERRMEM;
	if (MMpush(m,(D.numrows)<<1)) return MERRMEM;
	if (MMpush(m,4<<1)) return MERRMEM;
	return(MBdeftab(m));
}


char* mSQLname[]=
{"SqlDB","SqlParam","SqlCreate","SqlDestroy",
 //$BLG: v4.6a5 - Sta
 "SqlCreateEx",
 //$BLG: End
 "SqlRequest","SqlFetch",
 "SqlCod","SqlDescErr",
 "SqlCommit","SqlRollback","SqlSetAttr",
 "AUTOCOMMIT_ON","AUTOCOMMIT_OFF",
 "SQL_SUCCESS","SQL_ERROR","SQL_NO_DATA",
 "SQL_BIGINT","SQL_BINARY","SQL_BIT","SQL_CHAR",
 "SQL_DATE","SQL_DECIMAL","SQL_DOUBLE","SQL_FLOAT",
 "SQL_INTEGER","SQL_LONGVARBINARY","SQL_LONGVARCHAR","SQL_NUMERIC",
 "SQL_REAL","SQL_SMALLINT","SQL_TIME","SQL_TIMESTAMP",
 "SQL_TINYINT","SQL_VARBINARY","SQL_VARCHAR","SQL_NIL"
};

#define NSQLPKG (sizeof(mSQLname)/sizeof(char*))

int (*mSQLfun[NSQLPKG])(mmachine m)=
{NULL,NULL,MSqlCreate,MSqlDestroy,
 //$BLG: v4.6a5 - Sta
 MSqlCreateEx,
 //$BLG: End
 MSqlRequest,MSqlFetch,
 MSqlCod,MSqlDescErr,
 MSqlCommit,MSqlRollback,MSqlSetAttr,
 (void*)(AUTOCOMMIT_ON<<1),(void*)(AUTOCOMMIT_OFF<<1),
 (void*)(Sql_SUCCESS<<1),(void*)(Sql_ERROR<<1),(void*)(Sql_NO_DATA<<1),
 (void*)0,(void*)2,(void*)4,(void*)6,
 (void*)8,(void*)10,(void*)12,(void*)14,
 (void*)16,(void*)18,(void*)20,(void*)22,
 (void*)24,(void*)26,(void*)28,(void*)30,
 (void*)32,(void*)34,(void*)36,(void*)NIL
};

int mSQLnarg[NSQLPKG]=
{TYPTYPE,TYPTYPE,4,1,
 //$BLG: v4.6a5 - Sta
 2,
 //$BLG: End
 3,3,
 1,1,
 1,1,2,
 TYPVAR,TYPVAR,
 TYPVAR,TYPVAR,TYPVAR,
 TYPCONS,TYPCONS,TYPCONS,TYPCONS,
 TYPCONS,TYPCONS,TYPCONS,TYPCONS,
 TYPCONS,TYPCONS,TYPCONS,TYPCONS,
 TYPCONS,TYPCONS,TYPCONS,TYPCONS,
 TYPCONS,TYPCONS,TYPCONS,TYPCONS
};

char* mSQLtype[NSQLPKG]=
{NULL,NULL,"fun [Chn S S S] SqlDB","fun [SqlDB] I",
 //$BLG: v4.6a5 - Sta
 "fun [Chn S] SqlDB",
 //$BLG: End
 "fun [SqlDB S [SqlParam r1]] [[S r1] r1]","fun [SqlDB fun [SqlDB u0 [S r1]] I u0] SqlDB",
 "fun [SqlDB] I","fun [SqlDB] [S I S I]",
 "fun [SqlDB] SqlDB","fun [SqlDB] SqlDB","fun [SqlDB I] SqlDB",
 "I","I",
 "I","I","I",
 "fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam",
 "fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam",
 "fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam",
 "fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam",
 "fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam","fun [S] SqlParam"
};

#if VERSIONPC
__declspec (dllexport)
#endif
int ScolLoadPlugin(mmachine m,cbmachine w)
{
    int k;
    ww=w;
    k=PKhardpak(m,"mSQL.pkg-3.3",NSQLPKG,mSQLname,mSQLfun,mSQLnarg,mSQLtype);
    sqltype=OBJregister(1,0,MSqlDestroy2,"OBJTYPESQL");
    return k;
}


//$BLG: v4.6a5
#if VERSIONPC
__declspec (dllexport)
#endif
int ScolUnloadPlugin()
{
    return 0;
}

