This is an automated email from the ASF dual-hosted git repository.

mxmanghi pushed a commit to branch 3.2
in repository https://gitbox.apache.org/repos/asf/tcl-rivet.git


The following commit(s) were added to refs/heads/3.2 by this push:
     new 9a3bee1  add consistent handling of TDBC named arguments substitution 
on a table when there are no columns of it needing special formatting (via 
special_fields_formatter object), reimplementation of update,insert and delete 
for the ::DIO::Tdbc driver
9a3bee1 is described below

commit 9a3bee12ec7c7ad656f8b33e0e3427bd1378a872
Author: Massimo Manghi <[email protected]>
AuthorDate: Thu Oct 16 02:26:09 2025 +0200

    add consistent handling of TDBC named arguments substitution on a table 
when there are no columns of it needing special formatting (via 
special_fields_formatter object), reimplementation of update,insert and delete 
for the ::DIO::Tdbc driver
---
 ChangeLog                         |   8 ++
 rivet/packages/dio/dio.tcl        |  41 +++++----
 rivet/packages/dio/dio_Tdbc.tcl   | 184 +++++++++++++++++++++++++++++++++-----
 rivet/packages/dio/formatters.tcl |  11 +++
 4 files changed, 206 insertions(+), 38 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5357961..88468b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-10-15 Massimo Manghi <[email protected]>
+    * rivet/packages/dio/dio_Tdbc.tcl: reimplementing methods
+    update, delete and insert to enable DIO Tdbc driver to handle also
+    SQL queries using the TDBC named argument substitution
+    * rivet/packages/dio/dio.tcl: better handling of the special field
+    formatter. The special fields formatter usage disables on specific
+    tables the SQL queries with named arguments substitution
+ 
 2025-10-07 Massimo Manghi <[email protected]>
        * rivet/packages/dio/dio_Tdbc.tcl: removing method insert from
        class ::DIO::Tdbc, the real responsible of honoring SQL statements
diff --git a/rivet/packages/dio/dio.tcl b/rivet/packages/dio/dio.tcl
index 8ab32e0..2a874e4 100644
--- a/rivet/packages/dio/dio.tcl
+++ b/rivet/packages/dio/dio.tcl
@@ -19,7 +19,8 @@ package require Itcl
 package require dio::formatters
 
 # Command ::rivet::lempty is extensively used within this class but it's
-# defined only when we run DIO from mod_rivet. We load it here for convenience
+# defined only when we run DIO from mod_rivet. For convenience we load it
+# here if needed
 
 if {[info commands ::rivet::lempty] == ""} {
 
@@ -47,7 +48,6 @@ proc handle {interface args} {
     }
 
     #puts "interface: $interface ($tdbc_driver)"
-
     set first [lindex $args 0]
     if {![::rivet::lempty $first] && [string index $first 0] != "-"} {
         set obj  [lindex $args 0]
@@ -56,15 +56,22 @@ proc handle {interface args} {
     uplevel \#0 package require dio_$interface
 
     #puts "tdbc: '$tdbc_driver' obj: '$obj' args 3: '$args'"
-
     if {$tdbc_driver == ""} {
 
         # old connectors based on traditional dbms drivers
 
-        return [uplevel \#0 ::DIO::$interface $obj $args]
+        set dio_o [uplevel \#0 ::DIO::$interface $obj $args]
     } else {
-        return [uplevel \#0 ::DIO::$interface $obj $tdbc_driver {*}$args]
+        set dio_o [uplevel \#0 ::DIO::$interface $obj $tdbc_driver {*}$args]
     }
+
+    # the Tdbc driver may rename the drive because we created
+    # a set of equivalence classes among DBMS (eg mariadb,sqlite etc)
+    # therefore the class for the interface passed by the constructor
+    # may not exist and it's ::DIO::Tdbc responsability to create it
+
+    $dio_o create_field_formatter
+    return $dio_o
 }
 
 ##
@@ -73,7 +80,6 @@ proc handle {interface args} {
 ::itcl::class Database {
     constructor {args} {
         eval configure $args
-        set special_fields_formatter [::DIO::formatters::${interface} 
::DIO::formatters::#auto]
     }
 
     destructor {
@@ -120,17 +126,17 @@ proc handle {interface args} {
             set elem [lindex $args $i]
 
             switch -- [::string tolower $elem] {
-                "-and" { 
+                "-and" {
                     # -and -- switch to AND-style processing
                     set bool AND 
                 }
 
-                "-or"  { 
+                "-or"  {
                     # -or -- switch to OR-style processing
                     set bool OR 
                 }
 
-                "-table" { 
+                "-table" {
                     # -table -- identify which table the query is about
                     set myTable [lindex $args [incr i]] 
                 }
@@ -415,7 +421,7 @@ proc handle {interface args} {
             return -code error -errorcode missing_keyfield "-keyfield not 
specified in DIO object"
         }
 
-        set $keyVar   $data(-keyfield)
+        set $keyVar $data(-keyfield)
     }
 
     #
@@ -497,11 +503,11 @@ proc handle {interface args} {
     #
     method update {arrayName args} {
         table_check $args
-        upvar 1 $arrayName $arrayName $arrayName array
+        upvar 1 $arrayName $arrayName $arrayName row_a
 
         set key [makekey $arrayName $myKeyfield]
 
-        set fields [::array names array]
+        set fields [::array names row_a]
         set req [build_update_query array $fields $myTable]
         append req [build_key_where_clause $myKeyfield $key]
 
@@ -634,8 +640,12 @@ proc handle {interface args} {
         return [string "select count(*) from $myTable"]
     }
 
+    public method create_field_formatter {} {
+        set special_fields_formatter [::DIO::formatters::[::string totitle 
$interface] ::DIO::formatters::#auto]
+    }
+
     protected method set_field_formatter {formatter_class} {
-        $special_fields_formatter destroy
+        if {$special_fields_formatter != ""} { $special_fields_formatter 
destroy }
         set special_fields_formatter [$formatter_class 
::DIO::formatters::#auto]
     }
 
@@ -683,8 +693,7 @@ proc handle {interface args} {
     method host {{string ""}} { return [configure_variable host $string] }
     method port {{string ""}} { return [configure_variable port $string] }
 
-    public variable special_fields_formatter \
-                    [::DIO::formatters::RootFormatter ::DIO::formatters::#auto]
+    public variable special_fields_formatter ""
 
     public variable interface   ""
     public variable errorinfo   ""
@@ -924,4 +933,4 @@ proc handle {interface args} {
 
 } ; ## namespace eval DIO
 
-package provide DIO 1.2.1
+package provide DIO 1.2.3
diff --git a/rivet/packages/dio/dio_Tdbc.tcl b/rivet/packages/dio/dio_Tdbc.tcl
index 5c3469a..48c9eb9 100644
--- a/rivet/packages/dio/dio_Tdbc.tcl
+++ b/rivet/packages/dio/dio_Tdbc.tcl
@@ -20,8 +20,9 @@
 #    under the License.
 
 package require tdbc
-package require DIO      1.2
-package provide dio_Tdbc 1.2.2
+package require DIO      1.2.3
+package provide dio_Tdbc 1.2.3
+package require struct::set
 
 namespace eval DIO {
     ::itcl::class Tdbc {
@@ -69,7 +70,9 @@ namespace eval DIO {
                 }
             }
 
-            set tdbc_connector "tdbc::${connector_name}"
+            set interface $connector_name
+            $this set_field_formatter ::DIO::formatters::[::string totitle 
$interface]
+            set tdbc_connector "tdbc::${interface}"
 
             uplevel #0 package require ${tdbc_connector}
         }
@@ -118,39 +121,176 @@ namespace eval DIO {
             }
         }
 
+        # delete -
         #
-        # build_insert_query --
         #
-        # Override ::DIO::build_insert_query method taking advantage of
-        # the named parameters feature of TDBC SQL statements objects
+
+        method delete {key args} {
+            table_check $args
+
+            set sql "DELETE FROM $myTable"
+            set sql_values [dict create]
+            if {[$special_fields_formatter has_special_fields $table]} {
+                append sql [build_key_where_clause $myKeyfield $key]
+            } else {
+                set where_key_value_pairs [lmap field $myKeyfield {
+                    set v "${field}=:${field}"
+                }]
+                set sql "$sql WHERE [join $where_key_value_pairs { AND }]"
+                foreach field $myKeyfield k $key { dict set sql_values $field 
$k }
+            }
+
+            #puts $sql
+
+            $this check_connector
+            set tdbc_statement [uplevel 1 $connector prepare [::list $sql]]
+
+            #puts "--> $sql_values"
+            if {[catch {set tdbc_result [$tdbc_statement execute $sql_values]} 
e errorinfo]} {
+                $tdbc_statement close
+                return -code error -errorinfo [::list $errorinfo]
+            } else {
+
+                # we must store also the TDBC SQL statement as it owns
+                # the TDBC results set represented by tdbc_result. Closing
+                # a tdbc::statement closes also any active tdbc::resultset
+                # owned by it
+
+                set result_obj [$this result TDBC -resultid   $tdbc_result     
 \
+                                                  -statement  $tdbc_statement  
 \
+                                                  -isselect   false            
 \
+                                                  -fields     [::list 
[$tdbc_result columns]]] 
+            }
+
+            # this doesn't work on postgres, you've got to use cmdRows,
+            # we need to figure out what to do with this
+
+            set numrows [$result_obj numrows]
+            $result_obj destroy
+            return $numrows
+        }
+
+
         #
+        # update - reimplementation of the ::DIO::Database::update
+        # method that is supposed to exploit the named arguments
+        # feature of TDBC. 
+        #
+        method update {arrayName args} {
+            upvar 1 $arrayName row_a
+            $this table_check $args
+
+            # myTable is implicitly set by table_check
+            
+            $this configure -table $myTable
+            set key [makekey row_a $myKeyfield]
+
+            #puts "-> table: $table"
+            #puts "-> key: $key"
+            #puts "-> keyfield: $myKeyfield"
+
+            set sql_values [dict create {*}[::array get row_a]] 
+            set fields     [::array names row_a]
+            if {[$special_fields_formatter has_special_fields $table]} {
+
+                # the special fields formatter is fundamentally incompatible 
with
+                # TDBC's named arguments mechanism. We resort to the 
superclass method
+                # where a literal SQL statement is built
+
+                set sql     [$this build_update_query row_a $fields $table]
+                append sql  [$this build_key_where_clause $myKeyfield $key]
+
+            } else {
+
+                set set_key_value_pairs [lmap field $fields {
+                    set v "${field}=:${field}"
+                }]
+                set where_key_value_pairs [lmap field $myKeyfield {
+                    set v "${field}=:${field}"
+                }]
+
+                ::lappend where_key_value_pairs "1 = 1"
+                set sql [join [::list "UPDATE $table" \
+                                      "SET   [join $set_key_value_pairs {, }]" 
\
+                                      "WHERE [join $where_key_value_pairs { 
AND }]"] " "]
 
-        protected method build_insert_query {arrayName fields {myTable ""}} {
+            }
+
+            #puts $sql
+            $this check_connector
+            set tdbc_statement [uplevel 1 $connector prepare [::list $sql]]
+
+            #puts "--> $sql_values"
+            if {[catch {set tdbc_result [$tdbc_statement execute $sql_values]} 
e errorinfo]} {
+                $tdbc_statement close
+                return -code error -errorinfo [::list $errorinfo]
+            } else {
+
+                # we must store also the TDBC SQL statement as it owns
+                # the TDBC results set represented by tdbc_result. Closing
+                # a tdbc::statement closes also any active tdbc::resultset
+                # owned by it
+
+                set result_obj [$this result TDBC -resultid   $tdbc_result     
 \
+                                                  -statement  $tdbc_statement  
 \
+                                                  -isselect   false            
 \
+                                                  -fields     [::list 
[$tdbc_result columns]]] 
+            }
+
+            # this doesn't work on postgres, you've got to use cmdRows,
+            # we need to figure out what to do with this
+
+            set numrows [$result_obj numrows]
+            $result_obj destroy
+            return $numrows
+        }
+
+        #
+        # insert - 
+        #
+        # overriding method 'insert' as in case of registered special fields
+        # we have to give up with the idea of using the named arguments 
approach
+        #
+        method insert {table arrayName} {
             upvar 1 $arrayName row_a
 
-            if {[::rivet::lempty $myTable]} { set myTable $table }
-            set vars [::list]
-            set named_pars_l [::list]
+            set sql_values [dict create {*}[::array get row_a]] 
+            set fields     [::array names row_a]
+            if {[$special_fields_formatter has_special_fields $table]} {
+                set sql [build_insert_query row_a [::array names row_a] $table]
+            } else {
+                set values [lmap field $fields { set v ":${field}" }]
 
-            # we adopt the TDBC named parameters approach to deal with binary
-            # data that may cause the SQL sanity checks to fail
+                set sql "INSERT INTO $table ([join $fields {,}]) VALUES ([join 
$values {,}])"
+            }
 
-            foreach field $fields {
-                if {![info exists row_a($field)]} { continue }
-                lappend vars "$field"
+            #puts $sql
 
-                # we reformat the fields evaluating through the "special 
fields formatter"
-                # and assign their value to the array row_a which shadows 
$arrayName in
-                # the caller frame
+            $this check_connector
+            set tdbc_statement [uplevel 1 $connector prepare [::list $sql]]
 
-                set row_a($field) [$this build_special_field $myTable $field 
$row_a($field)]
+            if {[catch {set tdbc_result [$tdbc_statement execute $sql_values]} 
e errorinfo]} {
+                $tdbc_statement close
+                return -code error -errorinfo [::list $errorinfo]
+            } else {
 
-                # we don't evaluate an SQL statement, we build it for 
evaluation by the caller
+                # we must store also the TDBC SQL statement as it owns
+                # the TDBC results set represented by tdbc_result. Closing
+                # a tdbc::statement closes also any active tdbc::resultset
+                # owned by it
 
-                lappend named_pars_l ":${arrayName}($field)"
+                set result_obj [$this result TDBC -resultid   $tdbc_result     
 \
+                                                  -statement  $tdbc_statement  
 \
+                                                  -isselect   false            
 \
+                                                  -fields     [::list 
[$tdbc_result columns]]] 
             }
 
-            return "INSERT INTO $myTable ([join $vars {,}]) VALUES ([join 
$named_pars_l {,}])"
+            # this doesn't work on postgres, you've got to use cmdRows,
+            # we need to figure out what to do with this
+
+            set numrows [$result_obj numrows]
+            $result_obj destroy
+            return $numrows
         }
 
         #
diff --git a/rivet/packages/dio/formatters.tcl 
b/rivet/packages/dio/formatters.tcl
index 2e6f605..b61082b 100644
--- a/rivet/packages/dio/formatters.tcl
+++ b/rivet/packages/dio/formatters.tcl
@@ -44,6 +44,17 @@ namespace eval ::DIO::formatters {
             dict set special_fields $table_name $field_name $ftype
         }
 
+        public method get_special_fields {table_name} {
+            if {[dict exists $special_fields $table_name]} {
+                return [dict keys [dict get $special_fields $table_name]
+            }
+            return ""
+        }
+
+        public method has_special_fields {table_name} {
+            return [dict exists $special_fields $table_name]
+        }
+
         public method build {table_name field_name val convert_to} {
             if {[dict exists $special_fields $table_name $field_name]} {
                 set field_type [dict get $special_fields $table_name 
$field_name]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to