I would love to hear more about this driver that lets you access SQL Server 7 data.  I 
am migrating to a NetBSD box and would like to have access to the SQL Server 7 
database until it is migrated to postgresql.

As far as transactions, I am currently using nsodbc on a Win32 box to talk to SQL 
Server 7.  If I need a transaction I use a stored procedure, or I just code it into 
the sql string that I send to the database via a dml call.  Sometimes a combination of 
the two is required, taking advantage of an error code returned by the SP.  Here is an 
example...

proc processUpdateProgramFunding {db formdata} {

set planYear [checkCookie planYear]
set totalCount [ns_set get $formdata totalCount]
set PROGRAMNO [ns_set get $formdata PROGRAMNO]
# Build the sql string and wrap it in a transaction.  I wish there was
# a prettier way to do this.  This is a stupid programmer trick, but I
# can't see how else to do it in the context of HTML.

set sqlString "DECLARE @ROWS int, @ID int, @ERRORCODE int SELECT @ROWS=0, @ERRORCODE = 
0 "
append sqlString "BEGIN TRANSACTION SET NOCOUNT ON "
set varList {DELETE INSERT UPDATE FUNDSOURCEID PROGFUNDEFFDATE PROGFUNDTERMDATE 
PROGFUNDAMOUNT}
for {set i 1} {$i<=$totalCount} {incr i} {
    foreach var $varList {
        set $var [ns_set get $formdata $var$i]
    }

# A bad hack to make sure the dates are within the plan year before they go to the
# database.  Database does not know what plan year the user is logged in for.   I
# could do it in an insert stored procedure, if I could figure out how to parse
# the input in a stored procedure.  Sigh...

    if {[string length $DELETE] && [string length $INSERT] && [string length 
$UPDATE]>0} {
        if {[expr [clock scan $PROGFUNDEFFDATE]<[clock scan $planYear-01-01]]} {
            ns_puts "<FONT COLOR=RED>Start date is outside current logged in plan 
year: $planYear</FONT>"
            ns_adp_break
        }
        if {[expr [clock scan $PROGFUNDTERMDATE]>[clock scan $planYear-12-31]]} {
            ns_puts "<FONT COLOR=RED>End date is outside current logged in plan year: 
$planYear</FONT>"
            ns_adp_break
        }
    }
    if {[string length $DELETE]>0} {
        append sqlString "IF @ERRORCODE = 0 EXEC @ERRORCODE = vsp_DeleteProgFund 
'$DELETE' \n"
        } elseif {[string length $INSERT]>0} {
        append sqlString "IF @ERRORCODE = 0 EXEC @ERRORCODE = vsp_InsertProgFund "
        append sqlString "'$PROGFUNDEFFDATE', '$PROGFUNDTERMDATE', "
        append sqlString "'$FUNDSOURCEID', $PROGFUNDAMOUNT, '$PROGRAMNO' \n"
        } elseif {[string length $UPDATE]>0} {
        append sqlString "IF @ERRORCODE = 0 EXEC @ERRORCODE = vsp_UpdateProgFund "
        append sqlString "$UPDATE, '$PROGFUNDEFFDATE', '$PROGFUNDTERMDATE', "
        append sqlString "'$FUNDSOURCEID', $PROGFUNDAMOUNT, '$PROGRAMNO' "
    }
}
append sqlString "EXEC @ERRORCODE = vsp_CheckProgFundRanges $PROGRAMNO, $planYear IF 
@ERRORCODE <> 0 BEGIN "
append sqlString "ROLLBACK TRANSACTION RETURN "
append sqlString "END COMMIT TRANSACTION"

# Send the string and see what happens...

catch [set res [ns_db dml $db "$sqlString"]] error
if [string length $error]>0 {
    ns_puts "<FONT COLOR=RED>$error</FONT>"
    updateProgramFunding $db $formdata
    } else {
    ns_puts "<FONT COLOR=BLUE>Update Successful.</FONT>"
    updateProgramFunding $db $formdata
}

}

Since each call to nsodbc is a separate 'connection' as far as the database is 
concerned, you have to begin and end the transaction in the same call.  As I said, 
most times a simple call to a stored procedure is best, but the input form that the 
above lame code parses can do one of 3 different things for each group of form data.  
Here is one of the stored procedures it calls...

CREATE PROCEDURE vsp_InsertProgFund
@PROGFUNDEFFDATE datetime,
@PROGFUNDTERMDATE datetime,
@FUNDSOURCEID int,
@PROGFUNDAMOUNT money,
@PROGRAMNO char(10)
AS
DECLARE
@PROGFUNDRANGEID int,
@ERRORCODE int

SELECT @ERRORCODE = 0

-- See if there is an existing parent record

IF EXISTS(SELECT * FROM ProgramFundRanges WHERE PROGFUNDEFFDATE = @PROGFUNDEFFDATE
    AND PROGFUNDTERMDATE = @PROGFUNDTERMDATE AND PROGRAMNO = @PROGRAMNO)

BEGIN

    -- Get his ID number for the insert

    SELECT @PROGFUNDRANGEID = PROGFUNDRANGEID FROM ProgramFundRanges WHERE 
PROGFUNDEFFDATE = @PROGFUNDEFFDATE
    AND PROGFUNDTERMDATE = @PROGFUNDTERMDATE AND PROGRAMNO = @PROGRAMNO

END
ELSE
BEGIN

    -- If not, insert a new one and get the id

    INSERT INTO ProgramFundRanges (PROGRAMNO, PROGFUNDEFFDATE, PROGFUNDTERMDATE)
        VALUES (@PROGRAMNO, @PROGFUNDEFFDATE, @PROGFUNDTERMDATE)
    SELECT @ERRORCODE = @@ERROR, @PROGFUNDRANGEID = @@IDENTITY

END

-- Do the insert

IF @ERRORCODE = 0
BEGIN
    INSERT INTO ProgramFund (PROGFUNDRANGEID, FUNDSOURCEID, PROGFUNDAMOUNT)
        VALUES (@PROGFUNDRANGEID, @FUNDSOURCEID, @PROGFUNDAMOUNT)
    SELECT @ERRORCODE = @@ERROR
END

RETURN @ERRORCODE

This one does not use a transaction, since I always call it from the kludge above.  It 
simply returns the error code that tells the whole bloody thing to roll back if 
something went wrong.  If it is a standalone stored procedure I use a transaction and 
issue nice descriptive error codes with the rollback and display them to the user 
directly.

I hope this helps.

Ian A. Harding
Programmer/Analyst II
Tacoma-Pierce County Health Department
(253) 798-3549
mailto: [EMAIL PROTECTED]

>>> [EMAIL PROTECTED] 04/30/01 05:34PM >>>
I've been using nsodbc a bit and I wonder how you nsodbc users implement
transactions?

It appears that nsodbc seems to default to autocommit mode, and I can't
figure how to issue a "begin transaction" against it.  I just get dml
errors back (when working with SQL Server 7)

What are you guys doing?

Thanks,

Jerry

P.S. I am working with Rob Mayoff's db2 driver and modifying it to be a
more compliant ODBC driver.  It appears to be working nicely with SQL
Server 7, and I can commit/rollback transactions using his driver....
=====================================================
Jerry Asher                       [EMAIL PROTECTED]
1678 Shattuck Avenue Suite 161    Tel: (510) 549-2980
Berkeley, CA 94709                Fax: (877) 311-8688

Reply via email to