Class ::nsmcp::DatabaseSession (public)

 ::nx::Class ::nsmcp::DatabaseSession[i]

Defined in /usr/local/ns/tcl/nsmcp/lib/database.tcl

Testcases:
No testcase defined.
Source code:
    :property handle:required
    :variable templates
    :variable inTransaction
    :method init {} {set :templates {}; set :inTransaction false}
    :public method quote {value} {
        if {[string first \x00 $value]>=0} {
            return "CAST(X'[binary encode hex [encoding convertto utf-8 $value]]' AS TEXT)"
        }
        return "'[string map [list ' ''] $value]'"
    }
    :method compile {sql} {
        if {[dict exists ${:templates} $sql]} {return [dict get ${:templates} $sql]}
        set statements {}; set parts {}; set literal {}; set length [string length $sql]
        set quote {}; set comment {}; set active false
        for {set i 0} {$i<$length} {incr i} {
            set char [string index $sql $i]; set next [string index $sql [expr {$i+1}]]
            if {$comment eq "line"} {
                append literal $char
                if {$char eq "\n"} {set comment {}}
                continue
            }
            if {$comment eq "block"} {
                append literal $char
                if {$char eq "*" && $next eq "/"} {append literal $next; incr i; set comment {}}
                continue
            }
            if {$quote ne ""} {
                append literal $char
                if {$char eq $quote} {
                    if {$next eq $quote} {append literal $next; incr i} else {set quote {}}
                }
                continue
            }
            if {$char eq "-" && $next eq "-"} {set comment line; append literal $char $next; incr i; continue}
            if {$char eq "/" && $next eq "*"} {set comment block; append literal $char $next; incr i; continue}
            if {$char eq "'" || $char eq "\"" || $char eq "\x60"} {set active true; set quote $char; append literal $char; continue}
            if {$char eq "\u005b"} {set active true; set quote "\u005d"; append literal $char; continue}
            if {$char eq ";"} {
                lappend parts literal $literal
                if {$active} {lappend statements $parts}
                set parts {}; set literal {}; set active false; continue
            }
            if {![string is space -strict $char]} {set active true}
            if {$char eq "$" && [string match {[A-Za-z_]} $next]} {
                lappend parts literal $literal; set literal {}; set name {}
                while {$i+1<$length && [string match {[A-Za-z0-9_]} [string index $sql [expr {$i+1}]]]} {
                    incr i; append name [string index $sql $i]
                }
                lappend parts variable $name
            } else {append literal $char}
        }
        if {$quote ne "" || $comment eq "block"} {error "unterminated trusted SQL template"}
        lappend parts literal $literal
        if {$active} {lappend statements $parts}
        if {[dict size ${:templates}]>=64} {set :templates {}}
        dict set :templates $sql $statements
        return $statements
    }
    :public method execute {sql {arrayName {}} {script {}}} {
        set rows {}
        foreach template [:compile $sql] {
            set statement {}
            foreach {type value} $template {
                if {$type eq "literal"} {append statement $value} elseif {[uplevel 1 [list info exists $value]]} {
                    append statement [:quote [uplevel 1 [list set $value]]]
                } else {append statement NULL}
            }
            if {[string trim $statement] eq ""} continue
            set kind [ns_db exec ${:handle} $statement]
            set command [string toupper [lindex [split [string trim $statement]] 0]]
            if {$command eq "BEGIN"} {set :inTransaction true}
            if {$command in {COMMIT ROLLBACK END}} {set :inTransaction false}
            if {$kind ne "NS_ROWS"} continue
            set row [ns_db bindrow ${:handle}]
            try {
                while {[ns_db getrow ${:handle} $row]} {
                    set record {}; set keys {}
                    for {set i 0} {$i<[ns_set size $row]} {incr i} {
                        set key [ns_set key $row $i]
                        dict set record $key [ns_set value $row $i]; lappend keys $key
                    }
                    if {$arrayName eq ""} {lappend rows $record} else {
                        upvar 1 $arrayName target
                        array unset target
                        dict for {key value} $record {set target($key) $value}
                        set target(*) $keys
                        set code [catch {uplevel 1 $script} value options]
                        if {$code==3} break
                        if {$code==4} continue
                        if {$code} {return -options $options $value}
                    }
                }
            } finally {ns_db flush ${:handle}; ns_set free $row}
        }
        return $rows
    }
    :public method scalar {sql} {
        set rows [uplevel 1 [list [nsf::current object] execute $sql]]
        if {$rows eq ""} {return {}}
        return [lindex [dict values [lindex $rows 0]] 0]
    }
    :public method changes {} {return [:scalar {SELECT changes()}]}
    :public method transaction {script} {
        :execute {BEGIN IMMEDIATE}
        try {
            set value [uplevel 1 $script]
            :execute COMMIT
            return $value
        } on error {message options} {
            catch {:execute ROLLBACK}
            return -options $options $message
        }
    }
    :public method close {} {:destroy}
    :method destroy {} {
        if {[info exists :handle]} {
            if {${:inTransaction}} {catch {ns_db dml ${:handle} ROLLBACK}}
            ns_db releasehandle ${:handle}
            unset :handle
        }
        next
    }
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: