diff CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/tdbcodbc1.1.5/tdbcodbc.tcl @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/tdbcodbc1.1.5/tdbcodbc.tcl	Tue Mar 18 16:23:26 2025 -0400
@@ -0,0 +1,554 @@
+# tdbcodbc.tcl --
+#
+#	Class definitions and Tcl-level methods for the tdbc::odbc bridge.
+#
+# Copyright (c) 2008 by Kevin B. Kenny
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: tdbcodbc.tcl,v 1.47 2008/02/27 02:08:27 kennykb Exp $
+#
+#------------------------------------------------------------------------------
+
+package require tdbc
+
+::namespace eval ::tdbc::odbc {
+
+    namespace export connection datasources drivers
+
+    # Data types that are predefined in ODBC
+
+    variable sqltypes [dict create \
+			   1 char \
+			   2 numeric \
+			   3 decimal \
+			   4 integer \
+			   5 smallint \
+			   6 float \
+			   7 real \
+			   8 double \
+			   9 datetime \
+			   12 varchar \
+			   91 date \
+			   92 time \
+			   93 timestamp \
+			   -1 longvarchar \
+			   -2 binary \
+			   -3 varbinary \
+			   -4 longvarbinary \
+			   -5 bigint \
+			   -6 tinyint \
+			   -7 bit \
+			   -8 wchar \
+			   -9 wvarchar \
+			   -10 wlongvarchar \
+			   -11 guid]
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::connection --
+#
+#	Class representing a connection to a database through ODBC.
+#
+#-------------------------------------------------------------------------------
+
+::oo::class create ::tdbc::odbc::connection {
+
+    superclass ::tdbc::connection
+
+    variable statementSeq typemap
+
+    # The constructor is written in C. It takes the connection string
+    # as its argument It sets up a namespace to hold the statements
+    # associated with the connection, and then delegates to the 'init'
+    # method (written in C) to do the actual work of attaching to the
+    # database. When that comes back, it sets up a statement to query
+    # the support types, makes a dictionary to enumerate them, and
+    # calls back to set a flag if WVARCHAR is seen (If WVARCHAR is
+    # seen, the database supports Unicode.)
+
+    # The 'statementCreate' method forwards to the constructor of the
+    # statement class
+
+    forward statementCreate ::tdbc::odbc::statement create
+
+    # The 'tables' method returns a dictionary describing the tables
+    # in the database
+
+    method tables {{pattern %}} {
+	set stmt [::tdbc::odbc::tablesStatement create \
+		      Stmt::[incr statementSeq] [self] $pattern]
+       	set status [catch {
+	    set retval {}
+	    $stmt foreach -as dicts row {
+		if {[dict exists $row TABLE_NAME]} {
+		    dict set retval [dict get $row TABLE_NAME] $row
+		}
+	    }
+	    set retval
+	} result options]
+	catch {rename $stmt {}}
+	return -level 0 -options $options $result
+    }
+
+    # The 'columns' method returns a dictionary describing the tables
+    # in the database
+
+    method columns {table {pattern %}} {
+	# Make sure that the type map is initialized
+	my typemap
+
+	# Query the columns from the database
+
+	set stmt [::tdbc::odbc::columnsStatement create \
+		      Stmt::[incr statementSeq] [self] $table $pattern]
+	set status [catch {
+	    set retval {}
+	    $stmt foreach -as dicts origrow {
+
+		# Map the type, precision, scale and nullable indicators
+		# to tdbc's notation
+
+		set row {}
+		dict for {key value} $origrow {
+		    dict set row [string tolower $key] $value
+		}
+		if {[dict exists $row column_name]} {
+		    if {[dict exists $typemap \
+			     [dict get $row data_type]]} {
+			dict set row type \
+			    [dict get $typemap \
+				 [dict get $row data_type]]
+		    } else {
+			dict set row type [dict get $row type_name]
+		    }
+		    if {[dict exists $row column_size]} {
+			dict set row precision \
+			    [dict get $row column_size]
+		    }
+		    if {[dict exists $row decimal_digits]} {
+			dict set row scale \
+			    [dict get $row decimal_digits]
+		    }
+		    if {![dict exists $row nullable]} {
+			dict set row nullable \
+			    [expr {!![string trim [dict get $row is_nullable]]}]
+		    }
+		    dict set retval [dict get $row column_name] $row
+		}
+	    }
+	    set retval
+	} result options]
+	catch {rename $stmt {}}
+	return -level 0 -options $options $result
+    }
+
+    # The 'primarykeys' method returns a dictionary describing the primary
+    # keys of a table
+
+    method primarykeys {tableName} {
+	set stmt [::tdbc::odbc::primarykeysStatement create \
+		      Stmt::[incr statementSeq] [self] $tableName]
+       	set status [catch {
+	    set retval {}
+	    $stmt foreach -as dicts row {
+		foreach {odbcKey tdbcKey} {
+		    TABLE_CAT		tableCatalog
+		    TABLE_SCHEM		tableSchema
+		    TABLE_NAME		tableName
+		    COLUMN_NAME		columnName
+		    KEY_SEQ		ordinalPosition
+		    PK_NAME		constraintName
+		} {
+		    if {[dict exists $row $odbcKey]} {
+			dict set row $tdbcKey [dict get $row $odbcKey]
+			dict unset row $odbcKey
+		    }
+		}
+		lappend retval $row
+	    }
+	    set retval
+	} result options]
+	catch {rename $stmt {}}
+	return -level 0 -options $options $result
+    }
+
+    # The 'foreignkeys' method returns a dictionary describing the foreign
+    # keys of a table
+
+    method foreignkeys {args} {
+	set stmt [::tdbc::odbc::foreignkeysStatement create \
+		      Stmt::[incr statementSeq] [self] {*}$args]
+       	set status [catch {
+	    set fkseq 0
+	    set retval {}
+	    $stmt foreach -as dicts row {
+		foreach {odbcKey tdbcKey} {
+		    PKTABLE_CAT		primaryCatalog
+		    PKTABLE_SCHEM	primarySchema
+		    PKTABLE_NAME	primaryTable
+		    PKCOLUMN_NAME	primaryColumn
+		    FKTABLE_CAT		foreignCatalog
+		    FKTABLE_SCHEM	foreignSchema
+		    FKTABLE_NAME	foreignTable
+		    FKCOLUMN_NAME	foreignColumn
+		    UPDATE_RULE		updateRule
+		    DELETE_RULE		deleteRule
+		    DEFERRABILITY	deferrable
+		    KEY_SEQ		ordinalPosition
+		    FK_NAME		foreignConstraintName
+		} {
+		    if {[dict exists $row $odbcKey]} {
+			dict set row $tdbcKey [dict get $row $odbcKey]
+			dict unset row $odbcKey
+		    }
+		}
+		# Horrible kludge: If the driver doesn't report FK_NAME,
+		# make one up.
+		if {![dict exists $row foreignConstraintName]} {
+		    if {![dict exists $row ordinalPosition]
+			|| [dict get $row ordinalPosition] == 1} {
+			set fkname ?[dict get $row foreignTable]?[incr fkseq]
+		    }
+		    dict set row foreignConstraintName $fkname
+		}
+		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} {
+
+	regexp {^[[:space:]]*(?:([A-Za-z_][A-Za-z_0-9]*)[[:space:]]*=)?(.*)} \
+	    $call -> varName rest
+	if {$varName eq {}} {
+	    my prepare \\{CALL $rest\\}
+	} else {
+	    my prepare \\{:$varName=CALL $rest\\}
+	}
+
+	if 0 {
+	# Kevin thinks this is going to be
+
+	if {![regexp -expanded {
+	    ^\s*				   # leading whitespace
+	    (?::([[:alpha:]_][[:alnum:]_]*)\s*=\s*) # possible variable name
+	    (?:(?:([[:alpha:]_][[:alnum:]_]*)\s*[.]\s*)?   # catalog
+	       ([[:alpha:]_][[:alnum:]_]*)\s*[.]\s*)?      # schema
+	    ([[:alpha:]_][[:alnum:]_]*)\s*		   # procedure
+	    (.*)$					   # argument list
+	} $call -> varName catalog schema procedure arglist]} {
+	    return -code error \
+		-errorCode [list TDBC \
+				SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION \
+				42000 ODBC -1] \
+		"Syntax error in stored procedure call"
+	} else {
+	    my PrepareCall $varName $catalog $schema $procedure $arglist
+	}
+
+	# at least if making all parameters 'inout' doesn't work.
+
+        }
+
+    }
+
+    # The 'typemap' method returns the type map
+
+    method typemap {} {
+	if {![info exists typemap]} {
+	    set typemap $::tdbc::odbc::sqltypes
+	    set typesStmt [tdbc::odbc::typesStatement new [self]]
+	    $typesStmt foreach row {
+		set typeNum [dict get $row DATA_TYPE]
+		if {![dict exists $typemap $typeNum]} {
+		    dict set typemap $typeNum [string tolower \
+						   [dict get $row TYPE_NAME]]
+		}
+		switch -exact -- $typeNum {
+		    -9 {
+			[self] HasWvarchar 1
+		    }
+		    -5 {
+			[self] HasBigint 1
+		    }
+		}
+	    }
+	    rename $typesStmt {}
+	}
+	return $typemap
+    }
+
+    # The 'begintransaction', 'commit' and 'rollback' methods are
+    # implemented in C.
+
+}
+
+#-------------------------------------------------------------------------------
+#
+# tdbc::odbc::statement --
+#
+#	The class 'tdbc::odbc::statement' models one statement against a
+#       database accessed through an ODBC connection
+#
+#-------------------------------------------------------------------------------
+
+::oo::class create ::tdbc::odbc::statement {
+
+    superclass ::tdbc::statement
+
+    # The constructor is implemented in C. It accepts the handle to
+    # the connection and the SQL code for the statement to prepare.
+    # It creates a subordinate namespace to hold the statement's
+    # active result sets, and then delegates to the 'init' method,
+    # written in C, to do the actual work of preparing the statement.
+
+    # The 'resultSetCreate' method forwards to the result set constructor
+
+    forward resultSetCreate ::tdbc::odbc::resultset create
+
+    # The 'params' method describes the parameters to the statement
+
+    method params {} {
+	set typemap [[my connection] typemap]
+	set result {}
+	foreach {name flags typeNum precision scale nullable} [my ParamList] {
+	    set lst [dict create \
+			 name $name \
+			 direction [lindex {unknown in out inout} \
+					[expr {($flags & 0x06) >> 1}]] \
+			 type [dict get $typemap $typeNum] \
+			 precision $precision \
+			 scale $scale]
+	    if {$nullable in {0 1}} {
+		dict set list nullable $nullable
+	    }
+	    dict set result $name $lst
+	}
+	return $result
+    }
+
+    # Methods implemented in C:
+    # init statement ?dictionary?
+    #     Does the heavy lifting for the constructor
+    # connection
+    #	Returns the connection handle to which this statement belongs
+    # paramtype paramname ?direction? type ?precision ?scale??
+    #     Declares the type of a parameter in the statement
+
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::tablesStatement --
+#
+#	The class 'tdbc::odbc::tablesStatement' represents the special
+#	statement that queries the tables in a database through an ODBC
+#	connection.
+#
+#------------------------------------------------------------------------------
+
+oo::class create ::tdbc::odbc::tablesStatement {
+
+    superclass ::tdbc::statement
+
+    # The constructor is written in C. It accepts the handle to the
+    # connection and a pattern to match table names.  It works in all
+    # ways like the constructor of the 'statement' class except that
+    # its 'init' method sets up to enumerate tables and not run a SQL
+    # query.
+
+    # The 'resultSetCreate' method forwards to the result set constructor
+
+    forward resultSetCreate ::tdbc::odbc::resultset create
+
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::columnsStatement --
+#
+#	The class 'tdbc::odbc::tablesStatement' represents the special
+#	statement that queries the columns of a table or view
+#	in a database through an ODBC connection.
+#
+#------------------------------------------------------------------------------
+
+oo::class create ::tdbc::odbc::columnsStatement {
+
+    superclass ::tdbc::statement
+
+    # The constructor is written in C. It accepts the handle to the
+    # connection, a table name, and a pattern to match column
+    # names. It works in all ways like the constructor of the
+    # 'statement' class except that its 'init' method sets up to
+    # enumerate tables and not run a SQL query.
+
+    # The 'resultSetCreate' class forwards to the constructor of the
+    # result set
+
+    forward resultSetCreate ::tdbc::odbc::resultset create
+
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::primarykeysStatement --
+#
+#	The class 'tdbc::odbc::primarykeysStatement' represents the special
+#	statement that queries the primary keys on a table through an ODBC
+#	connection.
+#
+#------------------------------------------------------------------------------
+
+oo::class create ::tdbc::odbc::primarykeysStatement {
+
+    superclass ::tdbc::statement
+
+    # The constructor is written in C. It accepts the handle to the
+    # connection and a table name.  It works in all
+    # ways like the constructor of the 'statement' class except that
+    # its 'init' method sets up to enumerate primary keys and not run a SQL
+    # query.
+
+    # The 'resultSetCreate' method forwards to the result set constructor
+
+    forward resultSetCreate ::tdbc::odbc::resultset create
+
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::foreignkeysStatement --
+#
+#	The class 'tdbc::odbc::foreignkeysStatement' represents the special
+#	statement that queries the foreign keys on a table through an ODBC
+#	connection.
+#
+#------------------------------------------------------------------------------
+
+oo::class create ::tdbc::odbc::foreignkeysStatement {
+
+    superclass ::tdbc::statement
+
+    # 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
+
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::typesStatement --
+#
+#	The class 'tdbc::odbc::typesStatement' represents the special
+#	statement that queries the types available in a database through
+#	an ODBC connection.
+#
+#------------------------------------------------------------------------------
+
+
+oo::class create ::tdbc::odbc::typesStatement {
+
+    superclass ::tdbc::statement
+
+    # The constructor is written in C. It accepts the handle to the
+    # connection, and (optionally) a data type number. It works in all
+    # ways like the constructor of the 'statement' class except that
+    # its 'init' method sets up to enumerate types and not run a SQL
+    # query.
+
+    # The 'resultSetCreate' method forwards to the constructor of result sets
+
+    forward resultSetCreate ::tdbc::odbc::resultset create
+
+    # The C code contains a variant implementation of the 'init' method.
+
+}
+
+#------------------------------------------------------------------------------
+#
+# tdbc::odbc::resultset --
+#
+#	The class 'tdbc::odbc::resultset' models the result set that is
+#	produced by executing a statement against an ODBC database.
+#
+#------------------------------------------------------------------------------
+
+::oo::class create ::tdbc::odbc::resultset {
+
+    superclass ::tdbc::resultset
+
+    # Methods implemented in C include:
+
+    # constructor statement ?dictionary?
+    #     -- Executes the statement against the database, optionally providing
+    #        a dictionary of substituted parameters (default is to get params
+    #        from variables in the caller's scope).
+    # columns
+    #     -- Returns a list of the names of the columns in the result.
+    # nextdict
+    #     -- Stores the next row of the result set in the given variable in
+    #        the caller's scope as a dictionary whose keys are
+    #        column names and whose values are column values.
+    # nextlist
+    #     -- Stores the next row of the result set in the given variable in
+    #        the caller's scope as a list of cells.
+    # rowcount
+    #     -- Returns a count of rows affected by the statement, or -1
+    #        if the count of rows has not been determined.
+
+}