I've moved to a new database project using DBI and DBD::ODBC. I'm in the design phase now, and I've run up against an odd requirement. I'm well aware of the value of abstracting SQL from the Perl that calls it -- but I now have the requirement not to use Stored Procedures. I'm not allowed to change this requirement.
So, what I can't figure out is: what's the best way, barring stored procedure use, to abstract the SQL as much as possible away from my perl code?
Update 2004-09.Sep-30:
I have solved the problem using ideas given by repliers combined with some of my own. I thought the Monastery might be interested.The end result is that the SQL files get drafted into a module, which can be included in the solid .exe that PAR generates. The SQL maintainer updates the .sql files and runs a batch file which calls the above script, then packs with PAR. No perl is ever apparent to the SQL maintainer.The methodology is simple:
- Put SQL into separate files, named with query_name.sql.
- Run this:
use strict; use warnings; my %queries; for (@ARGV) { open my $FH, '<', $_ or die("failed opening $_ :$!"); s'\.sql$''; while (chomp($queries{$_}.=<$FH>)) {} } # the PERLINST env var holds the location of our private # Perl Module store open my $MODULE, '>', "$ENV{PERLINST}/MyProject/SQL.pm" or die("Unable to open SQL module"); print $MODULE q( package Private::MyProject::SQL; require Exporter; use vars (@ISA, @EXPORT); ); print $MODULE '@EXPORT = qw(',join(' ',keys %queries),");\n"; for (keys %queries) { print $MODULE 'use constant '.$_.' => q('.$queries{$_}.");\n"; } print $MODULE '1;\n'; close $MODULE;- Pack code using PAR
Things to consider:
I'm currently thinking of putting it in our in-house Private:: module namespace with something like Private::ProjectName::SQL containing something like:
package Private::ProjectName::SQL; require Exporter; use warnings; use strict; use vars qw($VERSION @ISA @EXPORT); $VERSION = 0.01; @ISA = qw(Exporter); @EXPORT = qw(sql_queryname1 sql_queryname2 ...); use constant sql_queryname1 = qq( SELECT RecordID FROM Management WHERE IQ > 40 AND Salary < (SELECT UngodlyAmount FROM References) ); #don't trim this line! use constant sql_queryname2 =.... 1;
Will something like the above work? Is there a better way? Is that enough abstraction?
Thank you, wise monks, for bestowing wisdom upon me.
All code, unless otherwise noted, is untested
"All it will give you though, are headaches after headaches as it misinterprets your instructions in the most innovative yet useless ways." - Maypole and I - Tales from the Frontier of a Relationship (by Corion)
In reply to Abstracting SQL without Stored Procedures by radiantmatrix
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |