Okay, considering that using the same name prepare() like this may
confuse some people, here is a refined solution that uses 3 methods
instead; please disregard any contrary statements that I previously
made:
# Opt 1: A user that wants the most control can do this (new feature):
my $sth1 = $dbh.compile( $sql_or_ast ); # always sans connection
$sth1.prepare(); # always with connection, even if DBD doesn't use it
$sth1.execute(); # always with connection
# Opt 2: If they want less control, they do this (same as old DBI):
my $sth2 = $dbh.prepare( $sql_or_ast ); # combines Opt 1's comp/prep
$sth2.execute(); # same as Opt 1
# Opt 3: Alternately, there is this (akin to my older suggestion):
my $sth3 = $dbh.compile( $sql_or_ast ); # same as Opt 1
$sth3.execute(); # combines Opt 1's prep/exec
# Opt 4: Even less control (akin to old DBI's "do"):
$dbh.execute( $sql_or_ast ); # combines Opt 1's comp/prep/exec
In this model, when you use just prepare() and execute(), they behave
identically to the old DBI, including that they require an open
connection. So no mystery there.
The new feature is if you decide to use compile(); you then give that
method the arguments you would have given to prepare(), and you
invoke prepare() on the result with no arguments; each DBD would
decide for itself how the work is divided between compile() and
prepare() with the limitation that compile() is not allowed to access
the database; ideally the DBD would place as much work there as is
possible, which would vary between Oracle/Pg/etc.
Invoking just compile() then execute() will cause the execute() to do
what prepare() normally does against a database, and cache the
prepared handle.
In option 4, I renamed the old DBI's do() to execute() for
consistency with the other examples; but this execute() is different
in that it caches the prepared statement handle. In any event, with
all 4 examples, execute() gives you the same result regardless of
what is called before it.
At 2:49 PM +1200 7/5/05, Sam Vilain wrote:
In particular, I don't think that the DB driver should automatically
get a chance to interfere with SQL::Statement; if they want to do that,
then they should specialise SQL::Statement. IMHO.
I am operating under the assumption here that while the new DBI is
designed to effectively support wrapper modules, the wrapper modules
would also be altered from their current DBI-1-geared designs to
accomodate DBI-2.
But still, what do you mean by "interfere"?
5. All details used to construct a connection handle should be
completely decomposed rather than shoved into an ungainly "data
source".
I interpret this as asking that the detailed parameters to the DBI
connection are expanded into named options rather than simply bundled into
a string.
That, I agree with, and I guess it would be useful occasionally to be
able to specify all that rather than just setting it up once and labelling
those connection parameters with a "source" that comes from ~/.dbi.
Particularly for writing gui dialogs for interactive database utilities.
I see the act of storing all the data as a single string at any time
to be a messy affair to be avoided. The application doesn't have to
know about the complexity to pass around a hash of values any more
than it does with a string; but when the application wants to know
the details, dealing with a hash is easier.
Either way, you don't want most applications dealing with this complexity
at all, really.
I am operating under the assumption that this system should work if
there are no external config files that the DBI/DBD would read, and
the application would provide that information; if its in a file, the
application would read it in, or would explicitly tell DBI where it
is. Or at least it should be possible for this to happen, even if a
DBD defaults to look in a default location when it doesn't get the
equivalent from the application.
6. DBI drivers should always be specified by users with their
actual package name, such as 'DBD::SQLite', and not some alternate
or abbreviated version that either leaves the 'DBD::' out or is
spelled differently. Similarly, the DBI driver loader should
simply try to load exactly the driver name it is given, without
munging of any type. This approach is a lot more simple, flexible
and lacks the cludges of the current DBI. DBI driver implementers
can also name their module anything they want, and don't have to
name it 'DBD::*'. A DBI driver should not have to conform to
anything except a specific API by which it is called, which
includes its behaviour upon initialization, invocation, and
destruction.
Is this useful?
I can't see a reason that the DBI.new() / DBI.connect() call shouldn't be
flexible in what it accepts;
$dbh = DBI.new( :driver<Rosetta> ); # means DBD::Rosetta
$dbh = DBI.new( :driver<Rosetta::Emulate::DBD> ); # specify full package
$dbh = DBI.new( :driver(Rosetta::Emulate::DBD) ); # pass type object
$dbh = DBI.new( :driver(DBD::SQLite.new(:foo<bar>)) ); # pass driver object
My main point here is that DBI should not have to know any details
about particular drivers that are written to it, except in a generic
sense that may apply to any driver. The driver should know about DBI
details, but the reverse should never be true.
Unless there is a design flaw in DBI, we should not have to update
that module just because a new driver came into existence whose name
has not yet been hard-coded into DBI.
See this block for example, from DBI.pm v1.48:
my $dbd_prefix_registry = {
ad_ => { class => 'DBD::AnyData', },
ado_ => { class => 'DBD::ADO', },
amzn_ => { class => 'DBD::Amazon', },
best_ => { class => 'DBD::BestWins', },
csv_ => { class => 'DBD::CSV', },
db2_ => { class => 'DBD::DB2', },
dbi_ => { class => 'DBI', },
dbm_ => { class => 'DBD::DBM', },
df_ => { class => 'DBD::DF', },
f_ => { class => 'DBD::File', },
file_ => { class => 'DBD::TextFile', },
ib_ => { class => 'DBD::InterBase', },
ing_ => { class => 'DBD::Ingres', },
ix_ => { class => 'DBD::Informix', },
jdbc_ => { class => 'DBD::JDBC', },
msql_ => { class => 'DBD::mSQL', },
mysql_ => { class => 'DBD::mysql', },
mx_ => { class => 'DBD::Multiplex', },
nullp_ => { class => 'DBD::NullP', },
odbc_ => { class => 'DBD::ODBC', },
ora_ => { class => 'DBD::Oracle', },
pg_ => { class => 'DBD::Pg', },
proxy_ => { class => 'DBD::Proxy', },
rdb_ => { class => 'DBD::RDB', },
sapdb_ => { class => 'DBD::SAP_DB', },
solid_ => { class => 'DBD::Solid', },
sponge_ => { class => 'DBD::Sponge', },
sql_ => { class => 'SQL::Statement', },
syb_ => { class => 'DBD::Sybase', },
tdat_ => { class => 'DBD::Teradata', },
tmpl_ => { class => 'DBD::Template', },
tmplss_ => { class => 'DBD::TemplateSS', },
tuber_ => { class => 'DBD::Tuber', },
uni_ => { class => 'DBD::Unify', },
xbase_ => { class => 'DBD::XBase', },
xl_ => { class => 'DBD::Excel', },
yaswi_ => { class => 'DBD::Yaswi', },
};
I mean, what's up with that? I assume DBI 1 has this for legacy app
backwards compatability, but DBI version 2 should never have to
accomodate such abhorrent computer programming practices in its core.
By having users specify the full driver class name, DBI won't have to
do any such explicit mapping.
By the way, most driver names are quite short already, so its not
like abbreviations are necessary.
-- Darren Duncan