1/* BEGIN LICENSE BLOCK
2 * Version: CMPL 1.1
3 *
4 * The contents of this file are subject to the Cisco-style Mozilla Public
5 * License Version 1.1 (the "License"); you may not use this file except
6 * in compliance with the License.  You may obtain a copy of the License
7 * at www.eclipse-clp.org/license.
8 *
9 * Software distributed under the License is distributed on an "AS IS"
10 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
11 * the License for the specific language governing rights and limitations
12 * under the License.
13 *
14 * The Original Code is  The ECLiPSe Constraint Logic Programming System.
15 * The Initial Developer of the Original Code is  Cisco Systems, Inc.
16 * Portions created by the Initial Developer are
17 * Copyright (C) 1997 - 2006 Cisco Systems, Inc.  All Rights Reserved.
18 *
19 *
20 * Contributor(s): Joachim Schimpf, Stefano Novello, IC-Parc
21 *                 Kish Shen, CrossCore Optimization
22 *
23 * END LICENSE BLOCK */
24
25/*
26 * ECLiPSe LIBRARY MODULE
27 *
28 * $Header: /cvsroot/eclipse-clp/Eclipse/Oci/mysql.c,v 1.9 2015/10/29 01:04:21 kish_shen Exp $
29 *
30 *
31 * IDENTIFICATION:	mysql.c
32 *
33 * AUTHOR:		Joachim Schimpf
34 * AUTHOR:		Stefano Novello
35 * AUTHOR:		Kish Shen
36 *
37 */
38
39/*
40 * char *sccsid = "%W%  %E%";
41 *
42 * Contents:	Prolog wrappers around Oracle Call Interface
43 *
44 * Author:	Stefano Novello
45 * Author:      Kish Shen, Converted to MySQL from original oracle.c
46 *              Jan - Feb, 2006.
47 *
48 *
49 * TODO General header for contents of this file
50 */
51
52#ifdef _WIN32
53#include <windows.h>
54#endif
55#include <stdio.h>
56#include <stdlib.h>
57/*#include <malloc.h>*/
58#include <string.h>
59#include <mysql/mysql.h>
60#include "external.h"	/* ECLiPSe definitions */
61#include "dbi.h"	/* Oracle call interface */
62
63
64/* ----------------------------------------------------------------------
65 *  Definitions
66 * ---------------------------------------------------------------------- */
67
68/* these must correspond to their named structure positions in the ECLiPSe
69   option code
70*/
71#define SESSION_OPT_DBNAME	1
72#define SESSION_OPT_STORAGE     2
73#define CURSOR_OPT_BUFFER	3
74#define CURSOR_OPT_TYPE		4
75
76#define NoErrors {err_code = 0 ; err_msg = "\0"; }
77
78#define DBI_TYPE_CONV       1
79#define DBI_BAD_CURSOR      2
80#define DBI_BAD_SESSION     3
81#define DBI_BAD_FIELD       4
82#define DBI_BAD_TEMPLATE    5
83#define DBI_NOT_QUERY       6
84#define DBI_CANCELLED       7
85#define DBI_NOT_PREPARED    8
86#define DBI_NO_PARAM        9
87#define DBI_NYI            10
88#define DBI_MEMORY         11
89#define DBI_BUFFER_OVER    12
90#define DBI_DATA_TRUNC     13
91
92#ifdef HAVE_LONG_LONG
93
94#define BUFINT long long int
95
96typedef long long long_long;
97
98#define HAVE_MYSQLBIGINT
99
100#elif defined(HAVE__INT64)
101
102typedef __int64 long_long;
103
104#define BUFINT __int64
105
106#define HAVE_MYSQLBIGINT
107
108#else
109
110#define BUFINT int
111
112#undef HAVE_MYSQLBIGINT
113
114#endif
115
116/* ----------------------------------------------------------------------
117 *  Global data
118 * ---------------------------------------------------------------------- */
119
120int  err_code;
121const char *err_msg;
122char *dbi_error[] =
123{
124/* DBI_TYPE_CONV */	"DBI-001: type conversion failure" ,
125/* DBI_BAD_CURSOR */	"DBI-002: bad cursor state" ,
126/* DBI_BAD_SESSION */	"DBI-003: bad session state",
127/* DBI_BAD_FIELD */	"DBI-004: bad field name",
128/* DBI_BAD_TEMPLATE */  "DBI-005: bad template",
129/* DBI_NOT_QUERY */	"DBI-006: not a query",
130/* DBI_CANCELLED */	"DBI-007: cursor was cancelled",
131/* DBI_NOT_PREPARED */	"DBI-008: cursor was not a prepared SQL",
132/* DBI_NO_PARAM */      "DBI-009: input parameters not supplied",
133/* DBI_NYI */		"DBI-010: not implemented" ,
134/* DBI_MEMORY */        "DBI-011: memory allocation problem",
135/* DBI_BUFFER_OVER */   "BBI-012: buffer overflow",
136/* DBI_DATA_TRUNC */    "BBI-013: result data truncated"
137};
138
139/* ----------------------------------------------------------------------
140 *  Internally used procedures
141 * ---------------------------------------------------------------------- */
142
143void
144raise_dbi_error(int code)
145{
146	err_code= - code;
147	err_msg = dbi_error[code - 1];
148#ifdef DEBUG
149	fprintf(stderr,"DEBUG DBI: %d %s\n",err_code,err_msg);
150#endif
151}
152
153
154void
155raise_mysql_error(MYSQL * mysql)
156{
157
158	err_code = mysql_errno(mysql);
159	err_msg = mysql_error(mysql);
160#ifdef DEBUG
161	fprintf(stderr,"DEBUG SQL: %d %s\n",err_code,err_msg);
162#endif
163}
164
165void
166raise_mysql_stmt_error(MYSQL_STMT * stmt)
167{
168
169	err_code = mysql_stmt_errno(stmt);
170	err_msg = mysql_stmt_error(stmt);
171#ifdef DEBUG
172	fprintf(stderr,"DEBUG OSQL: %d %s\n",err_code,err_msg);
173#endif
174}
175/* ----------------------------------------------------------------------
176 *  Auxiliary functions
177 * ---------------------------------------------------------------------- */
178
179#define RoundupSize(s) (sizeof(word) * (1 + (s)/sizeof(word)))
180
181/* initialise the data structures  associated with a template from the
182   information supplied from Prolog. This should be followed, for prepared
183   statements,  by allocation of the data buffers, and the binding of the DB
184   cursor's parameter/result to the data buffers. Finally the actual values
185   of parameters are loaded into the data buffers by template_bind, or the
186   result values are extracted from the DB's return result with template_put
187   Direct statements have no parameters, and the result row do not need
188   user-supplied buffers, so only template_put has to be called.
189*/
190int
191template_get(value v,type t,template_t * * template_out)
192{
193	dident did;
194	char argtag;
195	word arity;
196	word i;
197	word size;
198	template_t * template;
199	pword * arg;
200
201	if (IsNil(t))
202	{
203	    *template_out = NULL;
204	    return 0;
205	}
206	Check_Structure(t);
207
208	did =  v.ptr->val.did;
209	arity = DidArity(did);
210
211	if (!(template = (template_t *) malloc(sizeof(template_t))))
212        {
213	    raise_dbi_error(DBI_MEMORY);
214	    return -1;
215	}
216	memset(template, 0, sizeof(template_t));
217
218	template->did = did;
219	template->arity = arity;
220	if (!(template->map = (map_t *) malloc(arity * sizeof(map_t))))
221        {
222	    raise_dbi_error(DBI_MEMORY);
223	    return -1;
224	}
225	memset(template->map, 0, arity * sizeof(map_t));
226
227	for( i = 0 ; i < arity ; i ++)
228	{
229	    arg = v.ptr+i+1;
230	    Dereference_(arg);
231	    argtag = TagType(arg->tag) ;
232	    template->map[i].prolog_tag = argtag;
233	    switch (argtag) {
234	    case TDICT:
235		/* LONG VARCHAR */
236	    	template->map[i].ext_type = MYSQL_TYPE_VAR_STRING;
237		size = atoi( DidName(arg->val.did) );
238		if ( size && size > 0)
239		{
240		    template->map[i].size = RoundupSize(size);
241		}
242		else
243		{
244		    template->map[i].size =  DEFAULT_BUFFER_SIZE;
245		}
246	    	break;
247	    case TSTRG:
248		/* LONG VARCHAR */
249	    	template->map[i].ext_type = MYSQL_TYPE_VAR_STRING;
250		size = atoi( StringStart(arg->val) );
251		if ( size && size > 0 )
252		{
253		    template->map[i].size = RoundupSize(size);
254		}
255		else
256		{
257		    template->map[i].size = DEFAULT_BUFFER_SIZE;
258		}
259	    	break;
260	    case TINT:
261		/* signed integer */
262#ifdef HAVE_MYSQLBIGINT /* buffer for 64 bit integers if available */
263		template->map[i].ext_type = MYSQL_TYPE_LONGLONG;
264#else
265		template->map[i].ext_type = MYSQL_TYPE_LONG;
266#endif
267		template->map[i].size =  sizeof(BUFINT);
268	    	break;
269	    case TDBL:
270		template->map[i].ext_type = MYSQL_TYPE_DOUBLE;
271		template->map[i].size =  sizeof(double);
272	    	break;
273	    default:
274		/* disable typechecking */
275		template->map[i].prolog_tag = 0;
276
277		template->map[i].ext_type = MYSQL_TYPE_BLOB;
278		if (argtag == TCOMP &&
279		    TagType((arg->val.ptr + 1)->tag) == TINT
280		   )
281		    template->map[i].size =
282				    RoundupSize((arg->val.ptr + 1)->val.nint);
283		else
284		    template->map[i].size = DEFAULT_BUFFER_SIZE*2;
285	    	break;
286	    }
287	}
288#ifdef DEBUG
289	for( i = 0 ; i < arity ; i ++)
290	{
291		fprintf(stderr,"DEBUG template i %d size %d etype %d\n",
292			i, template->map[i].size, template->map[i].ext_type);
293	}
294	fprintf(stderr, "DEBUG template_get template=0x%x m=0x%x\n",
295		template,template->map);
296#endif
297
298	*template_out = template;
299	Succeed;
300}
301
302/* Construct a Prolog structure for a row of tuple results, copying the data
303   from the tuple buffer. For tuple (output) templates only
304   tuple_num is for compatibility for the Oracle code only and is not used
305   For MySQL, tuple_num must be 0.
306*/
307int
308template_put(int tuple_num, template_t * template,sql_t sql_type,
309	     void * buffer, void * lengths, pword * tuple)
310{
311    word i;
312    word arg;
313    map_t * argmap;
314    char * argbuf;
315    pword *pw;
316    char *s;
317    double d;
318    pword * res;
319    extern pword * dbformat_to_term(char *, dident, type);
320
321    pw = TG;
322    Make_Struct(tuple , pw);
323    Push_Struct_Frame(template->did);
324    for (arg = 0 ; arg < template->arity ; arg++)
325    {
326	/* For prepared statements, buffers with sizes specified by the
327           results tuple template is used to receive the results. A
328           MYSQL_DATA_TRUNCATED result code is returned by
329           mysql_stmt_fetch() if the returned results are truncated.
330	*/
331	argmap = &(template->map[arg]);
332	if (sql_type == prepared)
333	{
334	    if (argmap->is_null)
335	    {/* NULL value */
336		Make_Var(&pw[arg+1]);
337		continue;
338	    }
339	    argbuf = ((char *)buffer) + argmap->offset;
340
341	} else /* if (sql_type == direct) */
342	{
343	    /* For direct statements, the buffer sizes specified
344               by the results tuple template can be ignored, because the
345               results are returned as a byte stream by MySQL, which is then
346               converted to ECLiPSe data structure, using the global stack
347               as needed
348	    */
349	    argbuf = (char *) ((MYSQL_ROW)buffer)[arg];
350	    if (argbuf == NULL)
351	    {/* NULL value */
352		Make_Var(&pw[arg+1]);
353		continue;
354	    }
355	}
356	switch(argmap->prolog_tag)
357	{
358	case TDICT:
359	    /* lengths is a unsigned long * rather than uword *, as defined
360               in MySQL.
361	    */
362	    argbuf[((unsigned long *)lengths)[arg] ] = '\0';
363	    Make_Atom(&pw[arg+1], Did(argbuf, 0));
364	    break;
365	case TSTRG:
366	    pw[arg+1].tag.kernel = TSTRG;
367	    Make_Stack_String(((unsigned long *)lengths)[arg] , pw[arg+1].val, s);
368	    Copy_Bytes(s, argbuf, ((unsigned long *)lengths)[arg]);
369	    s[ ((unsigned long *)lengths)[arg] ] = '\0';
370	    break;
371	case TINT:
372	    /* signed integer */
373	    if (sql_type == prepared)
374	    {
375#if defined(HAVE_MYSQLBIGINT)
376		/* may convert to ECLiPSe TBIG if required */
377		tag_desc[TBIG].arith_op[ARITH_BOXLONGLONG](*(long_long *)argbuf, &pw[arg+1]);
378#else
379		Make_Integer( &pw[arg+1], *(word *)argbuf);
380#endif
381	    } else
382	    {
383#if defined(HAVE_MYSQLBIGINT)
384		long_long i;
385#ifdef _WIN32
386		if (sscanf(((MYSQL_ROW)buffer)[arg],"%I64d",&i) == 0) {
387#else
388		if (sscanf(((MYSQL_ROW)buffer)[arg],"%lld",&i) == 0) {
389#endif
390		    raise_dbi_error(DBI_TYPE_CONV); /* no integer read */
391		    return -1;
392		}
393
394		/* may convert to ECLiPSe TBIG if required */
395		tag_desc[TBIG].arith_op[ARITH_BOXLONGLONG](i, &pw[arg+1]);
396#else
397		word i;
398		if (sscanf(((MYSQL_ROW)buffer)[arg],"%ld",&i) == 0) {
399		    raise_dbi_error(DBI_TYPE_CONV);
400		    return -1;
401		}
402
403		Make_Integer(&pw[arg+1], i);
404#endif
405	    }
406	    break;
407	case TDBL:
408	    if (sql_type == prepared)
409	    {
410		Make_Float( &pw[arg+1], *(double *)argbuf);
411	    } else
412	    {
413		double f;
414		if (sscanf(((MYSQL_ROW)buffer)[arg],"%lf",&f) == 0) {
415		    raise_dbi_error(DBI_TYPE_CONV);
416		    return -1;
417		}
418
419		Make_Float(&pw[arg+1], f);
420	    }
421
422	    break;
423/* no longer supported
424	case TFLOAT:
425	    d = *(float *)argbuf;
426	    Make_Float( &pw[arg+1], d);
427	    break;
428*/
429	default:
430	    /* RAW -- check for dbformat header, assume rest is
431               in dbformat
432	    */
433	    for (i=0; i<DBF_HEADER_LEN; i++)
434	    {
435		if (argbuf[i] != dbformat_header[i])
436		{
437		    TG = pw;
438		    Bip_Error(TYPE_ERROR);
439		}
440	    }
441	    res = dbformat_to_term( argbuf+DBF_HEADER_LEN, 0, tdict);
442	    if (NULL == res)
443	    {
444		/* bad error but probably won't happen since, if the
445		 * string is bad we will probably overflow
446		 */
447		TG = pw;
448		Bip_Error(TYPE_ERROR);
449	    }
450/*	    if(IsMut(res->tag))
451	    {
452		Make_Ref( &(pw[arg+1]) , res);
453	    }
454	    else */
455	    {
456		pw[arg+1] = *res;
457	    }
458	    break;
459	}
460    }
461
462    Succeed;
463}
464
465#define BindLong(lbuf, buf, max, start, len)\
466	{\
467	    *(lbuf) = (len);\
468	    if ( *(lbuf) > (max) ) {\
469	    	raise_dbi_error(DBI_BUFFER_OVER);\
470                return -1;\
471            }\
472	    Copy_Bytes((buf),(start),(len));\
473	}
474
475/* based on BindLong, the header for the dbformat is placed at the start
476   of the buffer before the dbformatted string is copied
477*/
478#define BindDbFormat(lbuf, buf, max, start, len) \
479	{\
480	    *(lbuf) = (len)+DBF_HEADER_LEN;\
481	    if ( *(lbuf) > (max) ) {\
482	    	raise_dbi_error(DBI_BUFFER_OVER);\
483                return -1;\
484            }\
485	    Copy_Bytes((buf),dbformat_header,DBF_HEADER_LEN);\
486	    Copy_Bytes((buf)+DBF_HEADER_LEN,(start),(len));\
487	}
488
489/* bind the actual data supplied in Prolog structure tuple to the buffers in
490   template (used for input (param) templates)
491*/
492int
493template_bind(int tuple_num, template_t * template,char * buffer,void * lengths,pword * tuple)
494{
495    word i;
496    word j;
497    pword * arg;
498    value argval;
499    map_t * m;
500    char * argbuf;
501    unsigned long * largbuf;
502    extern pword * term_to_dbformat(pword *,dident);
503
504#ifdef DEBUG
505    fprintf(stderr,"tuple_num=%d template=0x%x buffer=0x%x tuple=0x%x\n",
506            tuple_num, template, buffer, tuple);
507#endif
508
509    /* if there is no template */
510    if (IsNil(tuple->tag) && template == NULL) { Succeed; }
511
512    Check_Structure(tuple->tag)
513    if (template->did != tuple->val.ptr->val.did)
514	Bip_Error(TYPE_ERROR);
515
516    for (i = 0 ; i < template->arity ; i++)
517    {
518	m = &(template->map[i]);
519	largbuf = &(((unsigned long *)lengths)[i]);
520	arg = tuple->val.ptr + i + 1;
521#ifdef DEBUG
522	fprintf(stderr,"m=0x%x arg=0x%x\n",m,arg);
523#endif
524	Dereference_(arg);
525	if (IsRef(arg->tag))
526	{
527	    m->is_null = 1;
528	    continue;
529	}
530	m->is_null = 0;
531
532	argbuf  = buffer + m->offset  + tuple_num * m->increment;
533	*largbuf = m->size; /* maximum size (buffer size) */
534
535	if (m->prolog_tag && (TagType(arg->tag) != m->prolog_tag))
536	{
537#ifdef DEBUG
538	    fprintf(stderr,"DEBUG m tag %d arg tag %d\n",
539	    	m->prolog_tag, TagType(arg->tag));
540#endif
541#if defined(HAVE_LONG_LONG) && SIZEOF_LONG == 4
542	    /* allow TBIG for integers between 32 and 64 bits */
543	    if (!(TagType(arg->tag) == TBIG && m->prolog_tag == TINT))
544#endif
545		Bip_Error(TYPE_ERROR);
546	}
547	switch(m->prolog_tag)
548	{
549	case TDICT:
550	    BindLong(largbuf, argbuf, m->size,
551	    	DidName(arg->val.did), DidLength(arg->val.did));
552#ifdef DEBUG
553	    for (j=0 ; j < *largbuf ; j++)
554		    fprintf(stderr,"%02x ",argbuf[j]);
555	    fprintf(stderr,"\n");
556#endif
557	    break;
558	case TSTRG:
559	    /*
560	     * Build up a LONG VARCHAR which consists of a 32 bit
561	     * string length + chars in non null terminated string
562	     */
563	    BindLong(largbuf, argbuf, m->size,
564	    	StringStart(arg->val), StringLength(arg->val));
565#ifdef DEBUG
566	    for (j=0 ; j < *largbuf ; j++)
567		    fprintf(stderr,"%02x ",argbuf[j]);
568	    fprintf(stderr,"\n");
569#endif
570	    break;
571	case TINT:
572#ifdef DEBUG
573	    fprintf(stderr,"DEBUG bind int argbuf 0x%x int %d\n",
574	    				argbuf, arg->val.nint);
575#endif
576#if defined(HAVE_MYSQLLONGLONG) && SIZEOF_LONG == 4
577	    if (TagType(arg->tag) == TBIG)
578	    {/* convert TBIG integers of between 33 and 64 bits to long long */
579		int res;
580		res = tag_desc[TBIG].arith_op[ARITH_TOCLONGLONG](arg->val.ptr, argbuf);
581		if (res != PSUCCEED) Bip_Error(res);
582		break;
583	    } else
584#endif
585	    /* BUFINT sized signed integer */
586	    *(BUFINT *) argbuf  =  (BUFINT) arg->val.nint;
587	    break;
588	case TDBL:
589	    *(double *) argbuf  =  Dbl(arg->val);
590	    break;
591	default:
592	    {
593	    	pword * old_tg = TG;
594		pword * ext;
595
596		ext = term_to_dbformat(arg,NULL);
597		if (NULL == ext)
598		    Bip_Error(TYPE_ERROR);
599		BindDbFormat(largbuf, argbuf, m->size,
600		    (char *) BufferStart(ext), BufferSize(ext));
601	    	TG = old_tg;
602	    }
603	}
604    }
605    Succeed;
606}
607
608/* ----------------------------------------------------------------------
609 *  Stubs
610 * ---------------------------------------------------------------------- */
611void
612session_init(session_t ** session)
613{
614	session_t * s;
615
616	*session = NULL;
617
618	if (!(s = (session_t *) malloc(sizeof(session_t))))
619        {
620	    raise_dbi_error(DBI_BAD_SESSION);
621	    return;
622	}
623	memset(s, 0, sizeof(session_t));
624	s->mysql = mysql_init(NULL);
625
626	if (s->mysql == NULL) /* init failed */
627	{/* cannot raise mysql error here -- no mysql handle! */
628	    raise_dbi_error(DBI_BAD_SESSION);
629	    free(s);
630	    return;
631	}
632
633	NoErrors;
634	*session = s;
635	return;
636
637}
638
639int
640session_start(session_t * s, char * username, char * host, char * password, value v_opts)
641{
642	char * dbname = NULL;
643	pword * optarg;
644	char * engine = NULL;
645	dident optdid = v_opts.ptr->val.did;
646
647	if (host[0] == '\0') host = NULL; /* emptry string -> no hostname */
648
649	/* processing options */
650	if (strcmp(DidName(optdid), "options") != 0)
651	{
652	    raise_dbi_error(DBI_BAD_FIELD);
653	    return -1;
654	}
655
656	optarg = v_opts.ptr+SESSION_OPT_DBNAME;
657	Dereference_(optarg);
658	if (!IsRef(optarg->tag))
659	{
660	    switch (TagType(optarg->tag))
661	    {
662	    case TSTRG:
663		dbname = StringStart(optarg->val);
664		break;
665	    case TDICT:
666		dbname = DidName(optarg->val.did);
667		break;
668	    default:
669		raise_dbi_error(DBI_BAD_FIELD);
670		return -1; /* incompatible type */
671		break;
672	    }
673	}
674
675	optarg = v_opts.ptr+SESSION_OPT_STORAGE;
676	Dereference_(optarg);
677	if (!IsRef(optarg->tag))
678	{
679	    char * trans_type;
680	    switch (TagType(optarg->tag))
681	    {
682	    case TDICT:
683		trans_type = DidName(optarg->val.did);
684		break;
685	    case TSTRG:
686		trans_type = StringStart(optarg->val);
687		break;
688	    default:
689		raise_dbi_error(DBI_BAD_FIELD);
690		return -1;
691	    }
692	    if (strcmp(trans_type, "transactional") == 0)
693	    {
694		engine = "innodb";
695	    }
696	    else if (strcmp(trans_type, "nontransactional") == 0)
697	    {
698		engine = "myisam";
699	    }
700	}
701
702	if (!mysql_real_connect(s->mysql, host, username, password, dbname, 0, NULL, CLIENT_MULTI_STATEMENTS))
703	{
704	    raise_mysql_error(s->mysql);
705	    return -1;
706	}
707	mysql_autocommit(s->mysql, 0);
708	/* select @@storage_engine */
709	if (engine)
710	{
711	    char sql_set[50] = "set @@storage_engine=";
712
713	    strcat(sql_set, engine);
714	    if (!mysql_query(s->mysql, sql_set))
715	    {
716		raise_mysql_error(s->mysql);
717		return -1;
718	    }
719	}
720
721	NoErrors;
722	return 0;
723}
724
725void
726session_error_value( session_t * session, int * code, char ** msg)
727{
728	*code = err_code;
729	*msg =  (char*) err_msg;
730
731}
732
733int
734session_commit(session_t * session)
735{
736
737	if (mysql_commit(session->mysql))
738	{
739	    raise_mysql_error(session->mysql);
740	    return -1;
741	}
742
743	return 0;
744}
745
746int
747session_rollback(session_t * session)
748{
749	if (mysql_rollback(session->mysql))
750	{
751	    raise_mysql_error(session->mysql);
752	    return -1;
753	}
754
755	return 0;
756}
757
758/* initialise and prepare a cursor (no bindings) */
759cursor_t *
760session_sql_prepare(session_t * session, char * SQL, word length, char use_prepared)
761{
762	cursor_t * cursor;
763
764
765	if (!(cursor = (cursor_t *) malloc(sizeof(cursor_t))))
766        {
767	    raise_dbi_error(DBI_MEMORY);
768	    return NULL;
769	}
770	memset(cursor, 0, sizeof(cursor_t));
771
772	cursor->session = session;
773	cursor->state = closed;
774        cursor->sql_type = (use_prepared ? prepared : direct);
775
776	if (use_prepared)
777	{/* prepared statement */
778	    cursor->sql_length = 0;
779	    cursor->s.stmt = mysql_stmt_init(session->mysql);
780	    if (cursor->s.stmt == NULL)
781	    {
782		raise_mysql_error(session->mysql);
783		free(cursor);
784		return NULL;
785	    }
786
787	    session->refs++; /* incremented before cursor_free() can be called */
788	    if (mysql_stmt_prepare(cursor->s.stmt, SQL, (unsigned long)length) != 0)
789	    {
790		raise_mysql_stmt_error(cursor->s.stmt);
791		cursor_free(cursor);
792		return NULL;
793	    }
794	} else
795	{/* interprete SQL as string */
796	    cursor->sql_length = length;
797	    if (!(cursor->s.sql = (char *)malloc(length)))
798	    {
799		raise_dbi_error(DBI_MEMORY);
800		free(cursor);
801		return NULL;
802	    }
803	    memcpy(cursor->s.sql, SQL, length);
804	    session->refs++;
805	}
806
807	cursor->state = opened;
808
809	NoErrors;
810	return cursor;
811}
812
813/* ready the cursor for the SQL statement SQL for both prepared and direct
814   statements. Does not execute or bind the parameters for the statement
815*/
816cursor_t *
817ready_session_sql_cursor(session_t *session, template_t *params, template_t *query,
818		  char *SQL, word length, word N, char use_prepared)
819{
820    cursor_t * cursor;
821    MYSQL_STMT * c;
822    MYSQL_BIND * bind = NULL;
823    MYSQL_RES *resdata = NULL;
824    MYSQL_FIELD *field;
825    char * b;
826    map_t * m;
827    word free_off;
828    word size;
829    unsigned long *tlengths;
830    my_bool * errors;
831#ifdef DEBUG
832    int * mytype;
833#endif
834
835    /* params != NULL only for prepared statements which have (input) parameters */
836    if (params)
837    	cursor = session_sql_prep(session, params, SQL, length, 1);
838    else
839	cursor = session_sql_prepare(session,SQL,length,use_prepared);
840
841    if (NULL == cursor)
842	return NULL;
843
844    if (query == NULL)
845    {/* query template cannot be NULL for a query */
846	raise_dbi_error(DBI_NOT_QUERY);
847	return NULL;
848    }
849
850    cursor->tuple_template = query;
851    query->tuples = N;
852    query->from = 0;
853    query->to = 0;
854
855    if (!use_prepared) return cursor;
856
857    /* prepared statement only from here on */
858    c = cursor->s.stmt;
859
860    free_off = 0;
861
862    resdata = mysql_stmt_result_metadata(c);
863    /* for prepared statement, we can catch the mismatch between template
864       and actual result columns early here. For direct statements, this
865       can only be done after the statement is executed
866    */
867    if (mysql_num_fields(resdata) != query->arity)
868    {
869	TryFree(bind);
870	TryFree(resdata);
871	raise_dbi_error(DBI_BAD_TEMPLATE);
872	return NULL;
873    }
874
875    if (query->arity == 0) return cursor;
876
877    if (!(bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND)*query->arity)))
878    {
879	raise_dbi_error(DBI_MEMORY);
880	return NULL;
881    }
882    memset(bind, 0, sizeof(MYSQL_BIND)*query->arity);
883    if (!(tlengths = (unsigned long *) malloc(query->arity*sizeof(unsigned long))))
884    {
885	raise_dbi_error(DBI_MEMORY);
886	return NULL;
887    }
888    if (!(errors = (my_bool *) malloc(query->arity*sizeof(my_bool))))
889    {
890	raise_dbi_error(DBI_MEMORY);
891	return NULL;
892    }
893
894    unsigned int i;
895    for(i=0 ; i < query->arity ; i++)
896    {
897	m = &(query->map[i]);
898	if (!(field = mysql_fetch_field_direct(resdata, i)))
899	{
900#ifdef DEBUG
901		fprintf(stderr,"DEBUG error fetch field i=%d\n",i);
902#endif
903		goto mysql_error;
904	}
905	m->offset = free_off;
906	m->dbtype = field->type;
907
908	/*
909	 * Make sure sizes are OK.
910	 */
911	switch(field->type)
912	{
913	case MYSQL_TYPE_TINY:
914	case MYSQL_TYPE_SHORT:
915	case MYSQL_TYPE_LONG:
916	case MYSQL_TYPE_INT24:
917	case MYSQL_TYPE_LONGLONG:
918	case MYSQL_TYPE_FLOAT:
919	case MYSQL_TYPE_DOUBLE:
920	case MYSQL_TYPE_YEAR:
921	case MYSQL_TYPE_SET:
922	case MYSQL_TYPE_ENUM:
923	case MYSQL_TYPE_NULL:
924	case MYSQL_TYPE_BIT:
925	case MYSQL_TYPE_DECIMAL:
926	case MYSQL_TYPE_NEWDECIMAL:
927	    /*
928	     * These are types whose string representation will never be
929	     * very long so we can save a bit of buffer space in
930	     * these cases
931	     */
932	    if (m->size > 32) m->size = 32;
933	    break;
934	case MYSQL_TYPE_TIME:
935	case MYSQL_TYPE_DATE:
936	case MYSQL_TYPE_DATETIME:
937	case MYSQL_TYPE_TIMESTAMP:
938	    /* these are types which may require slightly more buffer space */
939	    if (m->size > 255) m->size = 255;
940	    break;
941	case MYSQL_TYPE_STRING:
942	    /* fixed length string, get the length (+1 for \0) */
943	    if (m->prolog_tag != TSTRG && m->prolog_tag != TDICT)
944		goto conversion_error;
945
946	    m->size = field->length+1;
947	    break;
948
949	/* last cases: no clue from DB type as to what size  is needed */
950	case MYSQL_TYPE_VAR_STRING: /* VARCHAR, BINARY data */
951	    if (m->prolog_tag != TSTRG && m->prolog_tag != TDICT)
952		goto conversion_error;
953	    break;
954	case MYSQL_TYPE_BLOB:
955	    if (m->prolog_tag != 0) goto conversion_error;
956	    break;
957/* unsupported types - these should not occur in prepared statements
958	case MYSQL_TYPE_GEOMETRY:
959*/
960	default:
961	    goto conversion_error;
962    	}
963
964	m->increment = m->size;
965	free_off += N * m->size;
966    }
967
968    if (!(b = (char *)malloc(free_off)))
969    {
970	raise_dbi_error(DBI_MEMORY);
971	return NULL;
972    }
973#ifdef DEBUG
974    fprintf(stderr,"DEBUG buffer = 0x%x\n",b);
975#endif
976    cursor->tuple_buffer = b;
977    cursor->tuple_datalengths = tlengths;
978    cursor->tuple_errors = errors;
979
980    for(i=0 ; i < query->arity ; i++)
981    {
982	m = &(query->map[i]);
983	bind[i].buffer_type = m->ext_type;
984	bind[i].buffer = (char *)b+m->offset;
985	bind[i].buffer_length = m->size;
986	bind[i].length = (unsigned long *) &(tlengths[i]);
987	bind[i].is_null = &(m->is_null);
988	m->is_null = 0;
989	bind[i].error = &(errors[i]);
990#ifdef DEBUG
991    for(i=0 ; i < query->arity ; i++)
992	fprintf(stderr,"DEBUG bind[%2d]\toff=%8d,size=%8d,ext_type=%8d,dbtype=%8d\n",
993		i,
994		query->map[i].offset,
995		bind[i].length,
996	        bind[i].buffer_type,
997		query->map[i].dbtype);
998
999    fprintf(stderr,"DEBUG buffer size = %d\n",free_off);
1000#endif
1001    }
1002    if (mysql_stmt_bind_result(cursor->s.stmt, bind))
1003    {
1004	goto mysql_error;
1005    }
1006    TryFree(bind);
1007    TryFree(resdata);
1008    return cursor;
1009
1010mysql_error:
1011    TryFree(bind);
1012    TryFree(resdata);
1013    raise_mysql_error(session->mysql);
1014    cursor_free(cursor);
1015    return NULL;
1016
1017conversion_error:
1018    TryFree(bind);
1019    TryFree(resdata);
1020    raise_dbi_error(DBI_TYPE_CONV);
1021    cursor_free(cursor);
1022    return NULL;
1023
1024}
1025
1026
1027/* prepare the param template's data buffer, and bind them to the DB */
1028cursor_t *
1029session_sql_prep(session_t *session,
1030		template_t *template, char *SQL, word length, word N)
1031{
1032    cursor_t * cursor;
1033    word i,j;
1034    map_t * m;
1035    word free_off, err;
1036    char * b;
1037    MYSQL_BIND *bind = NULL;
1038
1039    cursor = session_sql_prepare(session,SQL,length,1);
1040    if (NULL == cursor)
1041	return NULL;
1042
1043    cursor->param_template = template;
1044    cursor->tuple_template = NULL;
1045    if (template == NULL) return cursor;
1046
1047    template->tuples = N;
1048    template->from = 0;
1049    template->to = 0;
1050
1051    free_off = 0;
1052
1053    if (template->arity != mysql_stmt_param_count(cursor->s.stmt))
1054    {
1055	raise_dbi_error(DBI_BAD_TEMPLATE);
1056	return NULL;
1057    }
1058
1059    for(i=0 ; i < template->arity ; i++)
1060    {
1061	unsigned long size = DEFAULT_BUFFER_SIZE;
1062
1063	m = &(template->map[i]);
1064
1065	m->offset = free_off;
1066
1067
1068	free_off += m->size * N;
1069	m->increment = m->size;
1070/*	m->loffset = free_off;*/
1071	free_off += sizeof(word) * N;
1072
1073	m->increment = m->size;
1074	/*	m->lincrement = sizeof(word);*/
1075    }
1076
1077    if (!(cursor->param_buffer = (char *)malloc(free_off)))
1078    {
1079	raise_dbi_error(DBI_MEMORY);
1080	return NULL;
1081    }
1082    b = cursor->param_buffer;
1083
1084
1085    if (!(bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND)*template->arity)))
1086    {
1087	raise_dbi_error(DBI_MEMORY);
1088	return NULL;
1089    }
1090    memset(bind, 0, sizeof(MYSQL_BIND)*template->arity);
1091
1092    if (!(cursor->param_datalengths = (unsigned long *) malloc(template->arity*sizeof(unsigned long))))
1093    {
1094	raise_dbi_error(DBI_MEMORY);
1095	return NULL;
1096    }
1097    for(i=0 ; i < template->arity ; i++)
1098    {
1099	m = &(template->map[i]);
1100
1101	bind[i].length = &(cursor->param_datalengths[i]);
1102	bind[i].buffer_type = m->ext_type;
1103	bind[i].buffer = (char *) b + m->offset;
1104	bind[i].buffer_length = m->size;
1105	bind[i].is_null = &(m->is_null);
1106    }
1107
1108    if (err=mysql_stmt_bind_param(cursor->s.stmt, bind))
1109    {
1110	TryFree(bind);
1111	raise_mysql_stmt_error(cursor->s.stmt);
1112	cursor_free(cursor);
1113	return NULL;
1114    }
1115
1116#ifdef DEBUG
1117    fprintf(stderr,"DEBUG buffer = 0x%x\n",cursor->param_buffer);
1118#endif
1119    return cursor;
1120}
1121
1122int
1123session_tostr(session_t * session, char *buf, int quoted)
1124{
1125    sprintf(buf, "'MySQLS'(16'%x)", (word) session);
1126    return strlen(buf); /* size of actual string */
1127}
1128
1129void
1130session_close(session_t * session)
1131{
1132    mysql_close(session->mysql);
1133    session->mysql = NULL;
1134    session->closed = 1;
1135    return;
1136}
1137
1138/* sets MySQL specific options for cursor. opts must be a structure */
1139int
1140cursor_set_options(cursor_t * cursor, value v_opts)
1141{
1142    pword * optarg;
1143    char * option;
1144    dident optdid = v_opts.ptr->val.did;
1145
1146    /* processing options */
1147    if (strcmp(DidName(optdid), "options") != 0)
1148    {
1149	raise_dbi_error(DBI_BAD_FIELD);
1150	return -1;
1151    }
1152
1153    optarg = v_opts.ptr+CURSOR_OPT_BUFFER;
1154    Dereference_(optarg);
1155    switch (TagType(optarg->tag))
1156    {
1157    case TSTRG:
1158	option = StringStart(optarg->val);
1159	break;
1160    case TDICT:
1161	option = DidName(optarg->val.did);
1162	break;
1163    default:
1164	raise_dbi_error(DBI_BAD_FIELD);
1165	return -1;
1166	break;
1167    }
1168    cursor->server_cursor = (strcmp(option, "server") == 0 ? 1 : 0);
1169
1170    optarg = v_opts.ptr+CURSOR_OPT_TYPE;
1171    Dereference_(optarg);
1172    switch (TagType(optarg->tag))
1173    {
1174    case TSTRG:
1175	option = StringStart(optarg->val);
1176	break;
1177    case TDICT:
1178	option = DidName(optarg->val.did);
1179	break;
1180    default:
1181	raise_dbi_error(DBI_BAD_FIELD);
1182	return -1;
1183	break;
1184    }
1185    if (strcmp(option, "no_cursor") == 0)
1186    {
1187	cursor->cursor_type = CURSOR_NO_CURSOR;
1188    } else if (strcmp(option, "read_only") == 0)
1189    {
1190	cursor->cursor_type = CURSOR_READ_ONLY;
1191    } else
1192    {
1193	raise_dbi_error(DBI_BAD_FIELD);
1194	return -1;
1195    }
1196    return 0;
1197}
1198
1199/* executes the statement in cursor. For prepared statements, input params
1200   must already be bound
1201*/
1202int
1203cursor_sql_execute(cursor_t * cursor, int with_defaults)
1204{
1205    unsigned int nfield = 0;
1206#ifdef DEBUG
1207    fprintf(stderr,"cursor_sql_execute\n");
1208#endif
1209
1210    if (cursor->session->closed ||
1211	cursor->state == closed ||
1212	cursor->state == idle
1213	)
1214    {
1215	raise_dbi_error(DBI_BAD_CURSOR);
1216	return -1;
1217    }
1218
1219    if (with_defaults)
1220    {
1221	cursor->server_cursor = 0;
1222	cursor->cursor_type = CURSOR_NO_CURSOR;
1223    }
1224
1225    if (cursor->sql_type == prepared)
1226    {/* prepared statememnt */
1227	unsigned long type;
1228
1229	/* free any previous results */
1230	if (cursor->state == executed) mysql_stmt_free_result(cursor->s.stmt);
1231	if (cursor->cursor_type == CURSOR_READ_ONLY)
1232	    type  = CURSOR_TYPE_READ_ONLY;
1233	else
1234	    type = CURSOR_TYPE_NO_CURSOR;
1235	mysql_stmt_attr_set(cursor->s.stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type);
1236	if (mysql_stmt_execute(cursor->s.stmt))
1237	{
1238	    raise_mysql_stmt_error(cursor->s.stmt);
1239	    return -1;
1240	}
1241	cursor->prolog_processed_count = (word) mysql_stmt_affected_rows(cursor->s.stmt);
1242	if (!cursor->server_cursor && /* store result on client side */
1243	    mysql_stmt_store_result(cursor->s.stmt))
1244	{
1245	    raise_mysql_stmt_error(cursor->s.stmt);
1246	    return -1;
1247	}
1248	nfield = mysql_stmt_field_count(cursor->s.stmt);
1249    } else /* if cursor->sql_type == direct */
1250    {
1251	if (cursor->state != opened)
1252	{/* direct statements can only be executed once */
1253	    raise_dbi_error(DBI_NOT_PREPARED);
1254	    return -1;
1255	}
1256
1257	if (mysql_real_query(cursor->session->mysql, cursor->s.sql, cursor->sql_length))
1258	{
1259	    raise_mysql_error(cursor->session->mysql);
1260	    TryFree(cursor->s.sql);
1261	    return -1;
1262	}
1263	TryFree(cursor->s.sql);
1264	cursor->prolog_processed_count = (word) mysql_affected_rows(cursor->session->mysql);
1265	if (cursor->server_cursor)
1266	{/* result on server side */
1267	    cursor->s.res = mysql_use_result(cursor->session->mysql);
1268	} else
1269	{/* result on client side */
1270	    cursor->s.res = mysql_store_result(cursor->session->mysql);
1271	}
1272	if (cursor->s.res == NULL)
1273	{
1274	    nfield = mysql_field_count(cursor->session->mysql);
1275	    if (nfield != 0)
1276	    {/* error - should have returned results */
1277		raise_mysql_error(cursor->session->mysql);
1278		return -1;
1279	    }
1280	} else
1281	{
1282	    nfield = mysql_num_fields(cursor->s.res);
1283	}
1284    }
1285    if (cursor->tuple_template == NULL)
1286    {/* no tuple template, should have no output columns */
1287	if (nfield != 0) {
1288	    raise_dbi_error(DBI_BAD_TEMPLATE);
1289	    return -1;
1290	}
1291    }
1292    else if (cursor->tuple_template->arity != nfield)
1293    {
1294	raise_dbi_error(DBI_BAD_TEMPLATE);
1295	return -1;
1296    }
1297
1298    cursor->state = executed;
1299    return 0;
1300}
1301
1302
1303/* MySQL returns only 1 tuple at a time */
1304int
1305cursor_one_tuple(cursor_t *cursor)
1306{
1307    template_t * t;
1308    void * b;
1309    int err;
1310
1311    if (cursor->session->closed)
1312    {
1313	raise_dbi_error(DBI_BAD_CURSOR);
1314	return -1;
1315    }
1316
1317    if (! cursor->tuple_template)
1318    {
1319	raise_dbi_error(DBI_NOT_QUERY);
1320	return -1;
1321    }
1322
1323    t = cursor->tuple_template;
1324    b = cursor->tuple_buffer;
1325
1326#ifdef DEBUG
1327/*    fprintf(stderr,"DEBUG: oexfet rpc = %d from = %d to = %d\n",
1328      cursor->cda.rpc, t->from, t->to);*/
1329    fprintf(stderr,"DEBUG: state = %d\n",cursor->state);
1330#endif
1331
1332    if (cursor->state ==  nodata)
1333    {
1334	cursor->state = idle;
1335	return 0;
1336    }
1337
1338    if (cursor->state ==  idle)
1339    {
1340	/*
1341	 * Application is trying to read on after it has already
1342	 * been told the cursor is not giving any more data
1343	 * or it has, itself cancelled the cursor
1344	 */
1345	raise_dbi_error(DBI_CANCELLED);
1346	return -1;
1347    }
1348
1349    if (cursor->state == opened)
1350    {
1351	if (cursor->param_template != NULL)
1352	{/* has parameters, but these are not yet bound to actual values */
1353	    raise_dbi_error(DBI_NO_PARAM);
1354	    return -1;
1355	}
1356	if (err = cursor_sql_execute(cursor, 1)) return err;
1357    }
1358
1359    /* using from..to is for compatibility with Oracle oci */
1360    t->to = t->from;
1361    if (cursor->state == executed) /* parameters set or */
1362    {
1363#ifdef DEBUG
1364	fprintf(stderr,"DEBUG: tuples = %d\n",t->tuples);
1365#endif
1366	if (cursor->sql_type == prepared)
1367	{
1368	    switch (mysql_stmt_fetch(cursor->s.stmt))
1369	    {
1370	    case MYSQL_NO_DATA:
1371		mysql_stmt_free_result(cursor->s.stmt);
1372		cursor->state = nodata;
1373		return 0;
1374		break;
1375	    case 1: /* error */
1376		raise_mysql_stmt_error(cursor->s.stmt);
1377		return -1;
1378		break;
1379	    case MYSQL_DATA_TRUNCATED:
1380		/* truncated  - cinversion problems or buffer(s) too small */
1381		raise_dbi_error(DBI_DATA_TRUNC);
1382		return -1;
1383		break;
1384	    /* otherwise, there is no problem */
1385	    }
1386
1387	} else
1388	{
1389	    /* note the results and lengths are stored in the s.res structure.
1390               this is freed when the result is freed
1391	    */
1392	    if (cursor->s.res == NULL)
1393	    {/* no result to return */
1394		cursor->state = nodata;
1395		return 0;
1396	    }
1397	    if ((cursor->tuple_buffer  = (void *) mysql_fetch_row(cursor->s.res)) == NULL)
1398	    {
1399		mysql_free_result(cursor->s.res);
1400		cursor->tuple_datalengths = NULL;
1401		cursor->tuple_errors = NULL;
1402		cursor->state = nodata;
1403		return 0;
1404	    }
1405	    cursor->tuple_datalengths = mysql_fetch_lengths(cursor->s.res);
1406	}
1407    }
1408
1409    else
1410    {
1411	raise_dbi_error(DBI_BAD_CURSOR);
1412	return -1;
1413    }
1414    t->to = t->from + 1;
1415
1416
1417#ifdef DEBUG
1418/*    fprintf(stderr,"DEBUG: oexfet rpc = %d from = %d to = %d\n",
1419      cursor->cda.rpc, t->from, t->to);*/
1420#endif
1421
1422    return 0;
1423}
1424
1425int
1426cursor_N_tuples(cursor_t * cursor, word * n, pword * tuple_listp, pword ** tp)
1427{
1428    pword * head;
1429    template_t * template;
1430    int res;
1431
1432    template = cursor->tuple_template;
1433
1434    *tp = tuple_listp;
1435    for (*n=0; ; (*n)++)
1436    {
1437	/*
1438	 * At this point *cdr is a pointer to the cell being created
1439	 * in this loop. I.e the cdr of the previous cell or the whole
1440	 * list
1441	 */
1442
1443	if (cursor->state == nodata) break; /* exhausted data previously, just return */
1444	if (cursor_one_tuple(cursor) == -1) return -1; /* error has occurred */
1445	if (template->to == template->from) break; /* reached end */
1446
1447	/* new cons cell */
1448	head = TG;
1449	Push_List_Frame();
1450	Make_List(*tp, head);
1451	if (res = template_put(*n, template, cursor->sql_type,
1452		cursor->tuple_buffer, cursor->tuple_datalengths, head))
1453	{
1454	    return res;
1455	}
1456	*tp = head + 1;
1457    }
1458    Make_Var(*tp);
1459    template->from = template->to;
1460
1461    return 0;
1462}
1463
1464int
1465cursor_N_execute(cursor_t * cursor, word * tuplep, value v_tuples, type t_tuples, pword ** cdrp)
1466{
1467    int res;
1468    pword * car;
1469
1470
1471    for (	*tuplep = 0 ;
1472    		IsList(t_tuples) ;
1473		(*tuplep)++)
1474    {
1475	car = v_tuples.ptr;
1476	*cdrp = car + 1;
1477	Dereference_(car);               /* access the data */
1478
1479	if (res = template_bind(0, cursor->param_template,
1480		       cursor->param_buffer, cursor->param_datalengths, car))
1481	{
1482#ifdef DEBUG
1483		fprintf(stderr,"DEBUG tuple=%d\n",tuplep);
1484#endif
1485		return res;
1486	}
1487
1488	if (cursor_sql_execute(cursor, 1))
1489	{
1490	    return -1;
1491	}
1492
1493	Dereference_((*cdrp));       /* proceed to next element */
1494	t_tuples = (*cdrp)->tag;
1495	v_tuples = (*cdrp)->val;
1496    }
1497
1498
1499    return 0;
1500}
1501
1502int
1503cursor_tostr(cursor_t * cursor, char *buf, int quoted)
1504{
1505    sprintf(buf, "'MySQLC'(16'%x)", (word) cursor);
1506    return strlen(buf); /* size of actual string */
1507}
1508
1509void
1510cursor_free(cursor_t * cursor)
1511{
1512	session_t * s;
1513
1514	if (cursor == NULL) return;
1515	s = cursor->session;
1516
1517	if (cursor->state != closed && ! s->closed )
1518	{
1519
1520	    if (cursor->sql_type == prepared)
1521	    {
1522		/* mysql_stmt_free_result() need not be called --
1523                   results cancelled automatically
1524		*/
1525		if (mysql_stmt_close(cursor->s.stmt)!= 0 )
1526		{
1527		    raise_mysql_stmt_error(cursor->s.stmt);
1528		    /* we can't return an error here because the copy method
1529                       returns void. Just print the error and return
1530		    */
1531		    fprintf(stderr, "MySQl error freeing cursor: %d %s\n", err_code,err_msg);
1532		    NoErrors;
1533		    return;
1534		}
1535	    } else
1536	    { /* direct SQL */
1537		if (cursor->state == executed)
1538		{
1539		    mysql_free_result(cursor->s.res);
1540		    /* freeing the results free the tuple + tuple length
1541                       buffers as well */
1542		    cursor->tuple_buffer = NULL;
1543		    cursor->tuple_datalengths = NULL;
1544		} else if (cursor->state == opened)
1545		{
1546		    TryFree(cursor->s.sql);
1547		}
1548	    }
1549	}
1550
1551	/*
1552	 * free extended cursor
1553	 */
1554
1555#ifdef DEBUG
1556	fprintf(stderr,"cursor free\n");
1557#endif
1558	if (cursor->param_template) {
1559	    TryFree(cursor->param_template->map);
1560	}
1561	TryFree(cursor->param_template);
1562	if (cursor->tuple_template) {
1563	    TryFree(cursor->tuple_template->map);
1564	}
1565	TryFree(cursor->tuple_template);
1566	TryFree(cursor->param_buffer);
1567	TryFree(cursor->tuple_errors);
1568	TryFree(cursor->param_datalengths);
1569	if (cursor->sql_type == prepared)
1570	{/* these are malloc'ed only for prepared statements.
1571	    If session has already been freed, then tuple_buffer etc.
1572            would be freed by MySQL already, even though they still have
1573            non-zero values
1574	 */
1575	    TryFree(cursor->tuple_buffer);
1576	    TryFree(cursor->tuple_datalengths);
1577	}
1578
1579	free(cursor);
1580
1581	/*
1582	 * update session cursor count
1583	 */
1584	if (--(s->refs) == 0)
1585	{
1586	    if (s->closed) free(s);
1587	    else
1588	    {
1589		/* this should not happen */
1590		fprintf(stderr, "Dbi error: problem with session handle reference count. Please report problem.\n");
1591		free(s);
1592	    }
1593	}
1594}
1595
1596int
1597cursor_field_value(cursor_t * cursor, field_t field,void ** value)
1598{
1599    if (cursor->session->closed)
1600    {
1601	raise_dbi_error(DBI_BAD_CURSOR);
1602	return -1;
1603    }
1604    switch(field)
1605    {
1606    case state :
1607	*value = &(cursor->state);
1608    	break;
1609    case rows_processed_count :
1610	/* A coursor has 2 templates now so no 1 processed count.
1611	if (cursor->template)
1612	cursor->prolog_processed_count +=
1613		cursor->template->to - cursor->template->from;
1614	*/
1615	*value = &(cursor->prolog_processed_count);
1616    	break;
1617    case return_code :
1618    	*value = &err_code;
1619    	break;
1620    case return_code_as_string :
1621    	*value = &err_msg;
1622    	break;
1623    default :
1624	raise_dbi_error(DBI_BAD_FIELD);
1625    	return -1;
1626    }
1627
1628    return 0;
1629}
1630
1631/* these initilization and finalization calls does not seem to be strictly
1632   needed, and were introduced only in MySQL 5.0.3, but they are recommended
1633   in the manual
1634*/
1635void
1636dbi_init()
1637{
1638    mysql_library_init(-1, NULL, NULL);
1639}
1640
1641void
1642dbi_final()
1643{
1644    mysql_library_end();
1645}
1646