On Wed 13 Feb 2002 20:36, Jonathan Leffler <[EMAIL PROTECTED]> wrote:
> Hi - another question:
>
> The sample code for DBD::Driver::db::prepare starts:
>
> sub prepare
> {
> my ($dbh, $statement, @attribs) = @_;
>
> # create a 'blank' sth
> my $sth = DBI::_new_sth($dbh, {
> 'Statement' => $statement,
> });
I do the same in dbd_st_prepare, see code snippets below. And after a short
search, I seem to do so *also* in Unify.pm:
sub prepare
{
my ($dbh, $statement, @attribs) = @_;
# Strip comments
$statement = join "" => map {
my $s = $_;
$s =~ m/^'.*'$/ or $s =~ s/(--.*)$//m;
$s;
} split m/('[^']*')/ => $statement;
# create a 'blank' sth
my $sth = DBI::_new_sth ($dbh, {
Statement => $statement,
});
# Setup module specific data
# $sth->STORE ("driver_params" => []);
# $sth->STORE ("NUM_OF_PARAMS" => ($statement =~ tr/?//));
DBD::Unify::st::_prepare ($sth, $statement, @attribs)
or return undef;
$sth;
} # prepare
Bit of code duplication?
> Question: does DBI keep track of the statement text, or do the DBD
> modules all have to do it?
I /think/ newer DBI's (1.18 and on) do so, but is you DBD /require/ing 1.18,
or do you still want to work with 0.93 and up?
> If DBI tracks it automatically, does that make the above code wrong,
> or just superfluous?
I think the last, but at least that would be backward compatible.
> If DBI does not track it, that presumably means that the DBD FETCH
> functions have to handle it, whereas if DBI does track it, then they
> don't. Is the statement text a read-only attribute of the statement
> handle? If not, why not, and what are the semantics of:
>
> $sth->{Statement} = "some new SQL";
And where can it be accessed from within the DBD-XS guts (if needed)
> Consider the cases where the original statement is a SELECT and the
> statement handle has been executed and maybe some data has been fetched,
> as well as before the statement is executed and after the statement is
> finished.
>
> If DBI tracks it and the DBD also tracks it, then which one 'wins'? The
DBD, I guess
> user loses, of course, because more work is being done than necessary.
> But does DBI use its own version of the statement text or does it end up
> using the driver's version?
Does the DBD issue the error or does it propagates to DBI. In the first case,
the DBD's version of the truth is used, in the latter DBI's.
> I ask because DBD::Informix currently tracks Statement for itself, more
> or less as implied by the code above. This has consequences for the
> FETCH code (which does recognize it) and the STORE code (which declines
> to do anything with it.
Here's snippets of the code I use to keep track of the handle's children. Feel
free to add comments (except for the indenting style)
/* Until those babys are able to change their own dirty nappies ... */
static void change_offspring (SV *dbh, imp_dbh_t *imp_dbh)
{
imp_sth_t **children;
int i, n;
/* Make this function extremely precautious ;-P */
unless (imp_dbh) return;
unless (children = imp_dbh->children) return;
unless ((n = imp_dbh->nchildren) > 0) return;
for (i = 0; i < n; i++) {
imp_sth_t *imp_sth = children[i];
dbg (2, "-- %03d/%03d 0x%08X %02X\n",i+1,n,imp_sth,imp_sth?imp_sth->stat:0);
if (!imp_sth || imp_sth->stat & ST_STAT_OPEN) continue;
dbd_st_destroy (dbh, imp_sth);
}
} /* change_offspring */
static void dbd_st_diaper (imp_dbh_t *imp_dbh, imp_sth_t *imp_sth)
{
imp_sth_t **children = imp_dbh->children;
int i, n = imp_dbh->nchildren;
for (i = 0; i < n; i++) {
if (children[i]) continue;
children[i] = imp_sth;
return;
}
if (n) imp_dbh->children = (imp_sth_t **)realloc ((void *)imp_dbh->children,
(imp_dbh->nchildren + 1) * sizeof (imp_sth_t *));
else imp_dbh->children = (imp_sth_t **) malloc (sizeof (imp_sth_t *));
if (imp_dbh->children) imp_dbh->children[imp_dbh->nchildren++] = imp_sth;
else imp_dbh->nchildren = 0;
} /* dbd_st_diaper */
static void dbd_st_growup (imp_dbh_t *imp_dbh, imp_sth_t *imp_sth)
{
imp_sth_t **children = imp_dbh->children;
int i, n = imp_dbh->nchildren;
for (i = 0; i <= n; i++) {
unless (children[i] == imp_sth) continue;
imp_dbh->children[i] = 0;
return;
}
} /* dbd_st_growup */
int dbd_db_commit (SV *dbh, imp_dbh_t *imp_dbh)
{
dbg (2, "DBD::Unify::db_commit\n");
unless (DBIc_ACTIVE (imp_dbh))
return (0);
change_offspring (dbh, imp_dbh);
/* Check for commit () being called whilst refs to cursors
* still exists. This needs some more thought.
*/
if (DBIc_ACTIVE_KIDS (imp_dbh) && DBIc_WARN (imp_dbh) && !dirty) {
warn ("DBD::Unify::db_commit (%s) invalidates %d active cursor(s)",
SvPV (dbh, na), (int)DBIc_ACTIVE_KIDS (imp_dbh));
}
EXEC SQL
COMMIT WORK;
return (sqlError (dbh));
} /* dbd_db_commit */
int dbd_db_rollback (SV *dbh, imp_dbh_t *imp_dbh)
{
dbg (2, "DBD::Unify::db_rollback\n");
unless (DBIc_ACTIVE (imp_dbh))
return (0);
change_offspring (dbh, imp_dbh);
/* Check for rollback () being called whilst refs to cursors
* still exists. See dbd_db_commit ()
*/
if (DBIc_ACTIVE_KIDS (imp_dbh) && DBIc_WARN (imp_dbh) && !dirty) {
warn ("DBD::Unify::db_rollback (%s) invalidates %d active cursor(s)",
SvPV (dbh, na), (int)DBIc_ACTIVE_KIDS (imp_dbh));
}
EXEC SQL
ROLLBACK WORK;
return (sqlError (dbh));
} /* dbd_db_rollback */
int dbd_db_disconnect (SV *dbh, imp_dbh_t *imp_dbh)
{
int transaction_active;
dbg (2, "DBD::Unify::db_disconnect\n");
unless (DBIc_ACTIVE (imp_dbh))
return (0);
change_offspring (dbh, imp_dbh);
if (imp_dbh->nchildren) {
if (imp_dbh->children) free ((void *)imp_dbh->children);
imp_dbh->children = (imp_sth_t **)0;
imp_dbh->nchildren = 0;
}
if (DBIc_ACTIVE_KIDS (imp_dbh) && DBIc_WARN (imp_dbh) && !dirty) {
warn ("DBD::Unify::db_disconnect (%s) invalidates %d active cursor(s)",
SvPV (dbh, na), (int)DBIc_ACTIVE_KIDS (imp_dbh));
}
DBIc_ACTIVE_off (imp_dbh);
EXEC SQL
DISCONNECT;
dbg (3, " After disconn, sqlcode = %d\n", SQLCODE);
imp_dbh->id = 0;
/* We assume that disconnect will always work
* since most errors imply already disconnected.
*/
return (sqlError (dbh));
} /* dbd_db_disconnect */
int dbd_st_prepare (SV *sth, imp_sth_t *imp_sth, char *statement, SV *attribs)
/* attribs unused */
{
SV *dbh = (SV *)DBIc_PARENT_H (imp_sth);
D_imp_dbh_from_sth;
unless (DBIc_ACTIVE (imp_dbh))
return (0);
if (strlen (statement) >= MAX_SQL_LEN) {
warn ("DBD::Unify::st_prepare (\"%.40s ...\") statement too long",
statement);
return (0);
}
unless (imp_sth->id = new_sth_id (dbh))
return (0);
unless (use_sth_id (dbh, imp_dbh->id, imp_sth->id))
return (0);
dbg (2, "DBD::Unify::st_prepare %s (\"%s\")\n", u_sql_nm, statement);
if (imp_sth->statement = (char *)malloc (strlen (statement) + 2))
(void)strcpy (imp_sth->statement, statement);
imp_sth->stat = 0;
imp_sth->fld = (imp_fld_t *)0;
imp_sth->prm = (imp_fld_t *)0;
dbd_st_diaper (imp_dbh, imp_sth);
:
:
} /* dbd_st_prepare */
void dbd_st_destroy (SV *sth, imp_sth_t *imp_sth)
{
SV *dbh = (SV *)DBIc_PARENT_H (imp_sth);
D_imp_dbh_from_sth;
dbg (2, "DBD::Unify::st_destroy '%s'\n", imp_sth->statement);
:
:
if (imp_sth->statement) {
(void)free (imp_sth->statement);
imp_sth->statement = (char *)0;
}
if (imp_sth->fld) {
(void)free (imp_sth->fld);
imp_sth->fld = (imp_fld_t *)0;
}
if (imp_sth->prm) {
(void)free (imp_sth->prm);
imp_sth->prm = (imp_fld_t *)0;
}
dbd_st_growup (imp_dbh, imp_sth);
if (DBIc_has (imp_sth, DBIcf_IMPSET))
DBIc_IMPSET_off (imp_sth);
dbg (2, "DBD::Unify::st 0x%08X 0x%04x 0x%04X 0x%08X 0x%08X 0x%08X\n",
imp_sth->com, imp_sth->id, imp_sth->stat, imp_sth->statement,
imp_sth->fld, imp_sth->prm);
dbg (2, "DBD::Unify::st destroyed\n");
} /* dbd_st_destroy */
--
H.Merijn Brand Amsterdam Perl Mongers (http://amsterdam.pm.org/)
using perl-5.6.1, 5.7.1 & 630 on HP-UX 10.20 & 11.00, AIX 4.2, AIX 4.3,
WinNT 4, Win2K pro & WinCE 2.11 often with Tk800.022 &/| DBD-Unify
ftp://ftp.funet.fi/pub/languages/perl/CPAN/authors/id/H/HM/HMBRAND/