DbObject(3NS)

NAME SYNOPSIS DESCRIPTION IMPLEMENTATION NOTES WARNINGS SEE ALSO AUTHORS

NAME

DbObject - This object serves as an interface between the object-oriented entity classes and the relational database. All object data is represented as a tcl array. This class translates between that array and the sybtcl input and output to the relational database.

SYNOPSIS

DbObject
handle
_dbuser
_dbpass
_dsquery
handleIndex
numHandles
waitInterval 30
tranFlag

Standard Public Interface

DbObject::Connect { {dbuser{}} {dbpass{}} {dsquery {}} } : newHandleNumber
DbObject::Close { } : closedHandleNumber
DbObject::SetHandle { {handle} } : currentHandleIndex
DbObject::Query { class objref {where {}} {order {}} }
DbObject::Cancel { class objref }
DbObject::Next { class objref } : 1(Success), 0(Failure)
DbObject::Count { class {where {}} {distinct *} } : count
DbObject::Insert { class objref } : oid
DbObject::Update { class objref }
DbObject::Delete { class objref }
DbObject::Begin { }
DbObject::Commit { }
DbObject::Rollback { }
DbObject::Sql { sqlString }
DbObject::Nextrow { } : raw row

Private Interface


DbObject::BuildType { type value } : formattedValue
DbObject::BuildUpdate { class objref } : updateClause
DbObject::BuildSelect { class objref } : selectClause
DbObject::BuildInsert { class objref } : insertAttrclause
DbObject::BuildValue { class objref } : insertValueClause
DbObject::BuildWhere { class where } : whereClause
 

DESCRIPTION

DbObject::Connect

This procedure makes a connection to the database using the given dbuser, dbpass, and dsquery information. This same procedure can also be used to open multiple handles. If after the first invocation, no information is given, it will open another connection using the same information as given for the first handle. It returns the integer handle that is opened.

Inputs:
dbuser - (optional on 2nd invocation) - the login for the database
dbpass - (optional on 2nd invocation) - The password for the database
dsquery - ( optional database server). If none is provided, the DSQUERY environment variable is used.
Outputs:
handle(n) - an array of database connections
Returns:
handle - [1, 2 ....n] - An integer specifying a handle opened by this command.

DbObject::Close

This procedure closes the sybase connection of the highest number handle and returns that closed handle. Any variable used to hold that handle is unset.

Inputs: None
Outputs: None
Returns:
handle - [0, 1, 2 ....n] - An integer specifying a handle closed by this command

DbObject::SetHandle

This procedure set the current handle to the given handle and the current handle is returned. This remains the current handle until another DbObject::SetHandle is issued or that handle is closed. If no handle parameter is given, the current handle is returned.

Inputs:
handle - [1, 2 ......] - (optional) - A handle to set as the current handle.
Outputs: None
Returns:
handle - [0, 1, 2 ....n] - An integer specifying a the current handle

DbObject::Begin

This procedure begins a transaction on the private variable handle. It enforces single layer transactions, preventing nesting.

Inputs: None
Outputs: None
Returns: None

DbObject::Commit

This procedure commits a transaction on the private variable handle.

Inputs: None
Outputs: None
Returns: None

DbObject::Rollback

This procedure rolls back a transaction on the private variable handle. Since only single layer transactions are supported, it is important that control is returned immediately to the original transaction beginning. In doing this, rollback can be called multiple times without ill effect.

Inputs: None
Outputs: None
Returns: None

DbObject::Query

This procedure sets up a query that may involve a join across multiple tables, depending on how the object is defined. The given objref array specifies the attributes that are to be retrieved. The where clause and order by clause specify the filtering and ordering of the retrieved results. Since the where and order clauses are raw SQL strings, the application programmer is encouraged to define new member procedures so these SQL specifications remain private to this object. Note, the same objref given to this procedure must be used to retrieve the results with DbObject::Next.

Inputs:
class - The entity class to operate on
objref - A reference to an entity array representing the object instance. The attributes specified become the select list in the SQL, meaning only those values are returned in the query
where - [default {}] - the where string for the SQL statement. This is a raw where clause that does not include join constraints. All column names and values are database specific
order - [default {}] - the orderby clause for the SQL statement. All column names are database specific.
Outputs: None
Returns: None

DbObject::Next

This procedure retrieves the query results and places them in the objref array.. It is important that the given objref array is the same as the array used in DbObject::Query since the attributes given in the array determine what attributes get loaded with the returned values. Each attribute is given the value returned by the query and an oid is also assigned if the return code is 1.

Inputs:
class - The entity class to operate on
objref - A reference to an entity array representing the object instance. Only the values of the attributes given in the array are populated in the database.
Outputs:
objref - A reference to an entity array representing the object instance given to tail::Query. Each attribute is given the value returned by the query and an oid is also assigned if the return code is 1.
Returns:
1 - A row is available; 0 - No rows returned.

DbObject::Count

This procedure counts the rows that match the given where statement. A key can be passed that is unique on the primary table in the case of a join to allow the count to be distinct. It

  1. If no distinct key is given, the select statement is set to count(*)
  2. If a key is given, the select clause is set to count(distinct $distinct)
  3. Build the sequel string by concatenating the select, from and where clauses. The where clause is built by calling DbObject::BuildWhere
  4. Send of the query by calling DbObject::Sql
  5. Retrieve the count by calling DbObject::Nextrow and return this result
Inputs:
class - The entity class to operate on
where - [default {}] - the where string for the SQL statement. This is a raw where clause that does not include join constraints. All column names and values are database specific
distinct - [default *] - A key that is to be counted distinctly. This is used when a join returns multiple rows for a unique key in the primary table. these rows would be counted as one when the key is specified.
Outputs: None
Returns:
count - The count result

DbObject::Cancel

This procedure cancels the result of a query. It is used to discard unretrieved results if they are unneeded.

Inputs: None
Outputs: None
Returns: None

DbObject::Insert

This procedure inserts the object into the database. It only inserts into one table, so each attribute in the objref array must belong to that table or an error will be generated. Only the attributes that are given in the objref array are inserted. If a table contains attributes that are not given and they do not accept null, an error will be generated The oid is returned. It

  1. Builds the attribute list using DbObject::BuildInsert
  2. Builds the valueList using DbObject::BuildValue
  3. Contructs the sql string using the constructed attribute list and value list.
  4. Issues the insert using DbObject::Sql
  5. If the table contains the primary key, retrieve the identity value by calling DbObject::Sql and return it as an oid.
Inputs:
class - The entity class to operate on
objref - A reference to an entity array representing the object instance. Only the values of the attributes given in the array are populated in the database.
Outputs:
objref(oid) - the unique identifier for the object
Returns:
oid - the unique identifier for the object.

DbObject::Update

This procedure updates the object represented by the given objref array. This array must contain a valid oid. Only the values of the attributes represented in the array are updated in the database so parts of the object can be updated. This procedure performs the update to only one table as indicated in the entity this(table) variable. Therefore, all parameters in objref must belong to this table or an error will occur.

Inputs:
class - The entity class to operate on
objref - A reference to an entity array representing the object instance. Only the values of the attributes given in the array are updated in the database.
Outputs: None
Returns: None

DbObject::Delete

This procedure deletes the object represented by the given objref array. The only required attribute is the oidsince this tells the procedure which object to delete.

Inputs:
class - The entity class to operate on
objref - A reference to an entity array representing the object instance.
Outputs: None
Returns: None

DbObject::Sql

This procedure issues a Sybase sql request. If a timeout (in seconds) is given, it will return to the Tcl event loop every timeout seconds.

Inputs:
sqlString - the Sybase sql string
timeout -(in seconds) [default -1] - This procedure blocks until the Sybase server satisfies the request. However, if a timeout, in seconds, is specified, Tcl will return to the event loop every timeout seconds.
Outputs: None
Returns: None

DbObject::Nextrow

This procedure retrieves the sybase row from the private handle connection..

Inputs:
Outputs: None
Returns:
nextrow - The next sybase row of a given sql search. It returns {} if no row is present.

DbObject::BuildWhere

This procedure concatenates the given where clause with any internal entity where clause and returns the final string. If the entity has no internal where clause, the given clause is simply returned.

Inputs:
class - The entity class to operate on
where - The given SQL where string
Outputs: None
Returns:
whereClause - Returns the final where clause.

DbObject::BuildType

This procedure formats the given value according to the given type. If the type is an integer, return the given value without modification. If it is a text or datetime type, return the value enclosed in single quotes. If the keyword function is used, the value after the keyword is simply returned. This allows the use of the sybase functions. If any parameter is specified as {} or "", it will be recorded in the database as a null. Nulls are returned as {}.

Inputs:
type - [Text, Datetime, EnumText, Integer, EnumInt] - the type of the given value
value - The value to be formatted
Outputs: None
Returns:
Returns either 'value' or value depending on type

DbObject::BuildUpdate

This procedure builds the SQL update string . Only the attributes found in objref are built in the list. the oid is not included

  1. For each attribute in objref, find the type by calling Entity::GetAttribute
  2. Format the value according to the type by calling DbObject::BuildType
  3. Create the update clause and concatenate each "attribute=value" adding the commas between expressions.
  4. Return the completed update clause, "attr1=value1, attr2='value2', etc" The quotes depend on attribute type.
Inputs:
class - The entity class to operate on
objref - The array reference that represents the entity object instance.
Outputs: None
Returns:
Returns the "attr1=value1, attr2='value2', etc" The quotes depend on attribute type

DbObject::BuildSelect

This procedure builds the SQL select clause used in an SQL query string. Only the attributes found in objref are built in the list. The oid is not included.

  1. For each attribute in objref, find the database aliased column name by calling Entity::GetAttribute
  2. Create the select clause and concatenate each value adding the commas between values.
  3. Return the completed select clause, "t.attr1, e.attr2, etc"
Inputs:
class - The entity class to operate on
objref - The array reference that represents the entity object instance.
Outputs: None
Returns:
SQL select List - Returns the aliased column name "t.attr1, e.attr2, etc"

DbObject::BuildInsert

This procedure builds the SQL attribute list used in an SQL insert string. Only the attributes found in objref are built in the list. the oid is not included.

  1. For each attribute in objref, create the attribute list and concatenate each attribute adding the commas between attributes.
  2. Return the completed insert clause, "attr1, attr2, etc"
Inputs:
class - The entity class to operate on
objref - The array reference that represents the entity object instance.
Outputs: None
Returns:
SQL insert list - Returns the "attr1, attr2, etc"

DbObject::BuildValue

This procedure builds the SQL value list used in an SQL insert string. Only the values of the attributes found in objref are built in the list. The oid is not included

  1. For each attribute in objref, find the type by calling Entity::GetAttribute
  2. Format the value according to the type by calling DbObject::BuildType
  3. Create the valueClause and concatenate each value adding the commas between values.
  4. Return the completed value clause, "value1, 'value2', etc". The quotes depend on attribute type
Inputs:
class - The entity class to operate on
objref - The array reference that represents the entity object instance.
Outputs: None
Returns:
SQL value list - Returns the "value1, 'value2', etc" The quotes depend on attribute type

DIAGNOSTICS

procName

The following messages will be logged.

message1
Explanation
message2
Explanation

IMPLEMENTATION

namespace eval DbObject {
   variable handle
   variable _dbuser
   variable _dbpass
   variable _dsquery
   variable handleIndex 0
   variable numHandles 0
   variable waitInterval 30
   variable minSleepInterval 5
   variable tranflag
}

DbObject::Connect

proc DbObject::Connect { {dbuser {}} {dbpass {}} {dsquery {}}} {
   variable handle
   variable _dbuser
   variable _dbpass
   variable _dsquery
   variable handleIndex
   variable numHandles
   variable tranflag
   global env sybmsg scriptName

   logEntryLS DbgEntryExit "Entering DbObject::Connect"

       #Save the given information the first time
   if {$numHandles == 0} {
          #dbuser and dbpass must be given
#      if {[string compare $dbuser {}] == 0 || [string compare $dbpass {}] == 0} {
#         error "DbObject::Connect: Connect Failed: No values given for dbuser and dbpass"
#         return $numHandles
#      }

      if {[string equal $dbuser {}]} {
	 if [catch {set dbuser $env(DBUSER)} result ] {
	    error "DbObject::Connect failed: $result"
         }
      }
      if {[string equal $dbpass {}]} {
	 if [catch {set dbpass $env(DBPASS)} result ] {
	    error "DbObject::Connect failed: $result"
         }
      }
      set _dbuser $dbuser
      set _dbpass $dbpass
      set _dsquery $dsquery
   } else {
      if {[string compare $dbuser {}] == 0 || [string compare $dbpass {}] == 0} {
         set dbuser $_dbuser
         set dbpass $_dbpass
      }
   }

        #Open the database Connection
        #Set isolation level to allow dirty reads, so selects do not cause deadlocks
   if {[string compare $dsquery {}] == 0} {
      if [ catch {
            set handle([incr DbObject::numHandles]) [sybconnect $dbuser $dbpass $dsquery $scriptName]
            sybsql $handle($numHandles) "set transaction isolation level 0"
      } result ] {
         error "DbObject::Connect: Connect Failed: $result"
         return $numHandles
      }
   } else {
      if [ catch {
            set handle([incr DbObject::numHandles]) [sybconnect $dbuser $dbpass $dsquery]
            sybsql $handle($numHandles) "set transaction isolation level 0"
      } result ] {
         error "DbObject::Connect: Connect Failed: $result"
         return $numHandles
      }
   }
         #Only set this the first time
   if {$numHandles == 1} {
    set handleIndex 1
   }

         #Initialize the transaction for the new connection
    set tranflag($numHandles) 0

         #This variable must be set to retrieve any binary data as hex.
   set sybmsg(binaryashex) 1
   set sybmsg(nullvalue) {}
   logEntryLS DbgEntryExit "Exiting DbObject::Connect with handle, $handle($handleIndex)"
   return $numHandles
}

DbObject::Close

proc DbObject::Close {} {
   variable handle
   variable handleIndex
   variable numHandles

   if {$numHandles == 0} {return $numHandles}

   logEntryLS DbgEntryExit "Entering DbObject::Close"
   set closedHandle $numHandles
   catch {
      sybclose $handle($numHandles)
      unset handle($numHandles)
      if {$handleIndex == $numHandles} {incr DbObject::handleIndex -1}
      incr DbObject::numHandles -1
   }
   logEntryLS DbgEntryExit "Exiting DbObject::Close"
   return $closedHandle
}

DbObject::SetHandle

proc DbObject::SetHandle { {index {}} } {
   variable handle
   variable dbuser
   variable dbpass
   variable dsquery
   variable handleIndex
   variable numHandles

       #Is this a read accessor?
   if {[string compare $index {}] == 0} {return $handleIndex}

   logEntryLS DbgEntryExit "Entering DbObject::SetHandle"
   if {$index > $numHandles || $index <= 0} {
      error "DbObject::SetHandle: The requested handle does not exist"
      return $handleIndex
   }
   set handleIndex $index

   logEntryLS DbgEntryExit "Exiting DbObject::SetHandle"
   return $handleIndex
}

DbObject::BuildType

proc DbObject::BuildType {type value} {

        #Include the ability to specify characteristic functions
   if {[string compare [lindex $value 0] function] == 0} {
      return [lindex $value 1]
   }

        #Include the ability to specify null values
   if {[string compare $value {}] == 0} {
      return null
   }

   switch -- $type {
   Text -
   Datetime {return '$value'}
   EnumText {return '[Entity::Terse $value]'}
   Binary { return [format "%#8.8x" $value] }
   Integer {return $value}
   EnumInt {return [Entity::Terse $value]}
   default {return {}}
   }
}

DbObject::BuildValue

proc DbObject::BuildValue {class objref} {
   upvar $objref obj

   logEntryLS DbgEntryExit "Entering DbObject::BuildValue"

      #Skip the oid if it is a primary key 
      if {[info exists obj(oid)]} {
	 if {[string compare [Entity::GetAttribute $class oid key] Primary] == 0} {
	     set prime_oid $obj(oid)
	     unset obj(oid)
         }
      }
	 
          #Used in insert to build the value list
   foreach attr [array names obj] {

      set type [Entity::GetAttribute $class $attr type]

      if {[info exists valueClause] == 0} {
         set valueClause [DbObject::BuildType $type $obj($attr)]
      } else {
         set valueClause [cconcat $valueClause ", [DbObject::BuildType $type $obj($attr)]"]
      }
   }
      # will not have a valueClause if obj emply, so I added the next statement - Doug. 
   if {[info exists valueClause] == 0} {set valueClause {}}
   logEntryLS DbgAll "DbObject::BuildValue valueClause: $valueClause"
   logEntryLS DbgEntryExit "Exiting DbObject::BuildValue"


   if {[info exist prime_oid] } {
	set obj(oid) $prime_oid
   }
   return $valueClause
}

DbObject::BuildUpdate

proc DbObject::BuildUpdate {class objref} {
   upvar $objref obj

   logEntryLS DbgEntryExit "Entering DbObject::BuildUpdate"

   set updateClause {}

        #This prepares the update list for an update
   foreach attr [array names obj] {

          #Skip the oid, mdt, dt
      switch $attr {
      oid -
      dt -
      mdt {continue}
      }
           #Retrieve the attribute column name and remove the alias
      set column [Entity::GetAttribute $class $attr column]
      set column [lindex [split $column .] 1]

      set type [Entity::GetAttribute $class $attr type]

      if {[string compare $updateClause {}] == 0} {
         set updateClause "$column = [DbObject::BuildType $type $obj($attr)]"
      } else {
         set updateClause [cconcat $updateClause ", $column = [DbObject::BuildType $type $obj($attr)]"]
      }
   }
   logEntryLS DbgAll "DbObject::BuildUpdate updateClause: $updateClause"
   logEntryLS DbgEntryExit "Exiting DbObject::BuildUpdate"
   puts "inside buildupdate updateClause: $updateClause"
   return $updateClause
}

DbObject::BuildWhere

proc DbObject::BuildWhere {class where} {
   upvar #0 $class\::this this

   logEntryLS DbgEntryExit "Entering DbObject::BuildWhere"

        #This builds the where clause for the Query
   if {[string compare $this(where) {}] != 0} {
      if {[string compare $where {}] == 0} {
         return $this(where)
      } else {
         return "$this(where) and $where"
      }
   }
   logEntryLS DbgAll "DbObject::BuildWhere whereClause: $where"
   logEntryLS DbgEntryExit "Exiting DbObject::BuildWhere" 
   return $where
}

DbObject::BuildSelect

proc DbObject::BuildSelect {class objref} {
   upvar $objref obj

   set selectClause {}

   logEntryLS DbgEntryExit "Entering DbObject::BuildSelect"

        #This builds the select clause for both select and insert
   foreach attr [array names obj] {

          #Skip the oid
      if {[string compare oid $attr] == 0} {continue}

      if {[string match $selectClause {} ]} {
	 set selectClause [Entity::GetAttribute $class $attr column]
      } else {
         set selectClause [cconcat $selectClause ", [Entity::GetAttribute $class $attr column]"]
      }
   }
   logEntryLS DbgAll "DbObject::BuildSelect selectClause: $selectClause"
   logEntryLS DbgEntryExit "Exiting DbObject::BuildSelect"
   return $selectClause
}

DbObject::BuildInsert

proc DbObject::BuildInsert {class objref} {
   upvar $objref obj
   upvar #0 $class\::attr classAttr

   logEntryLS DbgEntryExit "Entering DbObject::BuildInsert"

      #Skip the oid if it is a primary key 
   if {[info exists obj(oid)]} {
       if {[string compare [Entity::GetAttribute $class oid key] Primary] == 0} {
           set prime_oid $obj(oid)
           unset obj(oid)
       }
   }
	 

        #This builds the select clause for both select and insert 
   foreach attr [array names obj] {
         #Retrieve the attribute column name and remove the alias 
      set column [Entity::GetAttribute $class $attr column]
      set column [lindex [split $column .] 1]
      if {[info exists insertClause] == 0} {
         set insertClause $column
      } else {
         set insertClause [cconcat $insertClause ", $column"]
      }
   }
   logEntryLS DbgAll "DbObject::BuildInsert insertClause: $insertClause"
   logEntryLS DbgEntryExit "Exiting DbObject::BuildInsert"

   if {[info exist prime_oid] } {
	set obj(oid) $prime_oid
   }
   return $insertClause
}

DbObject::Query

proc DbObject::Query { class objref {where {}} {order {}} } {
   variable waitInterval
   global sybmsg
   upvar $objref obj
   upvar #0 $class\::this this
   upvar #0 $class\::attr attr

   logEntryLS DbgEntryExit "Entering DbObject::Query"

       #filter out any attributes not belonging to this class
   array set filteredObj [Entity::ArrayCopy $class obj] 

   set oidColumn [Entity::GetAttribute $class oid column]
   set selectString [DbObject::BuildSelect $class filteredObj]

   if [string match $selectString {} ] {
      set sqlString "select $oidColumn"
   } else {
      set sqlString "select $oidColumn, $selectString"
   }

   set sqlString [cconcat $sqlString " from $this(from)"]
   set where [DbObject::BuildWhere $class $where]
   if {[string compare $where {}] != 0} {set sqlString [cconcat $sqlString " where $where"]}
   if {[string compare $order {}] != 0} {set sqlString [cconcat $sqlString " order by $order"]}

   logEntryLS DbgMsg "DbObject::Query SqlString: $sqlString"

   if [catch {DbObject::Sql $sqlString $waitInterval} result] {
      error "DbObject::Query: $result: $sybmsg(msgtext)"
   }
}

DbObject::GetListOf

proc DbObject::GetListOf { class attr {where {}} {order {}} } {
   variable waitInterval
   upvar #0 $class\::this this

   logEntryLS DbgEntryExit "Entering DbObject::GetListOf"

   set valueList {}
   set selectString [Entity::GetAttribute $class $attr column]
   set sqlString "select distinct $selectString"

   set sqlString [cconcat $sqlString " from $this(from)"]

   set where [DbObject::BuildWhere $class $where]
   if {[string compare $where {}] != 0} {set sqlString [cconcat $sqlString " where $where"]}
   if {[string compare $order {}] != 0} {set sqlString [cconcat $sqlString " order by $order"]}

   logEntryLS DbgMsg "DbObject::Count SqlString: $sqlString"

   if [catch {
      DbObject::Sql $sqlString $waitInterval
      set row [DbObject::Nextrow]
      set x [llength $row]

      while {[llength $row] >0} {
         lappend valueList [lindex $row 0]
         set row [DbObject::Nextrow]
      }
   } result] {
      error "DbObject::GetListOf: $result: $result"
   }
   return $valueList
}

DbObject::Count

proc DbObject::Count { class {where {}} {distinct *} } {
   variable waitInterval
   global sybmsg
   upvar #0 $class\::this this

   logEntryLS DbgEntryExit "Entering DbObject::Count"

   if {[string match $distinct *]} {
      set sqlString "select count(*)"
   } else {
      set sqlString "select count(distinct $distinct)"
   }
   set sqlString [cconcat $sqlString " from $this(from)"]
   set where [DbObject::BuildWhere $class $where]
   if {[string compare $where {}] != 0} {set sqlString [cconcat $sqlString " where $where"]}

   logEntryLS DbgMsg "DbObject::Count SqlString: $sqlString"

   if [catch {
      DbObject::Sql $sqlString $waitInterval
      set count [DbObject::Nextrow]
   } result] {
      error "DbObject::Count: $result: $sybmsg(msgtext)"
   }
   return $count 
}

DbObject::Next

proc DbObject::Next { class objref } {
   upvar $objref obj
   global sybmsg

   logEntryLS DbgEntryExit "Entering DbObject::Next"

       #filter out any attributes not belonging to this class
   array set filteredObj [Entity::ArrayCopy $class obj] 

       #REG_ROW, NO_MORE_ROWS - REG_ROW will be returned when the array is filled
   set row [DbObject::Nextrow]
   logEntryLS DbgMsg "Returned Row: $row"
   switch $sybmsg(nextrow) {
   REG_ROW {
      set obj(oid) [lindex $row 0]
      set i 1
      foreach attr [array names filteredObj] {

             #Skip the oid
         if {[string compare oid $attr] == 0} {continue}

             #Check for binary
         switch -- [Entity::GetAttribute $class $attr type] {
            Binary { set obj($attr) [join [list 0x [string trim [lindex $row $i]]] {}] }
            EnumText -
            EnumInt { set obj($attr) [Entity::EnumValue [Entity::GetAttribute $class $attr enum] [lindex $row $i]] }
            default { set obj($attr) [lindex $row $i] }
         }
         incr i
      }
      logEntryLS DbgEntryExit "Exiting DbObject::Next after retrieving oid, $obj(oid) "
      return 1
   }
   default {
      logEntryLS DbgEntryExit "Exiting DbObject::Next: No row retrieved"
      return 0 
   }
   }
}

DbObject::Cancel

proc DbObject::Cancel {} {
   variable handle
   variable handleIndex
   variable numHandles

   if {$numHandles != 0} {
      sybcancel $handle($handleIndex)
   }
}

DbObject::Insert

proc DbObject::Insert { class objref } {
   variable handle
   variable handleIndex
   variable waitInterval
   global sybmsg
   upvar $objref obj
   upvar #0 $class\::this this

   logEntryLS DbgEntryExit "Entering DbObject::Insert"
   set attrList [DbObject::BuildInsert $class obj]

   set valueList [DbObject::BuildValue $class obj]
   set sqlString "insert into [lindex $this(table) 0] ($attrList) values ($valueList)"

   logEntryLS DbgMsg "DbObject::Insert SqlString: $sqlString"

   if [ catch { DbObject::Sql $sqlString $waitInterval } result ] { 
         error "DbObject::Insert: $result: $sybmsg(msgtext)"
   }


if {[string compare [Entity::GetAttribute $class oid key] Primary] == 0} {
      DbObject::Sql "select @@identity" $waitInterval
      set obj(oid) [lindex [sybnext $handle($handleIndex)] 0]
   }
   logEntryLS DbgEntryExit "Exiting DbObject::Insert with oid, $obj(oid)"
   return  $obj(oid) 
}

DbObject::Update

proc DbObject::Update {class objref} {
   variable waitInterval
   global sybmsg
   upvar $objref obj
   upvar #0 $class\::this this

   logEntryLS DbgEntryExit "Entering DbObject::Update"

   set setList [DbObject::BuildUpdate $class obj]

        #If there is no setlist, we are done
   if {[string compare $setList {}] == 0} { return }

   set column [Entity::GetAttribute $class oid column]
   set column [lindex [split $column .] 1]
   set where "$column = $obj(oid)"
   set sqlString "update [lindex $this(table) 0] set $setList from $this(table) where $where"

   puts "inside the Update proc sqlString: $sqlString"
   logEntryLS DbgMsg "DbObject::Update SqlString: $sqlString"

   if [ catch { DbObject::Sql $sqlString $waitInterval } result ] { 
       error "DbObject::Update: $result: $sybmsg(msgtext)"
   }

   logEntryLS DbgEntryExit "Exiting DbObject::Update"
}

DbObject::Delete

proc DbObject::Delete {class objref} {
   variable waitInterval
   global sybmsg
   upvar $objref obj
   upvar #0 $class\::this this

   logEntryLS DbgEntryExit "Entering DbObject::Delete"

   set where "[Entity::GetAttribute $class oid column] = $obj(oid)"
   set sqlString "delete [lindex $this(table) 0] from $this(table) where $where"

   logEntryLS DbgMsg "DbObject::Delete SqlString: $sqlString"

   if [ catch { DbObject::Sql $sqlString $waitInterval} result ] { 
        error "DbObject::Delete: $result: $sybmsg(msgtext)"
   }
   logEntryLS DbgEntryExit "Exiting DbObject::Delete"
}

DbObject::Begin

proc DbObject::Begin {} {
   variable waitInterval
   variable tranflag
   variable handleIndex
   global sybmsg

   logEntryLS DbgEntryExit "Entering DbObject::Begin"

   if {$tranflag($handleIndex) == 0} {
      if [catch {DbObject::Sql "begin transaction" $waitInterval} result] {
         error "DbObject::Begin: $result: $sybmsg(msgtext)"
	 return
      }
   }

  incr tranflag($handleIndex)
  
  logEntryLS DbgMsg "Begin Transaction"
   logEntryLS DbgEntryExit "Exiting DbObject::Begin"
}

DbObject::Commit

proc DbObject::Commit {} {
   variable waitInterval
   variable tranflag
   variable handleIndex
   global sybmsg

   logEntryLS DbgEntryExit "Entering DbObject::Commit"

   if {$tranflag($handleIndex) == 1} {
      if [catch {DbObject::Sql "commit transaction" $waitInterval} result] {
         error "DbObject::Begin: $result: $sybmsg(msgtext)"
	 return
      }
   }

   if {$tranflag($handleIndex) > 0 } {
     incr tranflag($handleIndex) -1
   }
   
   logEntryLS DbgMsg "Commit Transaction"
   logEntryLS DbgEntryExit "Exiting DbObject::Commit"
}

DbObject::Rollback

proc DbObject::Rollback {} {
   variable waitInterval
   variable tranflag
   variable handleIndex
   global sybmsg

   logEntryLS DbgEntryExit "Entering DbObject::Rollback"

   if {$tranflag($handleIndex) > 0} {
      if [catch {DbObject::Sql "rollback transaction" $waitInterval} result] {
         error "DbObject::Begin: $result: $sybmsg(msgtext)"
	 return
      }
      set tranflag($handleIndex) 0
   }

   logEntryLS DbgMsg "Rollback Transaction"
   logEntryLS DbgEntryExit "Exiting DbObject::Rollback"
}

DbObject::Sql

proc DbObject::Sql {sqlString {timeout -1}} {
   variable handle
   variable handleIndex
   variable minSleepInterval
   global sybmsg

   logEntryLS DbgEntryExit "Entering DbObject::Sql $sqlString, $timeout"
   logEntryLS DbgMsg "DbObject::Sql SqlString: $sqlString"

   set start [clock seconds]
   set numDeadlocks 0

       #A timeout of -1 is equivalent to synchronous
       #If not -1, it is a timeout in seconds
   if {[string compare $timeout -1] != 0} {
      set msInterval [expr $timeout * 1000]
   } else {set msInterval -1}

        #Retry on deadlocks until successful
   while {1} {
        #Send an asyncronous query
      if [catch {
          set rtn [sybsql $handle($handleIndex) $sqlString]
          #set rtn [sybsql $handle($handleIndex) $sqlString async]
          #Wait until the results are ready, but take enter the event loop after timeout seconds
          #  while { [string compare [sybpoll $handle $msInterval] $handle] != 0 } {
          #    update ;# catch up on HeartBeats, etc.
          #    logEntryLS Warning "DbObject::Sybsql: Database not responding after $timeout seconds!"
          #  }
      } result] {
               #Check for the deadlock msgno and retry
          switch -exact -- $sybmsg(msgno) {
             1205 {
                 incr numDeadlocks
                 logEntryLS DbgMsg "DbObject::Sql: DEADLOCK retry, $numDeadlocks"                 

                    #Alternate wait times before the retry
                 if {[expr $numDeadlocks % 2]} {
                    sleep $minSleepInterval
                 } else {
                    sleep [expr $minSleepInterval * 2]
                 }
                 continue
             }
             default {error "DbObject::Sybsql: $result: $sybmsg(msgtext)"}
          }          
      } else {
         logEntryLS DbgEntryExit "Exiting DbObject::Sql with $numDeadlocks DEADLOCK retries"
         return
      }
   }   
}

DbObject::Nextrow

proc DbObject::Nextrow {} {
   variable handle
   variable handleIndex
   global sybmsg

   return [sybnext $handle($handleIndex)]
}

EXAMPLES

NOTES

WARNINGS

SEE ALSO


AUTHORS

by Timothy L. Eshelman