tdbc::odbc

Check-in [3ec6cefd87]
Login
Bounty program for improvements to Tcl and certain Tcl packages.

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

Overview
Comment:New method '$odbcConnection evaldirect' to support SQL code that cannot be prepared or is mangled by the tokenizer (see bug [751477d142]). odbc::connection method 'evaldirect' constructs a subclass 'evaldirectStatement' of odbc::statement that does not tokenize or prepare the SQL statement. The SQL is executed via ODBC API function SQLExecDirectW, and the result set is flattened into a list of dicts (as for '$resultset allrows -as dicts') that is returned by 'evaldirect'. Approach suggested by KBK. Prototype implementation and docs provided; tests to follow.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | bug-751477d142-td
Files: files | file ages | folders
SHA1: 3ec6cefd876818119e49e0323ddcb047d72741e8
User & Date: twylite 2013-01-15 16:50:43
Context
2013-01-15
16:50
New method '$odbcConnection evaldirect' to support SQL code that cannot be prepared or is mangled by the tokenizer (see bug [751477d142]). odbc::connection method 'evaldirect' constructs a subclass 'evaldirectStatement' of odbc::statement that does not tokenize or prepare the SQL statement. The SQL is executed via ODBC API function SQLExecDirectW, and the result set is flattened into a list of dicts (as for '$resultset allrows -as dicts') that is returned by 'evaldirect'. Approach suggested by KBK. Prototype implementation and docs provided; tests to follow. Leaf check-in: 3ec6cefd87 user: twylite tags: bug-751477d142-td
2012-12-10
15:07
Put win/* nmake support files in the distribution. check-in: 10638f86a3 user: dgp tags: trunk, tdbcodbc-1-0-0
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to doc/tdbc_odbc.n.

203
204
205
206
207
208
209
















210
211
212
213
214
215
216
217
218
219
    {Microsoft Access Driver (*.mdb)} \\
    CREATE_DB=[file native [file normalize $fileName]] \\
    General
.CE
Creates a new, empty Microsoft Access database in the file identified
by "$fileName". No connection is made to the database until the
program calls \fBtdbc::odbc::connection create\fR.
















.SH "SEE ALSO"
tdbc(n), tdbc::connection(n),  tdbc::resultset(n), tdbc::statement(n)
.SH "KEYWORDS"
TDBC, SQL, ODBC, database, connectivity, connection
.SH "COPYRIGHT"
Copyright (c) 2008 by Kevin B. Kenny.
.\" Local Variables:
.\" mode: nroff
.\" End:
.\"






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
    {Microsoft Access Driver (*.mdb)} \\
    CREATE_DB=[file native [file normalize $fileName]] \\
    General
.CE
Creates a new, empty Microsoft Access database in the file identified
by "$fileName". No connection is made to the database until the
program calls \fBtdbc::odbc::connection create\fR.
.SH "ADDITIONAL CONNECTION METHODS"
In addition to the usual methods on the tdbc::connection(n) object,
connections to an ODBC database support one additional method:
.IP \fI$connection\fR \fBevaldirect\fR \fIsqlStatement\fR
This method takes the given driver-native SQL code \fIsqlStatement\fR and 
evaluates it without preparing it. The statement is not tokenized and must
not contain variable substitutions.  Evaluating the \fIsqlStatement\fR
produces a result set of zero or more rows.  The result of the command is a
list of dictionaries, with one list element per row in the result set (in
a similar format to the list returned by \fI$connection allrows -as dicts\fI).
\fIThis command is not recommended\fR for anything where the usual
\fIprepare\fR or \fIpreparecall\fR methods work correctly. It is
provided so that data management language statements that are not 
implemented by the driver's prepared statement API (such as \fBCREATE
DATABASE\fR or \fBCREATE PROCEDURE\fR), or that contain characters that
are reserved by the tokenizer, can be executed.
.SH "SEE ALSO"
tdbc(n), tdbc::connection(n),  tdbc::resultset(n), tdbc::statement(n)
.SH "KEYWORDS"
TDBC, SQL, ODBC, database, connectivity, connection
.SH "COPYRIGHT"
Copyright (c) 2008 by Kevin B. Kenny.
.\" Local Variables:
.\" mode: nroff
.\" End:
.\"

Changes to generic/odbcStubDefs.txt.

41
42
43
44
45
46
47

SQLRETURN SQLPrepareW(SQLHSTMT,SQLWCHAR*,SQLINTEGER);
SQLRETURN SQLPrimaryKeysW(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);
SQLRETURN SQLRowCount(SQLHSTMT,SQLLEN*);
SQLRETURN SQLSetConnectAttr(SQLHDBC,SQLINTEGER,SQLPOINTER,SQLINTEGER);
SQLRETURN SQLSetConnectOption(SQLHDBC,SQLUSMALLINT,SQLULEN); /* deprecated */
SQLRETURN SQLSetEnvAttr(SQLHENV,SQLINTEGER,SQLPOINTER,SQLINTEGER);
SQLRETURN SQLTablesW(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);







>
41
42
43
44
45
46
47
48
SQLRETURN SQLPrepareW(SQLHSTMT,SQLWCHAR*,SQLINTEGER);
SQLRETURN SQLPrimaryKeysW(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);
SQLRETURN SQLRowCount(SQLHSTMT,SQLLEN*);
SQLRETURN SQLSetConnectAttr(SQLHDBC,SQLINTEGER,SQLPOINTER,SQLINTEGER);
SQLRETURN SQLSetConnectOption(SQLHDBC,SQLUSMALLINT,SQLULEN); /* deprecated */
SQLRETURN SQLSetEnvAttr(SQLHENV,SQLINTEGER,SQLPOINTER,SQLINTEGER);
SQLRETURN SQLTablesW(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);
SQLRETURN SQLExecDirectW(SQLHSTMT,SQLWCHAR*,SQLINTEGER);

Changes to generic/odbcStubInit.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
71
72
73
74
75
76
77

78
79
80
81
82
83
84
/*
 * odbcStubInit.c --
 *
 *	Stubs tables for the foreign ODBC libraries so that
 *	Tcl extensions can use them without the linker's knowing about them.
 *
 * @[email protected] 2011-01-22 20:11:52Z by genExtStubs.tcl from ../generic/odbcStubDefs.txt
 *
 * Copyright (c) 2010 by Kevin B. Kenny.
 *
 * Please refer to the file, 'license.terms' for the conditions on
 * redistribution of this file and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *-----------------------------------------------------------------------------
................................................................................
    "SQLPrepareW",
    "SQLPrimaryKeysW",
    "SQLRowCount",
    "SQLSetConnectAttr",
    "SQLSetConnectOption",
    "SQLSetEnvAttr",
    "SQLTablesW",

    NULL
    /* @[email protected] */
};

/*
 * Table containing pointers to the functions named above.
 */





|







 







>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/*
 * odbcStubInit.c --
 *
 *	Stubs tables for the foreign ODBC libraries so that
 *	Tcl extensions can use them without the linker's knowing about them.
 *
 * @[email protected] 2013-01-15 10:22:09Z by genExtStubs.tcl from ./odbcStubDefs.txt
 *
 * Copyright (c) 2010 by Kevin B. Kenny.
 *
 * Please refer to the file, 'license.terms' for the conditions on
 * redistribution of this file and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *-----------------------------------------------------------------------------
................................................................................
    "SQLPrepareW",
    "SQLPrimaryKeysW",
    "SQLRowCount",
    "SQLSetConnectAttr",
    "SQLSetConnectOption",
    "SQLSetEnvAttr",
    "SQLTablesW",
    "SQLExecDirectW",
    NULL
    /* @[email protected] */
};

/*
 * Table containing pointers to the functions named above.
 */

Changes to generic/odbcStubs.h.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
41
42
43
44
45
46
47

48
49
50
51
52
53
54
..
72
73
74
75
76
77
78

79
/*
 *-----------------------------------------------------------------------------
 *
 * ../generic/odbcStubs.h --
 *
 *	Stubs for procedures in odbcStubDefs.txt
 *
 * Generated by genExtStubs.tcl: DO NOT EDIT
 * 2011-01-22 20:11:52Z
 *
 *-----------------------------------------------------------------------------
 */

typedef struct odbcStubDefs {

    /* Functions from libraries: odbc32 odbc libodbc32 libodbc */
................................................................................
    SQLRETURN (SQL_API*SQLPrepareWPtr)(SQLHSTMT,SQLWCHAR*,SQLINTEGER);
    SQLRETURN (SQL_API*SQLPrimaryKeysWPtr)(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);
    SQLRETURN (SQL_API*SQLRowCountPtr)(SQLHSTMT,SQLLEN*);
    SQLRETURN (SQL_API*SQLSetConnectAttrPtr)(SQLHDBC,SQLINTEGER,SQLPOINTER,SQLINTEGER);
    SQLRETURN (SQL_API*SQLSetConnectOptionPtr)(SQLHDBC,SQLUSMALLINT,SQLULEN);
    SQLRETURN (SQL_API*SQLSetEnvAttrPtr)(SQLHENV,SQLINTEGER,SQLPOINTER,SQLINTEGER);
    SQLRETURN (SQL_API*SQLTablesWPtr)(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);

} odbcStubDefs;
#define SQLAllocHandle (odbcStubs->SQLAllocHandlePtr)
#define SQLBindParameter (odbcStubs->SQLBindParameterPtr)
#define SQLCloseCursor (odbcStubs->SQLCloseCursorPtr)
#define SQLColumnsW (odbcStubs->SQLColumnsWPtr)
#define SQLDataSourcesW (odbcStubs->SQLDataSourcesWPtr)
#define SQLDescribeColW (odbcStubs->SQLDescribeColWPtr)
................................................................................
#define SQLPrepareW (odbcStubs->SQLPrepareWPtr)
#define SQLPrimaryKeysW (odbcStubs->SQLPrimaryKeysWPtr)
#define SQLRowCount (odbcStubs->SQLRowCountPtr)
#define SQLSetConnectAttr (odbcStubs->SQLSetConnectAttrPtr)
#define SQLSetConnectOption (odbcStubs->SQLSetConnectOptionPtr)
#define SQLSetEnvAttr (odbcStubs->SQLSetEnvAttrPtr)
#define SQLTablesW (odbcStubs->SQLTablesWPtr)

MODULE_SCOPE odbcStubDefs *odbcStubs;


|




|







 







>







 







>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
..
73
74
75
76
77
78
79
80
81
/*
 *-----------------------------------------------------------------------------
 *
 * odbcStubs.h --
 *
 *	Stubs for procedures in odbcStubDefs.txt
 *
 * Generated by genExtStubs.tcl: DO NOT EDIT
 * 2013-01-15 10:22:09Z
 *
 *-----------------------------------------------------------------------------
 */

typedef struct odbcStubDefs {

    /* Functions from libraries: odbc32 odbc libodbc32 libodbc */
................................................................................
    SQLRETURN (SQL_API*SQLPrepareWPtr)(SQLHSTMT,SQLWCHAR*,SQLINTEGER);
    SQLRETURN (SQL_API*SQLPrimaryKeysWPtr)(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);
    SQLRETURN (SQL_API*SQLRowCountPtr)(SQLHSTMT,SQLLEN*);
    SQLRETURN (SQL_API*SQLSetConnectAttrPtr)(SQLHDBC,SQLINTEGER,SQLPOINTER,SQLINTEGER);
    SQLRETURN (SQL_API*SQLSetConnectOptionPtr)(SQLHDBC,SQLUSMALLINT,SQLULEN);
    SQLRETURN (SQL_API*SQLSetEnvAttrPtr)(SQLHENV,SQLINTEGER,SQLPOINTER,SQLINTEGER);
    SQLRETURN (SQL_API*SQLTablesWPtr)(SQLHSTMT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT,SQLWCHAR*,SQLSMALLINT);
    SQLRETURN (SQL_API*SQLExecDirectWPtr)(SQLHSTMT,SQLWCHAR*,SQLINTEGER);
} odbcStubDefs;
#define SQLAllocHandle (odbcStubs->SQLAllocHandlePtr)
#define SQLBindParameter (odbcStubs->SQLBindParameterPtr)
#define SQLCloseCursor (odbcStubs->SQLCloseCursorPtr)
#define SQLColumnsW (odbcStubs->SQLColumnsWPtr)
#define SQLDataSourcesW (odbcStubs->SQLDataSourcesWPtr)
#define SQLDescribeColW (odbcStubs->SQLDescribeColWPtr)
................................................................................
#define SQLPrepareW (odbcStubs->SQLPrepareWPtr)
#define SQLPrimaryKeysW (odbcStubs->SQLPrimaryKeysWPtr)
#define SQLRowCount (odbcStubs->SQLRowCountPtr)
#define SQLSetConnectAttr (odbcStubs->SQLSetConnectAttrPtr)
#define SQLSetConnectOption (odbcStubs->SQLSetConnectOptionPtr)
#define SQLSetEnvAttr (odbcStubs->SQLSetEnvAttrPtr)
#define SQLTablesW (odbcStubs->SQLTablesWPtr)
#define SQLExecDirectW (odbcStubs->SQLExecDirectWPtr)
MODULE_SCOPE odbcStubDefs *odbcStubs;

Changes to generic/tdbcodbc.c.

223
224
225
226
227
228
229




230
231
232
233
234
235
236
...
441
442
443
444
445
446
447




448
449
450
451
452
453
454
...
680
681
682
683
684
685
686
















687
688
689
690
691
692
693
....
1162
1163
1164
1165
1166
1167
1168

1169
1170
1171
1172
1173
1174
1175
....
3225
3226
3227
3228
3229
3230
3231













































































































3232
3233
3234
3235
3236
3237
3238
....
3760
3761
3762
3763
3764
3765
3766



3767
3768
3769
3770
3771
3772
3773
....
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
....
5367
5368
5369
5370
5371
5372
5373


















5374
5375
5376
5377
5378
5379
5380
				 * asking for data type metadata */
#define STATEMENT_FLAG_PRIMARYKEYS 0x20
				/* This flag is set if the statement is
				 * asking for primary key metadata */
#define STATEMENT_FLAG_FOREIGNKEYS 0x40
				/* This flag is set if the statement is
				 * asking for primary key metadata */





/*
 * Structure describing the data types of substituted parameters in
 * a SQL statement.
 */

typedef struct ParamData {
................................................................................
				       int objc, Tcl_Obj *const objv[]);
static int PrimarykeysStatementConstructor(ClientData clientData,
					   Tcl_Interp* interp,
					   Tcl_ObjectContext context,
					   int objc, Tcl_Obj *const objv[]);
static int ForeignkeysStatementConstructor(ClientData clientData,
					   Tcl_Interp* interp,




					   Tcl_ObjectContext context,
					   int objc, Tcl_Obj *const objv[]);
static int TypesStatementConstructor(ClientData clientData, Tcl_Interp* interp,
				     Tcl_ObjectContext context,
				     int objc, Tcl_Obj *const objv[]);
static void DeleteStatementMetadata(ClientData clientData);
static void DeleteStatement(StatementData* sdata);
................................................................................

const static Tcl_MethodType ForeignkeysStatementConstructorType = {
    TCL_OO_METHOD_VERSION_CURRENT,
				/* version */
    "CONSTRUCTOR",		/* name */
    ForeignkeysStatementConstructor,
				/* callProc */
















    NULL,			/* deleteProc */
    NULL			/* cloneProc */
};

/*
 * Constructor type for the class that implements the fake 'statement'
 * used to query the names and attributes of database types.
................................................................................
    SQLRETURN rc;
    SQLHSTMT hStmt;
    ConnectionData* cdata = sdata->cdata;
    if (sdata->flags & (STATEMENT_FLAG_TABLES 
			| STATEMENT_FLAG_COLUMNS
			| STATEMENT_FLAG_PRIMARYKEYS
			| STATEMENT_FLAG_FOREIGNKEYS

			| STATEMENT_FLAG_TYPES)) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot have multiple result "
						  "sets in this context", -1));
	return SQL_NULL_HSTMT;
    }
    rc = SQLAllocHandle(SQL_HANDLE_STMT, cdata->hDBC, &hStmt);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
................................................................................
    DecrStatementRefCount(sdata);
    return TCL_ERROR;
}
 
/*
 *-----------------------------------------------------------------------------
 *













































































































 * TypesStatementConstructor --
 *
 *	C-level initialization for the object representing an ODBC query
 *	for data type metadata
 *
 * Parameters:
 *	Accepts a 3- or 4-element 'objv': 
................................................................................
			    sdata->nativeSqlW, sdata->nativeSqlLen);
    } else if (sdata->flags & STATEMENT_FLAG_FOREIGNKEYS) {
	rc = SQLForeignKeysW(rdata->hStmt, NULL, 0, NULL, 0, 
			     sdata->nativeSqlW, sdata->nativeSqlLen,
			     NULL, 0, NULL, 0,
			     sdata->nativeMatchPatternW, 
			     sdata->nativeMatchPatLen);



    } else {
	rc = SQLExecute(rdata->hStmt);
    }
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO
	&& rc != SQL_NO_DATA) {
	TransferSQLError(interp, SQL_HANDLE_STMT, rdata->hStmt,
			 "(executing the statement)");
................................................................................
    /* Attach the constructor to the 'primarykeysStatement' class */

    Tcl_ClassSetConstructor(interp, curClass,
			    Tcl_NewMethod(interp, curClass, NULL, 1,
					  &PrimarykeysStatementConstructorType,
					  (ClientData) NULL));

    /* Look up the 'typesStatement' class */

    nameObj = Tcl_NewStringObj("::tdbc::odbc::typesStatement", -1);
    Tcl_IncrRefCount(nameObj);
    if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
	Tcl_DecrRefCount(nameObj);
	return TCL_ERROR;
    }
    Tcl_DecrRefCount(nameObj);
    curClass = Tcl_GetObjectAsClass(curClassObject);

    /* Look up the 'foreignkeysStatement' class */

    nameObj = Tcl_NewStringObj("::tdbc::odbc::foreignkeysStatement", -1);
    Tcl_IncrRefCount(nameObj);
    if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
	Tcl_DecrRefCount(nameObj);
	return TCL_ERROR;
................................................................................

    /* Attach the constructor to the 'foreignkeysStatement' class */

    Tcl_ClassSetConstructor(interp, curClass,
			    Tcl_NewMethod(interp, curClass, NULL, 1,
					  &ForeignkeysStatementConstructorType,
					  (ClientData) NULL));



















    /* Look up the 'typesStatement' class */

    nameObj = Tcl_NewStringObj("::tdbc::odbc::typesStatement", -1);
    Tcl_IncrRefCount(nameObj);
    if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
	Tcl_DecrRefCount(nameObj);






>
>
>
>







 







>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>







 







<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
...
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
....
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
....
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
....
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
....
5476
5477
5478
5479
5480
5481
5482











5483
5484
5485
5486
5487
5488
5489
....
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
				 * asking for data type metadata */
#define STATEMENT_FLAG_PRIMARYKEYS 0x20
				/* This flag is set if the statement is
				 * asking for primary key metadata */
#define STATEMENT_FLAG_FOREIGNKEYS 0x40
				/* This flag is set if the statement is
				 * asking for primary key metadata */
#define STATEMENT_FLAG_EVALDIRECT  0x80
				/* This flag is set if the statement is
				 * asking for direct execution (no prepare 
				 * or variable substitution) */

/*
 * Structure describing the data types of substituted parameters in
 * a SQL statement.
 */

typedef struct ParamData {
................................................................................
				       int objc, Tcl_Obj *const objv[]);
static int PrimarykeysStatementConstructor(ClientData clientData,
					   Tcl_Interp* interp,
					   Tcl_ObjectContext context,
					   int objc, Tcl_Obj *const objv[]);
static int ForeignkeysStatementConstructor(ClientData clientData,
					   Tcl_Interp* interp,
					   Tcl_ObjectContext context,
					   int objc, Tcl_Obj *const objv[]);
static int EvaldirectStatementConstructor(ClientData clientData,
					   Tcl_Interp* interp,
					   Tcl_ObjectContext context,
					   int objc, Tcl_Obj *const objv[]);
static int TypesStatementConstructor(ClientData clientData, Tcl_Interp* interp,
				     Tcl_ObjectContext context,
				     int objc, Tcl_Obj *const objv[]);
static void DeleteStatementMetadata(ClientData clientData);
static void DeleteStatement(StatementData* sdata);
................................................................................

const static Tcl_MethodType ForeignkeysStatementConstructorType = {
    TCL_OO_METHOD_VERSION_CURRENT,
				/* version */
    "CONSTRUCTOR",		/* name */
    ForeignkeysStatementConstructor,
				/* callProc */
    NULL,			/* deleteProc */
    NULL			/* cloneProc */
};

/*
 * Method types for the class that implements the 'evaldirect' statement
 * used to execute driver-native SQL code without preparing it or performing
 * variable substitutions.
 */

const static Tcl_MethodType EvaldirectStatementConstructorType = {
    TCL_OO_METHOD_VERSION_CURRENT,
				/* version */
    "CONSTRUCTOR",		/* name */
    EvaldirectStatementConstructor,
				/* callProc */
    NULL,			/* deleteProc */
    NULL			/* cloneProc */
};

/*
 * Constructor type for the class that implements the fake 'statement'
 * used to query the names and attributes of database types.
................................................................................
    SQLRETURN rc;
    SQLHSTMT hStmt;
    ConnectionData* cdata = sdata->cdata;
    if (sdata->flags & (STATEMENT_FLAG_TABLES 
			| STATEMENT_FLAG_COLUMNS
			| STATEMENT_FLAG_PRIMARYKEYS
			| STATEMENT_FLAG_FOREIGNKEYS
			| STATEMENT_FLAG_EVALDIRECT
			| STATEMENT_FLAG_TYPES)) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot have multiple result "
						  "sets in this context", -1));
	return SQL_NULL_HSTMT;
    }
    rc = SQLAllocHandle(SQL_HANDLE_STMT, cdata->hDBC, &hStmt);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
................................................................................
    DecrStatementRefCount(sdata);
    return TCL_ERROR;
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * EvaldirectStatementConstructor --
 *
 *	C-level initialization for the object representing a a driver-native
 *	ODBC query that is not tokenized or prepared.
 *
 * Parameters:
 *	Accepts a 4-element 'objv': 
 *		columnsStatement new $connection $sqlStatement,
 *	where $connection is the ODBC connection object and $sqlStatement is
 *	the driver-native SQL to be executed.
 *
 * Results:
 *	Returns a standard Tcl result
 *
 * Side effects:
 *	Creates an ODBC statement, and stores it (plus a copy of the 
 *	driver-native sqlStatement a reference to the connection) in 
 *	instance metadata.
 *
 *-----------------------------------------------------------------------------
 */

static int
EvaldirectStatementConstructor(
    ClientData clientData,	/* Not used */
    Tcl_Interp* interp,		/* Tcl interpreter */
    Tcl_ObjectContext context,	/* Object context  */
    int objc, 			/* Parameter count */
    Tcl_Obj *const objv[]	/* Parameter vector */
) {

    Tcl_Object thisObject = Tcl_ObjectContextObject(context);
				/* The current statement object */
    int skip = Tcl_ObjectContextSkippedArgs(context);
				/* The number of parameters to skip */
    Tcl_Object connectionObject;
				/* The database connection as a Tcl_Object */
    ConnectionData* cdata;	/* The connection object's data */
    StatementData* sdata;	/* The statement's object data */
    RETCODE rc;			/* Return code from ODBC */


    /* Check param count */

    if (objc != skip+2) {
	Tcl_WrongNumArgs(interp, skip, objv, "connection sqlStatement");
	return TCL_ERROR;
    }

    /* Do not initialize superclasses; this constructor overrides 
     * StatementConstructor so that the SQL is not tokenizer or prepared. */

    /* Find the connection object, and get its data. */

    connectionObject = Tcl_GetObjectFromObj(interp, objv[skip]);
    if (connectionObject == NULL) {
	return TCL_ERROR;
    }
    cdata = (ConnectionData*) Tcl_ObjectGetMetadata(connectionObject,
						    &connectionDataType);
    if (cdata == NULL) {
	Tcl_AppendResult(interp, Tcl_GetString(objv[skip]),
			 " does not refer to an ODBC connection", NULL);
	return TCL_ERROR;
    }

    /*
     * Allocate an object to hold data about this statement
     */

    sdata = NewStatement(cdata, connectionObject);

    /* Allocate an ODBC statement handle */

    rc = SQLAllocHandle(SQL_HANDLE_STMT, cdata->hDBC, &(sdata->hStmt));
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
	TransferSQLError(interp, SQL_HANDLE_DBC, cdata->hDBC, 
			 "(allocating statement handle)");
	goto freeSData;
    }

    /* 
     * Stash the sqlStatement and set a flag to indicate direct execution.
     */

    sdata->nativeSqlW = GetWCharStringFromObj(objv[skip+1],
					      &(sdata->nativeSqlLen));
    sdata->flags = STATEMENT_FLAG_EVALDIRECT;

    /* Attach the current statement data as metadata to the current object */

    Tcl_ObjectSetMetadata(thisObject, &statementDataType, (ClientData) sdata);

    /* Statement will be executed when the statement object's resultSetCreate
     * is called (e.g. via $statement allrows).  In this statement
     * resultSetCreate forwards to ResultSetConstructor. */

    return TCL_OK;

    /* On error, unwind all the resource allocations */

 freeSData:
    DecrStatementRefCount(sdata);
    return TCL_ERROR;
}
 
/*
 *-----------------------------------------------------------------------------
 *
 * TypesStatementConstructor --
 *
 *	C-level initialization for the object representing an ODBC query
 *	for data type metadata
 *
 * Parameters:
 *	Accepts a 3- or 4-element 'objv': 
................................................................................
			    sdata->nativeSqlW, sdata->nativeSqlLen);
    } else if (sdata->flags & STATEMENT_FLAG_FOREIGNKEYS) {
	rc = SQLForeignKeysW(rdata->hStmt, NULL, 0, NULL, 0, 
			     sdata->nativeSqlW, sdata->nativeSqlLen,
			     NULL, 0, NULL, 0,
			     sdata->nativeMatchPatternW, 
			     sdata->nativeMatchPatLen);
    } else if (sdata->flags & STATEMENT_FLAG_EVALDIRECT) {
	rc = SQLExecDirectW(rdata->hStmt, sdata->nativeSqlW, 
			    sdata->nativeSqlLen);
    } else {
	rc = SQLExecute(rdata->hStmt);
    }
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO
	&& rc != SQL_NO_DATA) {
	TransferSQLError(interp, SQL_HANDLE_STMT, rdata->hStmt,
			 "(executing the statement)");
................................................................................
    /* Attach the constructor to the 'primarykeysStatement' class */

    Tcl_ClassSetConstructor(interp, curClass,
			    Tcl_NewMethod(interp, curClass, NULL, 1,
					  &PrimarykeysStatementConstructorType,
					  (ClientData) NULL));












    /* Look up the 'foreignkeysStatement' class */

    nameObj = Tcl_NewStringObj("::tdbc::odbc::foreignkeysStatement", -1);
    Tcl_IncrRefCount(nameObj);
    if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
	Tcl_DecrRefCount(nameObj);
	return TCL_ERROR;
................................................................................

    /* Attach the constructor to the 'foreignkeysStatement' class */

    Tcl_ClassSetConstructor(interp, curClass,
			    Tcl_NewMethod(interp, curClass, NULL, 1,
					  &ForeignkeysStatementConstructorType,
					  (ClientData) NULL));

    /* Look up the 'evaldirectStatement' class */

    nameObj = Tcl_NewStringObj("::tdbc::odbc::evaldirectStatement", -1);
    Tcl_IncrRefCount(nameObj);
    if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
	Tcl_DecrRefCount(nameObj);
	return TCL_ERROR;
    }
    Tcl_DecrRefCount(nameObj);
    curClass = Tcl_GetObjectAsClass(curClassObject);

    /* Attach the constructor to the 'evaldirectStatement' class */

    Tcl_ClassSetConstructor(interp, curClass,
			    Tcl_NewMethod(interp, curClass, NULL, 1,
					  &EvaldirectStatementConstructorType,
					  (ClientData) NULL));

    /* Look up the 'typesStatement' class */

    nameObj = Tcl_NewStringObj("::tdbc::odbc::typesStatement", -1);
    Tcl_IncrRefCount(nameObj);
    if ((curClassObject = Tcl_GetObjectFromObj(interp, nameObj)) == NULL) {
	Tcl_DecrRefCount(nameObj);

Changes to library/tdbcodbc.tcl.

216
217
218
219
220
221
222













223
224
225
226
227
228
229
...
441
442
443
444
445
446
447


























448
449
450
451
452
453
454
		lappend retval $row
	    }
	    set retval
	} result options]
	catch {rename $stmt {}}
	return -level 0 -options $options $result
    }














    # The 'prepareCall' method gives a portable interface to prepare
    # calls to stored procedures.  It delegates to 'prepare' to do the
    # actual work.

    method preparecall {call} {
	
................................................................................

    # The constructor is written in C. It accepts the handle to the
    # connection and the -primary and -foreign options.  It works in all
    # ways like the constructor of the 'statement' class except that
    # its 'init' method sets up to enumerate foreign keys and not run a SQL
    # query.



























    # The 'resultSetCreate' method forwards to the result set constructor

    forward resultSetCreate ::tdbc::odbc::resultset create

}
 
#------------------------------------------------------------------------------






>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
		lappend retval $row
	    }
	    set retval
	} result options]
	catch {rename $stmt {}}
	return -level 0 -options $options $result
    }

    # The 'evaldirect' evaluates driver-native SQL code without preparing it,
    # and returns a list of dicts (similar to '$connection allrows -as dicts').

    method evaldirect {sqlStatement} {
	set stmt [::tdbc::odbc::evaldirectStatement create \
		      Stmt::[incr statementSeq] [self] $sqlStatement]
       	set status [catch {
	    $stmt allrows -as dicts
	} result options]
	catch {rename $stmt {}}
	return -level 0 -options $options $result
    }

    # The 'prepareCall' method gives a portable interface to prepare
    # calls to stored procedures.  It delegates to 'prepare' to do the
    # actual work.

    method preparecall {call} {
	
................................................................................

    # The constructor is written in C. It accepts the handle to the
    # connection and the -primary and -foreign options.  It works in all
    # ways like the constructor of the 'statement' class except that
    # its 'init' method sets up to enumerate foreign keys and not run a SQL
    # query.

    # The 'resultSetCreate' method forwards to the result set constructor

    forward resultSetCreate ::tdbc::odbc::resultset create

}
 
#------------------------------------------------------------------------------
#
# tdbc::odbc::evaldirectStatement --
#
#	The class 'tdbc::odbc::evaldirectStatement' provides a mechanism to
#	execute driver-name SQL code through an ODBC connection.  The SQL code
#	is not prepared and no tokenization or variable substitution is done.
#
#------------------------------------------------------------------------------

oo::class create ::tdbc::odbc::evaldirectStatement {

    superclass ::tdbc::statement

    # The constructor is written in C. It accepts the handle to the
    # connection and a SQL statement.  It works in all
    # ways like the constructor of the 'statement' class except that
    # its 'init' method does not tokenize or prepare the SQL statement, and
    # sets up to run the SQL query without performing variable substitution.

    # The 'resultSetCreate' method forwards to the result set constructor

    forward resultSetCreate ::tdbc::odbc::resultset create

}
 
#------------------------------------------------------------------------------