JupiterCrash has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I suspect that this is more of a database issue than a perl problem, but I have been struggling with it for weeks and so I am now turning to you folks. It would be just great if anybody had *any* information on this, as I'm just plain stumped and out of ideas.

I am using dbi/dbd on Windows, using the odbc dbd and SQL Server. I am updating text fields, and occasionally I will get this bizarre error. I catch the error, disconnect/reconnect, and try again up to 10 times. (that was my last attempt at trying to "fix" this.)

When it wants to fail, it fails. However at other times (and usually) the same command will work fine. The error is happening on a customer's remote server - so I haven't had direct access to it myself. I keep sending patches back and forth trying to get to the bottom of this. Here is the error I am generating :

Slept 3s... Trying again after reconnect.
Slept 2s... Trying again after reconnect.
Slept 1s... Trying again after reconnect.
Slept 1s... Trying again after reconnect.
Slept 3s... Trying again after reconnect.
Slept 1s... Trying again after reconnect.
Slept 1s... Trying again after reconnect.
Slept 3s... Trying again after reconnect.
Slept 2s... Trying again after reconnect.
DBD::ODBC::st execute failed: MicrosoftODBC SQL Server DriverSQL ServerNULL textptr (text, ntext, or image pointer) passed to UpdateText function. (SQL-42000) MicrosoftODBC SQL Server DriverSQL ServerThe statement has been terminated. (SQL-01000)(DBD: st_execute/SQLExecute err=-1) at ...

UPDATE MASTER5 SET Promote__bFile__bList=?,Post__bPromote__bRequirements=? WHERE mrID=300806


It is basically coming from this code:

# the two sql statements must function as one single, atomic transacti +on # we check for errors after the eval block. LABEL_START_TRANSACTION: eval { # AutoCommit must be off for transaction handling, it is put b +ack on # after the transaction. $dbh->{AutoCommit} = 0; $dbh->{RaiseError} = 1; # if nothing is being updated, then we don't want to fail if ($setFields) { $sql = "UPDATE $table SET $setFields WHERE mrID=$mrNumber" +; # prepare the sql statement $sth = $dbh->prepare($sql); # bind the parameters (description and any multi-line fiel +ds) for (my $i=0; $i<=$#projValuesToBind; $i++) { $sth->bind_param($i+1, $projValuesToBind[$i], DBI::SQL +_LONGVARCHAR); } # execute the sql statement to insert row into MASTERx tab +le $sth->execute(); } # get the new address book fields if($ENV{'ABMASTER'} && $setABFields) { $sql = "UPDATE ${table}_ABDATA SET $setABFields WHERE mrID +=$mrNumber"; # prepare the sql statement $sth = $dbh->prepare($sql); # bind the parameters (any multi-line fields) for (my $i=0; $i<=$#ABValuesToBind; $i++) { $sth->bind_param($i+1, $ABValuesToBind[$i], DBI::SQL_L +ONGVARCHAR); } # execute the sql statement to update row into MASTERx_ABD +ATA table $sth->execute(); } # there are sql statements that need to be comitted if (($setABFields && $ENV{'ABMASTER'}) || $setFields) { # everything worked, so commit the database changes. $dbh->commit(); } }; if ($@) # error during transaction, rollback and report informatio +n { $errorCount++; my $originalError = $@; # retry for up to five errors if ($errorCount <= $DBI_RETRY_COUNT) { # roll back the transaction # (catch errors from rolling back. otherwise the script +will just die.) eval { $dbh->rollback(); }; if ($@) # error rolling back! { $errStr .= "$originalError <BR><BR>\n"; $errStr .= "$sql <BR><BR>\n $ABsql <BR>\n"; &errorExit(203, "mrChange transaction error: ", $errSt +r, "<BR>\nalso failed while rolling back: $@<BR><BR>\n"); } # sleep a random max of $DBI_RETRY_SECONDS my $retrySeconds = int(rand($DBI_RETRY_SECONDS)+1); $errStr .= "Slept ${retrySeconds}s... "; sleep $retrySeconds; $errStr .= "Trying again after reconnect.<BR>\n"; # reconnect &disconnectFromDBD; &connectToDBD; goto LABEL_START_TRANSACTION; } # roll back the transaction # (catch errors from rolling back. otherwise the script will + just die.) eval { $dbh->rollback(); }; if ($@) # error rolling back! { $errStr .= "$originalError <BR><BR>\n"; $errStr .= "$sql <BR><BR>\n $ABsql <BR>\n"; &errorExit(203, "mrChange transaction error: ", $errStr, " +<BR>\nalso failed while rolling back: $@<BR><BR>\n"); } # rollback was successful: # append warning from dbi to error string $errStr .= "$originalError <BR><BR>\n"; $errStr .= "$sql <BR><BR>\n $ABsql <BR>\n"; &errorExit(203, "mrChange transaction error: ", $errStr); } # AutoCommit MUST get turned back on, as the rest of FootPrints do +es not # use transactions. $dbh->{AutoCommit} = 1; $dbh->{RaiseError} = 0;


It's failing on the execute, always when binding a parameter to a text field.

Thanks for ANY help!

Matt
  • Comment on OT?>> DBD::ODBC::st execute failed: NULL textptr passed to UpdateText function-- why??
  • Download Code

Replies are listed 'Best First'.
Re: OT?>> DBD::ODBC::st execute failed: NULL textptr passed to UpdateText function-- why??
by terce (Friar) on Jun 01, 2005 at 15:50 UTC
    You don't say whether or not the failure of the query is dependent on the data being input - I'd be suspicious that that might be at the root of the issue. Have you been able to try an example of the query which fails through another interface (like query analyser)? This would at least give you some pointers on whether it's DBD, ODBC or SQL server causing the problem

    The error message is not well documented (it's SQL error number 7133), but I assume from its text that you're trying to update a long data field (text, ntext, image). I've experienced some (different) complications accessing long data fields through ODBC, but rather than waste hours on them, I used the sqloledb interface instead.

    Check the data first of all, as if this was a problem with ODBC I suspect it would fail 100% rather than intermittently.

    UPDATE: Just been reading up on UPDATETEXT in MSSQL books online. Have you checked that the row to be updated actually exists - the null pointer error suggests that it might not.
      Thanks for the reply.

      Yes, I think I'm certain that the row being updated does exist- I am pretty sure that is not the problem. Also, updates on non-text fields never fail, and the users are usually able to "hit the back button and try again" I am told. (this is why I thought a retry in a loop might help)

      However, you are right - I do not yet know if it is dependent on the input data which is bound to the parameters. I should include the bound parameter values in my error page. I think this will be my next step.

      It is difficult because I'm forced to debug this by providing patches - I don't have access to the server that runs my application. Worse yet, the people who run the patches are in a timezone 12 hours different than mine! So it's a slow process.

      Thanks-

      Matt
        I've just had a chance to do some testing on this.

        It looks like the problem is your code attempts to use the UPDATETEXT command on a text field which is currently set to NULL. Certainly, attempting to do this will produce the error message you report.

        You can test this using the attached script, which creates a table (text_text) with an id column, a varchar field and a text field, inserts two rows with the text field set to null, updates the first using a regular update statement then amends it with UPDATETEXT, then updates the second (null) text field using UPDATETEXT.
        CREATE TABLE [test_text] ( [id] [int] IDENTITY (1, 1) NOT NULL , [vc] [varchar] (50) COLLATE Latin1_General_CI_AS NULL , [txt] [text] COLLATE Latin1_General_CI_AS NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO insert test_text (vc) select 'hello, number 1' go insert test_text (vc) select 'hello, number 2' go update test_text set txt = '12325rfegsfdgervsdfgvsdfgvfdsgv' where id = 1 go DECLARE @ptrval binary(16) SELECT @ptrval = TEXTPTR(tt.txt) FROM test_text tt WHERE tt.id = 1 UPDATETEXT test_text.txt @ptrval 16 1 'b' GO DECLARE @ptrval binary(16) SELECT @ptrval = TEXTPTR(tt.txt) FROM test_text tt WHERE tt.id = 2 UPDATETEXT test_text.txt @ptrval 16 1 'b' GO
        Perhaps ODBC statements use UPDATETEXT rather than a conventional update statement. I realise it's late in the day to be suggesting this, but do you have to use ODBC rather than OLEDB?

        UPDATE: A further test reveals that executing the SQL directly using Win32::ODBC does not show a similar error. I don't have time to run the test using DBD::ODBC, but it's possible that that's where the problem lies.