tdbc::mysql

Check-in [181bfb064b]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Update to latest TEA. Make it work with C++ compiler
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA3-256: 181bfb064b0c4b64d54e69a798e09afb968413489354f2017454a9db474508df
User & Date: jan.nijtmans 2019-08-30 14:53:10
Context
2019-08-30
14:53
Update to latest TEA. Make it work with C++ compiler Leaf check-in: 181bfb064b user: jan.nijtmans tags: trunk
2019-08-29
11:12
Updated to latest Tcl nmake files. Bug fix [d6c4db25560b1a8c3a26f9369e57cf00327cb9b4]. check-in: fba0eae033 user: apnadkarni tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Makefile.in.

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
shell: binaries libraries
	@$(TCLSH) $(SCRIPT)

gdb:
	$(TCLSH_ENV) $(PKG_ENV) $(GDB) $(TCLSH_PROG) $(SCRIPT)

gdb-test: binaries libraries 
	$(TCLSH_ENV) $(PKG_ENV) $(GDB) \
	    --args $(TCLSH_PROG) `@[email protected] $(srcdir)/tests/all.tcl` \
	    $(TESTFLAGS) -singleproc 1 \
	    -load "package ifneeded $(PACKAGE_NAME) $(PACKAGE_VERSION) \
		[list load `@[email protected] $(PKG_LIB_FILE)` $(PACKAGE_NAME)]"

valgrind: binaries libraries






|







251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
shell: binaries libraries
	@$(TCLSH) $(SCRIPT)

gdb:
	$(TCLSH_ENV) $(PKG_ENV) $(GDB) $(TCLSH_PROG) $(SCRIPT)

gdb-test: binaries libraries
	$(TCLSH_ENV) $(PKG_ENV) $(GDB) \
	    --args $(TCLSH_PROG) `@[email protected] $(srcdir)/tests/all.tcl` \
	    $(TESTFLAGS) -singleproc 1 \
	    -load "package ifneeded $(PACKAGE_NAME) $(PACKAGE_VERSION) \
		[list load `@[email protected] $(PKG_LIB_FILE)` $(PACKAGE_NAME)]"

valgrind: binaries libraries

Changes to configure.

718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
...
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
....
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
....
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
....
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
....
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
....
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
pdfdir
dvidir
htmldir
infodir
docdir
oldincludedir
includedir
runstatedir
localstatedir
sharedstatedir
sysconfdir
datadir
datarootdir
libexecdir
sbindir
................................................................................
sbindir='${exec_prefix}/sbin'
libexecdir='${exec_prefix}/libexec'
datarootdir='${prefix}/share'
datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
infodir='${datarootdir}/info'
htmldir='${docdir}'
dvidir='${docdir}'
pdfdir='${docdir}'
................................................................................
  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
    psdir=$ac_optarg ;;

  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
  | -silent | --silent | --silen | --sile | --sil)
    silent=yes ;;

  -runstatedir | --runstatedir | --runstatedi | --runstated \
  | --runstate | --runstat | --runsta | --runst | --runs \
  | --run | --ru | --r)
    ac_prev=runstatedir ;;
  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
  | --run=* | --ru=* | --r=*)
    runstatedir=$ac_optarg ;;

  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
    ac_prev=sbindir ;;
  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
  | --sbi=* | --sb=*)
    sbindir=$ac_optarg ;;

  -sharedstatedir | --sharedstatedir | --sharedstatedi \
................................................................................
  esac
fi

# Check all directory arguments for consistency.
for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
		datadir sysconfdir sharedstatedir localstatedir includedir \
		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
		libdir localedir mandir runstatedir
do
  eval ac_val=\$$ac_var
  # Remove trailing slashes.
  case $ac_val in
    */ )
      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
      eval $ac_var=\$ac_val;;
................................................................................
Fine tuning of the installation directories:
  --bindir=DIR            user executables [EPREFIX/bin]
  --sbindir=DIR           system admin executables [EPREFIX/sbin]
  --libexecdir=DIR        program executables [EPREFIX/libexec]
  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
  --libdir=DIR            object code libraries [EPREFIX/lib]
  --includedir=DIR        C header files [PREFIX/include]
  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
  --infodir=DIR           info documentation [DATAROOTDIR/info]
  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
................................................................................
    # If the user did not set CFLAGS, set it now to keep macros
    # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
    if test "${CFLAGS+set}" != "set" ; then
	CFLAGS=""
    fi

    case "`uname -s`" in
	*win32*|*WIN32*|*MINGW32_*|*MINGW64_*)
	    # Extract the first word of "cygpath", so it can be a program name with args.
set dummy cygpath; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CYGPATH+:} false; then :
  $as_echo_n "(cached) " >&6
else
................................................................................
    # standard manufacturer compiler.

    if test "$GCC" = yes; then :

	case $system in
	    AIX-*) ;;
	    BSD/OS*) ;;
	    CYGWIN_*|MINGW32_*|MINGW64_*) ;;
	    IRIX*) ;;
	    NetBSD-*|DragonFly-*|FreeBSD-*|OpenBSD-*) ;;
	    Darwin-*) ;;
	    SCO_SV-3.2*) ;;
	    windows) ;;
	    *) SHLIB_CFLAGS="-fPIC" ;;
	esac






<







 







<







 







<
<
<
<
<
<
<
<
<







 







|







 







<







 







|







 







|







718
719
720
721
722
723
724

725
726
727
728
729
730
731
...
798
799
800
801
802
803
804

805
806
807
808
809
810
811
....
1050
1051
1052
1053
1054
1055
1056









1057
1058
1059
1060
1061
1062
1063
....
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
....
1340
1341
1342
1343
1344
1345
1346

1347
1348
1349
1350
1351
1352
1353
....
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
....
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
pdfdir
dvidir
htmldir
infodir
docdir
oldincludedir
includedir

localstatedir
sharedstatedir
sysconfdir
datadir
datarootdir
libexecdir
sbindir
................................................................................
sbindir='${exec_prefix}/sbin'
libexecdir='${exec_prefix}/libexec'
datarootdir='${prefix}/share'
datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'

includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
infodir='${datarootdir}/info'
htmldir='${docdir}'
dvidir='${docdir}'
pdfdir='${docdir}'
................................................................................
  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
    psdir=$ac_optarg ;;

  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
  | -silent | --silent | --silen | --sile | --sil)
    silent=yes ;;










  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
    ac_prev=sbindir ;;
  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
  | --sbi=* | --sb=*)
    sbindir=$ac_optarg ;;

  -sharedstatedir | --sharedstatedir | --sharedstatedi \
................................................................................
  esac
fi

# Check all directory arguments for consistency.
for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
		datadir sysconfdir sharedstatedir localstatedir includedir \
		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
		libdir localedir mandir
do
  eval ac_val=\$$ac_var
  # Remove trailing slashes.
  case $ac_val in
    */ )
      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
      eval $ac_var=\$ac_val;;
................................................................................
Fine tuning of the installation directories:
  --bindir=DIR            user executables [EPREFIX/bin]
  --sbindir=DIR           system admin executables [EPREFIX/sbin]
  --libexecdir=DIR        program executables [EPREFIX/libexec]
  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]

  --libdir=DIR            object code libraries [EPREFIX/lib]
  --includedir=DIR        C header files [PREFIX/include]
  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
  --infodir=DIR           info documentation [DATAROOTDIR/info]
  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
................................................................................
    # If the user did not set CFLAGS, set it now to keep macros
    # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
    if test "${CFLAGS+set}" != "set" ; then
	CFLAGS=""
    fi

    case "`uname -s`" in
	*win32*|*WIN32*|*MINGW32_*|*MINGW64_*|*MSYS_*)
	    # Extract the first word of "cygpath", so it can be a program name with args.
set dummy cygpath; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CYGPATH+:} false; then :
  $as_echo_n "(cached) " >&6
else
................................................................................
    # standard manufacturer compiler.

    if test "$GCC" = yes; then :

	case $system in
	    AIX-*) ;;
	    BSD/OS*) ;;
	    CYGWIN_*|MINGW32_*|MINGW64_*|MSYS_*) ;;
	    IRIX*) ;;
	    NetBSD-*|DragonFly-*|FreeBSD-*|OpenBSD-*) ;;
	    Darwin-*) ;;
	    SCO_SV-3.2*) ;;
	    windows) ;;
	    *) SHLIB_CFLAGS="-fPIC" ;;
	esac

Changes to configure.ac.

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# This also calls AC_PROG_CC and a few others to create the basic setup
# necessary to compile executables.
#-----------------------------------------------------------------------

TEA_SETUP_COMPILER

#-----------------------------------------------------------------------
# Setup inlining if available. Check sizeof long long, long. 
#-----------------------------------------------------------------------

AC_C_INLINE
AC_CHECK_TYPE([long long],[
    AC_DEFINE([HAVE_LONG_LONG],[1])
    AC_CHECK_SIZEOF([long long])
],[],[])






|







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# This also calls AC_PROG_CC and a few others to create the basic setup
# necessary to compile executables.
#-----------------------------------------------------------------------

TEA_SETUP_COMPILER

#-----------------------------------------------------------------------
# Setup inlining if available. Check sizeof long long, long.
#-----------------------------------------------------------------------

AC_C_INLINE
AC_CHECK_TYPE([long long],[
    AC_DEFINE([HAVE_LONG_LONG],[1])
    AC_CHECK_SIZEOF([long long])
],[],[])

Changes to generic/mysqlStubInit.c.

122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
MysqlInitStubs(Tcl_Interp* interp)
{
    int status;			/* Status of Tcl library calls */
    Tcl_Obj* path;		/* Path name of a module to be loaded */
    Tcl_Obj* shlibext;		/* Extension to use for load modules */
    Tcl_LoadHandle handle = NULL;
				/* Handle to a load module */
    int i, j;


    /* Determine the shared library extension */

    status = Tcl_EvalEx(interp, "::info sharedlibextension", -1,
			TCL_EVAL_GLOBAL);
    if (status != TCL_OK) return NULL;
    shlibext = Tcl_GetObjResult(interp);






|
>







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
MysqlInitStubs(Tcl_Interp* interp)
{
    int status;			/* Status of Tcl library calls */
    Tcl_Obj* path;		/* Path name of a module to be loaded */
    Tcl_Obj* shlibext;		/* Extension to use for load modules */
    Tcl_LoadHandle handle = NULL;
				/* Handle to a load module */
    int i;
    size_t j;

    /* Determine the shared library extension */

    status = Tcl_EvalEx(interp, "::info sharedlibextension", -1,
			TCL_EVAL_GLOBAL);
    if (status != TCL_OK) return NULL;
    shlibext = Tcl_GetObjResult(interp);

Changes to generic/tdbcmysql.c.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
....
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
....
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
....
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
....
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
....
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
....
3543
3544
3545
3546
3547
3548
3549



3550
3551
3552
3553
3554
3555
3556
3557
....
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
....
3711
3712
3713
3714
3715
3716
3717



3718
3719
3720
3721
3722
3723
3724
};

/*
 * Structure that holds per-interpreter data for the MYSQL package.
 */

typedef struct PerInterpData {
    int refCount;		/* Reference count */
    Tcl_Obj* literals[LIT__END];
				/* Literal pool */
    Tcl_HashTable typeNumHash;	/* Lookup table for type numbers */
} PerInterpData;
#define IncrPerInterpRefCount(x)  \
    do {			  \
	++((x)->refCount);	  \
    } while(0)
#define DecrPerInterpRefCount(x)		\
    do {					\
	PerInterpData* _pidata = x;		\
	if ((--(_pidata->refCount)) <= 0) {	\
	    DeletePerInterpData(_pidata);	\
	}					\
    } while(0)

/*
 * Structure that carries the data for an MYSQL connection
 *
................................................................................
 *	contains its statements is taken down, destroying them. It's
 *	not safe to take down the ConnectionData until nothing is
 *	referring to it, which avoids taking down the hDBC until the
 *	other objects that refer to it vanish.
 */

typedef struct ConnectionData {
    int refCount;		/* Reference count. */
    PerInterpData* pidata;	/* Per-interpreter data */
    MYSQL* mysqlPtr;		/* MySql connection handle */
    unsigned int nCollations;	/* Number of collations defined */
    int* collationSizes;	/* Character lengths indexed by collation ID */
    int flags;
} ConnectionData;

................................................................................
#define IncrConnectionRefCount(x) \
    do {			  \
	++((x)->refCount);	  \
    } while(0)
#define DecrConnectionRefCount(x)		\
    do {					\
	ConnectionData* conn = x;		\
	if ((--(conn->refCount)) <= 0) {	\
	    DeleteConnection(conn);		\
	}					\
    } while(0)

/*
 * Structure that carries the data for a MySQL prepared statement.
 *
................................................................................
 *	Just as with connections, statements need to defer taking down
 *	their client data until other objects (i.e., result sets) that
 * 	refer to them have had a chance to clean up. Hence, this
 *	structure is reference counted as well.
 */

typedef struct StatementData {
    int refCount;		/* Reference count */
    ConnectionData* cdata;	/* Data for the connection to which this
				 * statement pertains. */
    Tcl_Obj* subVars;	        /* List of variables to be substituted, in the
				 * order in which they appear in the
				 * statement */
    struct ParamData *params;	/* Data types and attributes of parameters */
    Tcl_Obj* nativeSql;		/* Native SQL statement to pass into
................................................................................
#define IncrStatementRefCount(x)		\
    do {					\
	++((x)->refCount);			\
    } while (0)
#define DecrStatementRefCount(x)		\
    do {					\
	StatementData* stmt = (x);		\
	if (--(stmt->refCount) <= 0) {		\
	    DeleteStatement(stmt);		\
	}					\
    } while(0)

/* Flags in the 'StatementData->flags' word */

#define STMT_FLAG_BUSY		0x1	/* Statement handle is in use */
................................................................................
 * Structure describing a MySQL result set.  The object that the Tcl
 * API terms a "result set" actually has to be represented by a MySQL
 * "statement", since a MySQL statement can have only one set of results
 * at any given time.
 */

typedef struct ResultSetData {
    int refCount;		/* Reference count */
    StatementData* sdata;	/* Statement that generated this result set */
    MYSQL_STMT* stmtPtr;	/* Handle to the MySQL statement object */
    Tcl_Obj* paramValues;	/* List of parameter values */
    MYSQL_BIND* paramBindings;	/* Parameter bindings */
    unsigned long* paramLengths;/* Parameter lengths */
    my_ulonglong rowCount;	/* Number of affected rows */
    my_bool* resultErrors;	/* Failure indicators for retrieving columns */
................................................................................
#define IncrResultSetRefCount(x)		\
    do {					\
	++((x)->refCount);			\
    } while (0)
#define DecrResultSetRefCount(x)		\
    do {					\
	ResultSetData* rs = (x);		\
	if (--(rs->refCount) <= 0) {		\
	    DeleteResultSet(rs);		\
	}					\
    } while(0)

/* Table of MySQL type names */

#define IS_BINARY	(1<<16)	/* Flag to OR in if a param is binary */
................................................................................
      "SELECT '', @@SSL_CIPHER" },
    { "-ssl_key",     TYPE_STRING,    INDX_SSLKEY,	  CONN_OPT_FLAG_SSL,
      "SELECT '', @@SSL_KEY" },
    { "-timeout",     TYPE_TIMEOUT,   0,		  CONN_OPT_FLAG_MOD,
      "SELECT '', @@WAIT_TIMEOUT" },
    { "-user",	      TYPE_STRING,    INDX_USER,	  CONN_OPT_FLAG_MOD,
      "SELECT '', USER()" },
    { NULL,	      0,	      0,		  0 }
};

/* Tables of isolation levels: Tcl, SQL, and MySQL 'tx_isolation' */

static const char *const TclIsolationLevels[] = {
    "readuncommitted",
    "readcommitted",
................................................................................
ResultDescToTcl(
    MYSQL_RES* result,		/* Result set description */
    int flags			/* Flags governing the conversion */
) {
    Tcl_Obj* retval = Tcl_NewObj();
    Tcl_HashTable names;	/* Hash table to resolve name collisions */
    Tcl_Obj* nameObj;		/* Name of a result column */
    int new;			/* Flag == 1 if a result column is unique */
    Tcl_HashEntry* entry;	/* Hash table entry for a column name */
    int count;			/* Number used to disambiguate a column name */

    Tcl_InitHashTable(&names, TCL_STRING_KEYS);
    if (result != NULL) {
	unsigned int fieldCount = mysql_num_fields(result);
	MYSQL_FIELD* fields = mysql_fetch_fields(result);
	unsigned int i;
	char numbuf[16];
	for (i = 0; i < fieldCount; ++i) {
	    MYSQL_FIELD* field = MysqlFieldIndex(fields, i);
	    nameObj = Tcl_NewStringObj(field->name, field->name_length);
	    Tcl_IncrRefCount(nameObj);
	    entry = Tcl_CreateHashEntry(&names, field->name, &new);
	    count = 1;
	    while (!new) {
		count = PTR2INT(Tcl_GetHashValue(entry));
		++count;
		Tcl_SetHashValue(entry, INT2PTR(count));
		sprintf(numbuf, "#%d", count);
		Tcl_AppendToObj(nameObj, numbuf, -1);
		entry = Tcl_CreateHashEntry(&names, Tcl_GetString(nameObj),
					    &new);
	    }
	    Tcl_SetHashValue(entry, INT2PTR(count));
	    Tcl_ListObjAppendElement(NULL, retval, nameObj);
	    Tcl_DecrRefCount(nameObj);
	}
    }
    Tcl_DeleteHashTable(&names);
................................................................................
		}

	    case MYSQL_TYPE_FLOAT:
	    case MYSQL_TYPE_DOUBLE:
	    real:
		MysqlBindSetBufferType(rdata->paramBindings, nBound,
				       MYSQL_TYPE_DOUBLE);
		bufPtr = MysqlBindAllocBuffer(rdata->paramBindings,
					      nBound, sizeof(double));
		rdata->paramLengths[nBound] = sizeof(double);
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		if (Tcl_GetDoubleFromObj(interp, paramValObj,
					 (double*) bufPtr) != TCL_OK) {
		    return TCL_ERROR;
................................................................................
		break;

	    case MYSQL_TYPE_BIT:
	    case MYSQL_TYPE_LONGLONG:
	    biginteger:
		MysqlBindSetBufferType(rdata->paramBindings, nBound,
				       MYSQL_TYPE_LONGLONG);
		bufPtr = MysqlBindAllocBuffer(rdata->paramBindings, nBound,
					      sizeof(Tcl_WideInt));
		rdata->paramLengths[nBound] = sizeof(Tcl_WideInt);
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		if (Tcl_GetWideIntFromObj(interp, paramValObj,
					  (Tcl_WideInt*) bufPtr) != TCL_OK) {
		    return TCL_ERROR;
................................................................................
	    case MYSQL_TYPE_TINY:
	    case MYSQL_TYPE_SHORT:
	    case MYSQL_TYPE_INT24:
	    case MYSQL_TYPE_LONG:
	    smallinteger:
		MysqlBindSetBufferType(rdata->paramBindings, nBound,
				       MYSQL_TYPE_LONG);
		bufPtr = MysqlBindAllocBuffer(rdata->paramBindings, nBound,
					      sizeof(int));
		rdata->paramLengths[nBound] = sizeof(int);
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		if (Tcl_GetIntFromObj(interp, paramValObj,
				      (int*) bufPtr) != TCL_OK) {
		    return TCL_ERROR;
................................................................................
		    paramValStr = (char*)
			Tcl_GetByteArrayFromObj(paramValObj, &len);
		} else {
		    MysqlBindSetBufferType(rdata->paramBindings, nBound,
					   MYSQL_TYPE_STRING);
		    paramValStr = Tcl_GetStringFromObj(paramValObj, &len);
		}
		bufPtr = MysqlBindAllocBuffer(rdata->paramBindings, nBound,
					      len+1);
		memcpy(bufPtr, paramValStr, len);
		rdata->paramLengths[nBound] = len;
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		break;

................................................................................
 * Side effects:
 *	Creates the ::tdbc::mysql namespace and the commands that reside in it.
 *	Initializes the MYSQL environment.
 *
 *-----------------------------------------------------------------------------
 */




extern DLLEXPORT int
Tdbcmysql_Init(
    Tcl_Interp* interp		/* Tcl interpreter */
) {
    PerInterpData* pidata;	/* Per-interpreter data for this package */
    Tcl_Obj* nameObj;		/* Name of a class or method being looked up */
    Tcl_Object curClassObject;  /* Tcl_Object representing the current class */
    Tcl_Class curClass;		/* Tcl_Class representing the current class */
................................................................................
    pidata->refCount = 1;
    for (i = 0; i < LIT__END; ++i) {
	pidata->literals[i] = Tcl_NewStringObj(LiteralValues[i], -1);
	Tcl_IncrRefCount(pidata->literals[i]);
    }
    Tcl_InitHashTable(&(pidata->typeNumHash), TCL_ONE_WORD_KEYS);
    for (i = 0; dataTypes[i].name != NULL; ++i) {
	int new;
	Tcl_HashEntry* entry =
	    Tcl_CreateHashEntry(&(pidata->typeNumHash),
				INT2PTR(dataTypes[i].num),
				&new);
	Tcl_Obj* nameObj = Tcl_NewStringObj(dataTypes[i].name, -1);
	Tcl_IncrRefCount(nameObj);
	Tcl_SetHashValue(entry, (ClientData) nameObj);
    }

    /*
     * Find the connection class, and attach an 'init' method to it.
................................................................................

    /*
     * TODO: mysql_thread_init, and keep a TSD reference count of users.
     */

    return TCL_OK;
}



 
/*
 *-----------------------------------------------------------------------------
 *
 * DeletePerInterpData --
 *
 *	Delete per-interpreter data when the MYSQL package is finalized






|











|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|













|

|






|







 







|







 







|







 







|







 







|







 







>
>
>
|







 







|



|







 







>
>
>







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
....
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
....
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
....
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
....
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
....
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
....
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
....
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
....
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
};

/*
 * Structure that holds per-interpreter data for the MYSQL package.
 */

typedef struct PerInterpData {
    size_t refCount;		/* Reference count */
    Tcl_Obj* literals[LIT__END];
				/* Literal pool */
    Tcl_HashTable typeNumHash;	/* Lookup table for type numbers */
} PerInterpData;
#define IncrPerInterpRefCount(x)  \
    do {			  \
	++((x)->refCount);	  \
    } while(0)
#define DecrPerInterpRefCount(x)		\
    do {					\
	PerInterpData* _pidata = x;		\
	if (((_pidata->refCount))-- <= 1) {	\
	    DeletePerInterpData(_pidata);	\
	}					\
    } while(0)

/*
 * Structure that carries the data for an MYSQL connection
 *
................................................................................
 *	contains its statements is taken down, destroying them. It's
 *	not safe to take down the ConnectionData until nothing is
 *	referring to it, which avoids taking down the hDBC until the
 *	other objects that refer to it vanish.
 */

typedef struct ConnectionData {
    size_t refCount;		/* Reference count. */
    PerInterpData* pidata;	/* Per-interpreter data */
    MYSQL* mysqlPtr;		/* MySql connection handle */
    unsigned int nCollations;	/* Number of collations defined */
    int* collationSizes;	/* Character lengths indexed by collation ID */
    int flags;
} ConnectionData;

................................................................................
#define IncrConnectionRefCount(x) \
    do {			  \
	++((x)->refCount);	  \
    } while(0)
#define DecrConnectionRefCount(x)		\
    do {					\
	ConnectionData* conn = x;		\
	if (((conn->refCount)--) <= 01) {	\
	    DeleteConnection(conn);		\
	}					\
    } while(0)

/*
 * Structure that carries the data for a MySQL prepared statement.
 *
................................................................................
 *	Just as with connections, statements need to defer taking down
 *	their client data until other objects (i.e., result sets) that
 * 	refer to them have had a chance to clean up. Hence, this
 *	structure is reference counted as well.
 */

typedef struct StatementData {
    size_t refCount;		/* Reference count */
    ConnectionData* cdata;	/* Data for the connection to which this
				 * statement pertains. */
    Tcl_Obj* subVars;	        /* List of variables to be substituted, in the
				 * order in which they appear in the
				 * statement */
    struct ParamData *params;	/* Data types and attributes of parameters */
    Tcl_Obj* nativeSql;		/* Native SQL statement to pass into
................................................................................
#define IncrStatementRefCount(x)		\
    do {					\
	++((x)->refCount);			\
    } while (0)
#define DecrStatementRefCount(x)		\
    do {					\
	StatementData* stmt = (x);		\
	if ((stmt->refCount--) <= 1) {		\
	    DeleteStatement(stmt);		\
	}					\
    } while(0)

/* Flags in the 'StatementData->flags' word */

#define STMT_FLAG_BUSY		0x1	/* Statement handle is in use */
................................................................................
 * Structure describing a MySQL result set.  The object that the Tcl
 * API terms a "result set" actually has to be represented by a MySQL
 * "statement", since a MySQL statement can have only one set of results
 * at any given time.
 */

typedef struct ResultSetData {
    size_t refCount;		/* Reference count */
    StatementData* sdata;	/* Statement that generated this result set */
    MYSQL_STMT* stmtPtr;	/* Handle to the MySQL statement object */
    Tcl_Obj* paramValues;	/* List of parameter values */
    MYSQL_BIND* paramBindings;	/* Parameter bindings */
    unsigned long* paramLengths;/* Parameter lengths */
    my_ulonglong rowCount;	/* Number of affected rows */
    my_bool* resultErrors;	/* Failure indicators for retrieving columns */
................................................................................
#define IncrResultSetRefCount(x)		\
    do {					\
	++((x)->refCount);			\
    } while (0)
#define DecrResultSetRefCount(x)		\
    do {					\
	ResultSetData* rs = (x);		\
	if ((rs->refCount--) <= 0) {		\
	    DeleteResultSet(rs);		\
	}					\
    } while(0)

/* Table of MySQL type names */

#define IS_BINARY	(1<<16)	/* Flag to OR in if a param is binary */
................................................................................
      "SELECT '', @@SSL_CIPHER" },
    { "-ssl_key",     TYPE_STRING,    INDX_SSLKEY,	  CONN_OPT_FLAG_SSL,
      "SELECT '', @@SSL_KEY" },
    { "-timeout",     TYPE_TIMEOUT,   0,		  CONN_OPT_FLAG_MOD,
      "SELECT '', @@WAIT_TIMEOUT" },
    { "-user",	      TYPE_STRING,    INDX_USER,	  CONN_OPT_FLAG_MOD,
      "SELECT '', USER()" },
    { NULL,	      TYPE_STRING,	      0,		  0, NULL }
};

/* Tables of isolation levels: Tcl, SQL, and MySQL 'tx_isolation' */

static const char *const TclIsolationLevels[] = {
    "readuncommitted",
    "readcommitted",
................................................................................
ResultDescToTcl(
    MYSQL_RES* result,		/* Result set description */
    int flags			/* Flags governing the conversion */
) {
    Tcl_Obj* retval = Tcl_NewObj();
    Tcl_HashTable names;	/* Hash table to resolve name collisions */
    Tcl_Obj* nameObj;		/* Name of a result column */
    int isNew;			/* Flag == 1 if a result column is unique */
    Tcl_HashEntry* entry;	/* Hash table entry for a column name */
    int count;			/* Number used to disambiguate a column name */

    Tcl_InitHashTable(&names, TCL_STRING_KEYS);
    if (result != NULL) {
	unsigned int fieldCount = mysql_num_fields(result);
	MYSQL_FIELD* fields = mysql_fetch_fields(result);
	unsigned int i;
	char numbuf[16];
	for (i = 0; i < fieldCount; ++i) {
	    MYSQL_FIELD* field = MysqlFieldIndex(fields, i);
	    nameObj = Tcl_NewStringObj(field->name, field->name_length);
	    Tcl_IncrRefCount(nameObj);
	    entry = Tcl_CreateHashEntry(&names, field->name, &isNew);
	    count = 1;
	    while (!isNew) {
		count = PTR2INT(Tcl_GetHashValue(entry));
		++count;
		Tcl_SetHashValue(entry, INT2PTR(count));
		sprintf(numbuf, "#%d", count);
		Tcl_AppendToObj(nameObj, numbuf, -1);
		entry = Tcl_CreateHashEntry(&names, Tcl_GetString(nameObj),
					    &isNew);
	    }
	    Tcl_SetHashValue(entry, INT2PTR(count));
	    Tcl_ListObjAppendElement(NULL, retval, nameObj);
	    Tcl_DecrRefCount(nameObj);
	}
    }
    Tcl_DeleteHashTable(&names);
................................................................................
		}

	    case MYSQL_TYPE_FLOAT:
	    case MYSQL_TYPE_DOUBLE:
	    real:
		MysqlBindSetBufferType(rdata->paramBindings, nBound,
				       MYSQL_TYPE_DOUBLE);
		bufPtr = (char *)MysqlBindAllocBuffer(rdata->paramBindings,
					      nBound, sizeof(double));
		rdata->paramLengths[nBound] = sizeof(double);
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		if (Tcl_GetDoubleFromObj(interp, paramValObj,
					 (double*) bufPtr) != TCL_OK) {
		    return TCL_ERROR;
................................................................................
		break;

	    case MYSQL_TYPE_BIT:
	    case MYSQL_TYPE_LONGLONG:
	    biginteger:
		MysqlBindSetBufferType(rdata->paramBindings, nBound,
				       MYSQL_TYPE_LONGLONG);
		bufPtr = (char *)MysqlBindAllocBuffer(rdata->paramBindings, nBound,
					      sizeof(Tcl_WideInt));
		rdata->paramLengths[nBound] = sizeof(Tcl_WideInt);
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		if (Tcl_GetWideIntFromObj(interp, paramValObj,
					  (Tcl_WideInt*) bufPtr) != TCL_OK) {
		    return TCL_ERROR;
................................................................................
	    case MYSQL_TYPE_TINY:
	    case MYSQL_TYPE_SHORT:
	    case MYSQL_TYPE_INT24:
	    case MYSQL_TYPE_LONG:
	    smallinteger:
		MysqlBindSetBufferType(rdata->paramBindings, nBound,
				       MYSQL_TYPE_LONG);
		bufPtr = (char *)MysqlBindAllocBuffer(rdata->paramBindings, nBound,
					      sizeof(int));
		rdata->paramLengths[nBound] = sizeof(int);
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		if (Tcl_GetIntFromObj(interp, paramValObj,
				      (int*) bufPtr) != TCL_OK) {
		    return TCL_ERROR;
................................................................................
		    paramValStr = (char*)
			Tcl_GetByteArrayFromObj(paramValObj, &len);
		} else {
		    MysqlBindSetBufferType(rdata->paramBindings, nBound,
					   MYSQL_TYPE_STRING);
		    paramValStr = Tcl_GetStringFromObj(paramValObj, &len);
		}
		bufPtr = (char *)MysqlBindAllocBuffer(rdata->paramBindings, nBound,
					      len+1);
		memcpy(bufPtr, paramValStr, len);
		rdata->paramLengths[nBound] = len;
		MysqlBindSetLength(rdata->paramBindings, nBound,
				   &(rdata->paramLengths[nBound]));
		break;

................................................................................
 * Side effects:
 *	Creates the ::tdbc::mysql namespace and the commands that reside in it.
 *	Initializes the MYSQL environment.
 *
 *-----------------------------------------------------------------------------
 */

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */
DLLEXPORT int
Tdbcmysql_Init(
    Tcl_Interp* interp		/* Tcl interpreter */
) {
    PerInterpData* pidata;	/* Per-interpreter data for this package */
    Tcl_Obj* nameObj;		/* Name of a class or method being looked up */
    Tcl_Object curClassObject;  /* Tcl_Object representing the current class */
    Tcl_Class curClass;		/* Tcl_Class representing the current class */
................................................................................
    pidata->refCount = 1;
    for (i = 0; i < LIT__END; ++i) {
	pidata->literals[i] = Tcl_NewStringObj(LiteralValues[i], -1);
	Tcl_IncrRefCount(pidata->literals[i]);
    }
    Tcl_InitHashTable(&(pidata->typeNumHash), TCL_ONE_WORD_KEYS);
    for (i = 0; dataTypes[i].name != NULL; ++i) {
	int isNew;
	Tcl_HashEntry* entry =
	    Tcl_CreateHashEntry(&(pidata->typeNumHash),
				INT2PTR(dataTypes[i].num),
				&isNew);
	Tcl_Obj* nameObj = Tcl_NewStringObj(dataTypes[i].name, -1);
	Tcl_IncrRefCount(nameObj);
	Tcl_SetHashValue(entry, (ClientData) nameObj);
    }

    /*
     * Find the connection class, and attach an 'init' method to it.
................................................................................

    /*
     * TODO: mysql_thread_init, and keep a TSD reference count of users.
     */

    return TCL_OK;
}
#ifdef __cplusplus
}
#endif  /* __cplusplus */
 
/*
 *-----------------------------------------------------------------------------
 *
 * DeletePerInterpData --
 *
 *	Delete per-interpreter data when the MYSQL package is finalized

Changes to win/makefile.vc.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#   nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\installdir
#   nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\installdir install
#   nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\installdir test
#
# For other build options (debug, static etc.)
# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
# detailed documentation.
# 
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
#------------------------------------------------------------------------------

PROJECT = tdbcmysql
# Tcl 8.6 etc. compile with /DUNICODE. TDBC pre-nmake reform compiled






|







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#   nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\installdir
#   nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\installdir install
#   nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\installdir test
#
# For other build options (debug, static etc.)
# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
# detailed documentation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
#------------------------------------------------------------------------------

PROJECT = tdbcmysql
# Tcl 8.6 etc. compile with /DUNICODE. TDBC pre-nmake reform compiled