Handing multiple dbcount from freetds?

172 views Asked by At

I have a few queries which should return multiple 'affected rows' (and do so when running via SQL Server Management Studio), but I cannot seem to figure out how to properly call dbcount more than one time.

This is my testing function using the FreeTDS library:

- (BOOL) testCommand: (NSString*) queryToExecute
{
    RETCODE retcode;

    // Set our command
    retcode = dbcmd(dbProc, [queryToExecute UTF8String]);
    if(SUCCEED != retcode)
    {
        return NO;
    } // End of failed to set command

    retcode = dbsqlexec(dbProc);
    if(SUCCEED != retcode)
    {
        NSLog(@"Query failure, retcode was: %d", retcode);

        // Error handling goes here

        return NO;
    } // End of failed to sqlexec

    while(SUCCEED == (retcode = dbresults(dbProc)))
    {
        if(SUCCEED == (retcode = dbrows(dbProc)))
        {
            // Loop though our records
            NSUInteger rowCount = 0;
            while (NO_MORE_ROWS != (retcode = dbnextrow(dbProc)))
            {
                ++rowCount;
            } // End of records loop

            NSLog(@"Command completed successfully. (%ld results).", rowCount);
        } // End dbrows succeeded
        else
        {
            NSNumber * numberOfRowsAffected = [NSNumber numberWithInt: dbcount(dbProc)];
            bool isCountReal = dbiscount(dbProc);
            NSLog(@"Command completed successfully. (%@ rows affected). (%@).",
                  numberOfRowsAffected,
                  isCountReal ? @"YES" : @"NO");
        }
    } // End of dbresults loop

    return YES;
}

If I run queries via this code vs SSMS I get different results:

update actor set [first_name] = 'PENELOPE' where first_name = 'PENELOPE';
SELECT * FROM actor;
update actor set [first_name] = 'NICK' where first_name = 'NICK';
SELECT * FROM actor;

SSMS:

(4 row(s) affected)
(200 row(s) affected)
(3 row(s) affected)
(200 row(s) affected)

FreeTDS:

Command completed successfully. (4 rows affected).
Command completed successfully. (200 results).
Command completed successfully. (200 results).

Query:

SELECT * FROM actor;
update actor set [first_name] = 'PENELOPE' where first_name = 'PENELOPE';

SSMS:

(200 row(s) affected)
(4 row(s) affected)

FreeTDS:

Command completed successfully. (200 results).

Query:

update actor set [first_name] = 'PENELOPE' where first_name = 'PENELOPE';
SELECT * FROM actor;

SSMS:

(4 row(s) affected)
(200 row(s) affected)

FreeTDS:

Command completed successfully. (4 rows affected).
Command completed successfully. (200 results).

I have confirmed via SQL Server Profiler that the commands are executed the same both from SSMS and from my FreeTDS code.

As you can see from the FreeTDS code/output I am never able to get more than one count of the number of affected rows. I am assuming that I've done something wrong somewhere along the lines or that I'm missing something, but so far I have been unable to figure it out. I've gone through the FreeTDS documentation multiple times.

Can anyone point me in the right direction?

1

There are 1 answers

0
Kyle On BEST ANSWER

Turns it it was an issue in FreeTDS (issue IMO, but for some others it may be the required behaviour).

My fix was to replace the following code in the _dbresults method:

            switch (dbproc->dbresults_state) {

            case _DB_RES_INIT:
            case _DB_RES_NEXT_RESULT:
                dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
                if (done_flags & TDS_DONE_ERROR)
                    return FAIL;
                break;

With:

            switch (dbproc->dbresults_state) {

            case _DB_RES_INIT:
                dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
                if (done_flags & TDS_DONE_ERROR)
                    return FAIL;
                break;

            case _DB_RES_NEXT_RESULT:
                dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
                if (done_flags & TDS_DONE_ERROR)
                    return FAIL;

                return SUCCEED;
                break;

In the previous code, a loop would continue until a result set, or end of result was found. In my case, I wanted to be able to match SSMS output. In my tests so far, this seems to work and if I determine everything is good, I will look into submitting a patch to FreeTDS.

I didn't mess with the _DB_RES_INIT switch as I really didn't want to dig that far into it and that did not seem to be my problem, so I left that case as was.