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

Reply via email to