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

I'm currently attempting to re-write a cgi-shell script to query an MS SQL database into perl.
The shell script uses sqsh compiled against freetds to access the database, which whilst it works, doesn't really give the level of flexibility I'm after.
I'm confident that I'm managing to turn the query string, into appropriate SQL properly, but I'm having problems actually running the query and getting some results.
The SQL I'm trying to run is this:
set TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; set quoted_identifier on use "smdb"; SELECT "_SMDBA_"."_TELMASTE_".SEQUENCE AS "Problem #", "_SMDBA_"."_CUSTOMER_".FNAME + ' ' + "_SMDBA_"."_CUSTOMER_".NAME AS "N +ame", "_SMDBA_"."_CUSTOMER_".EXT AS "Cl. Ext", "_SMDBA_"."_TELMASTE_"."DESCRIPTION" AS "Problem Description", "_SMDBA_"."_TELMASTE_"."DATE OPEN" AS "Opened", "_SMDBA_"."_PERSONNEL_".CODE AS "Assigned To" FROM "_SMDBA_"."_CUSTOMER_","_SMDBA_"."_TELMASTE_","_SMDBA_"."_PERSONN +EL_" WHERE "_SMDBA_"."_TELMASTE_"."SENT TO" = "_SMDBA_"."_PERSONNEL_".SEQUE +NCE AND "_SMDBA_"."_TELMASTE_".CLIENT = "_SMDBA_"."_CUSTOMER_".SEQUENCE AND "_SMDBA_"."_PERSONNEL_".CODE LIKE 'MUSER' AND "_SMDBA_"."_TELMASTE_".STATUS = 'O' ORDER BY "Problem #";
This works fine when cut/pasted directly into sqsh. The code to run it against my database looks like this:
my $DSN = 'server=sqlsrv;database=\"smdb\"'; #my $DSN = 'server=sqlsrv'; #my $DSN = 'database=smdb;server=sqlsrv $db_handle = DBI->connect("dbi:Sybase:$DSN", $DB_User{'username'}, $DB_User{'password'}, { RaiseError => 0, PrintError => 1, AutoCommit => 0, syb_quoted_identifier => 1, syb_rowcount => ( $input{rowcount} ) + ? $input{rowcount} : 0 } ) or warn "$DBI::errstr"; $db_handle -> do ( "set TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" + ); $db_handle -> do ( "set quoted_identifier on;" ); $db_handle -> do ( " use \"smdb\";" ); $trans_handle -> $dh_handle -> prepare ( $sql ); if ( $trans_handle -> execute ) { while ( @row = $trans_handle -> fetchrow ) { print "<TR><TD>",join("</TD><TD>",@row), "</TD></TR>\n"; } }

This code merely returns 'Changed database context to 'master''. Which is expected, since that's the default for the user I connect as. I'd also expect another message though, saying that it had changed to a different context, which I'm not seeing.
After some messing around, it seems that if I do
$db_handle -> execute or die "$DBI::errstr"
then it fails sure enough, but there's no 'die' message.
I can get _some_ sql to work. The basic
$db_handle -> prepare("select \@\@servername");
seems to work just fine.
Now I'm far from a perl/database guru, so I'm fairly convinced that there's something pretty obvious that I've missed. However, I've spend half a day trolling the web, and experimenting with different combinations, so if anyone has an insight into what I'm doing wrong, a suggesting for 'better' diagnostics, or an example of how it should be done, I'd be deeply appreciative.

Replies are listed 'Best First'.
Re: DBD::Sybase + MS SQL server problem
by hmerrill (Friar) on Aug 30, 2002 at 12:42 UTC
    1. Paste in the line where you assign your sql to $sql.

    2. I'm not familiar with a LOT of the sql in

    set TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; set quoted_identifier on use "smdb"; SELECT "_SMDBA_"."_TELMASTE_".SEQUENCE AS "Problem #", "_SMDBA_"."_CUSTOMER_".FNAME + ' ' + "_SMDBA_"."_CUSTOMER_".NAME AS "N +ame", "_SMDBA_"."_CUSTOMER_".EXT AS "Cl. Ext", "_SMDBA_"."_TELMASTE_"."DESCRIPTION" AS "Problem Description", "_SMDBA_"."_TELMASTE_"."DATE OPEN" AS "Opened", "_SMDBA_"."_PERSONNEL_".CODE AS "Assigned To" FROM "_SMDBA_"."_CUSTOMER_","_SMDBA_"."_TELMASTE_","_SMDBA_"."_PERSONN +EL_" WHERE "_SMDBA_"."_TELMASTE_"."SENT TO" = "_SMDBA_"."_PERSONNEL_".SEQUE +NCE AND "_SMDBA_"."_TELMASTE_".CLIENT = "_SMDBA_"."_CUSTOMER_".SEQUENCE AND "_SMDBA_"."_PERSONNEL_".CODE LIKE 'MUSER' AND "_SMDBA_"."_TELMASTE_".STATUS = 'O' ORDER BY "Problem #";
    ------------------------------------
    maybe if you paste in exactly what you are setting $sql to, that will help.

    3. I always find it easier for debugging and error handling to use RaiseError => 1 used with "eval" - read the perldocs on error handling and transaction processing by doing

    perldoc DBI

    at a command prompt and search(using the forward slash "/") for "Transactions" - you'll find a section titled "Transactions" that describes using "eval" for trapping errors with DBI statements.

    HTH.
      Thanks for your response.
      In the original code, assigning
      $sql
      was done by a subroutine:
      sub build_sql { #process args; return $sql_block; } $sql = build_sql ( \@layout, \%myfilter );
      However, in the interests of testing, I simpy did it like this:
      $sql = qq { set quoted_identifier on use "smdb"; set TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT "_SMDBA_"."_TELMASTE_".SEQUENCE AS "Problem #", "_SMDBA_"."_CUSTOMER_".FNAME + ' ' + "_SMDBA_"."_CUSTOMER_".NAME AS "N +ame", "_SMDBA_"."_CUSTOMER_".EXT AS "Cl. Ext", "_SMDBA_"."_TELMASTE_"."DESCRIPTION" AS "Problem Description", "_SMDBA_"."_TELMASTE_"."DATE OPEN" AS "Opened", "_SMDBA_"."_PERSONNEL_".CODE AS "Assigned To" FROM "_SMDBA_"."_CUSTOMER_","_SMDBA_"."_TELMASTE_","_SMDBA_"."_PERSONN +EL_" WHERE "_SMDBA_"."_TELMASTE_"."SENT TO" = "_SMDBA_"."_PERSONNEL_".SEQUE +NCE AND "_SMDBA_"."_TELMASTE_".CLIENT = "_SMDBA_"."_CUSTOMER_".SEQUENCE AND "_SMDBA_"."_PERSONNEL_".CODE LIKE 'MUSER' AND "_SMDBA_"."_TELMASTE_".STATUS = 'O' ORDER BY "Problem #"; };
      The SQL may look a little hacky, but I didn't design the DB :) It's basically to select various fields related to open helpdesk calls from several tables. (I have tried a simpler select from a table within that database, and that fails with exactly the same problem)

      Having looked at eval, and trying it, I unfortunately get almost exactly the same response: $trans_handle -> execute does not return any value. (This was at the same time as setting 'RaiseError' and 'PrintError' to 1).

      Whether that means that it is only executing one of the 'set' commands (which is possible, but even when I remove them I still don't get any errors) I don't know.

      I still think that 'selecting' the database, and enabling the quoted identifiers is what's failing, but I can't figure out how/why they aren't setting properly.

        The double quotes and the dots are throwing me - I'm familiar with

            SELECT <table_name>.<column_name> AS <column_name_alias>

        But I'm not familiar with a 3rd dot(.sequence). And, why are you using double quotes within your string? I don't think(?) you need those since your string is already surrounded by qq{};

        Can you put a

           print sql = <$sql>\n";

        after the $sql = qq{ ... };

        and paste that in so that I can see what the sql looks like that will be sent to the database?
Re: DBD::Sybase + MS SQL server problem
by screamingeagle (Curate) on Aug 30, 2002 at 14:46 UTC
    a) You're firing the first 3 statements without a "GO" in between. try this :
    set TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; GO set quoted_identifier on GO use smdb GO

    b) You dont need to use double quotes in the : use "master" line
    c) try running the sql without any semi-colons. i dont think u need to add any semi-colons when you're executing the sql from a perl script.

    And </code> and lastly, i would suggest testing the sql with just a simpler sql ,and if that works, go on adding to the sql stmt , one small step at a time, testing each time to see what addition is causing the sql to fail.... hth
      OK thanks, after some experimentation, it a) doesn't seem to like compound statements (ie multi line bit, so I have to run those separately). And b) it seems to fail if I use the semi-colon. Sigh. Figured it would be something simple :)