【问题标题】:How to intercept queries in a subclassed DBI?如何拦截子类 DBI 中的查询?
【发布时间】:2018-02-09 00:16:23
【问题描述】:

我需要拦截所有查询(例如,为了进行一些结构化日志记录)。 该应用程序已经使用子类 DBI,所以我希望我可以将我需要的代码注入到 MyDB::st 类中的 execute 重载中。

显然,这还不够。

在下面的示例中,MyDB::st::execute 仅在示例 2) 中执行。它不会在 1) 我调用 do 的地方执行,也不会在 3) 我调用 selectrow_array 的地方执行(即使文档说 selectrow_array 结合了 prepareexecutefetchrow_hashref)。

我是否也需要重载 MyDB::db 类中的语句?有很多(doselectrow_arrayselectrow_arrayrefselectrow_hashrefselectall_arrayrefselectall_arrayselectall_hashrefselectcol_arrayref 等)我希望我能避免这种情况。

为了简单起见,该示例使用 SQLite,但我检查的其他驱动程序的工作方式相同。

#!/usr/bin/perl

package MyDB;
use strict;
use warnings;
use base 'DBI';

sub connect {
    return DBI::connect( 'MyDB', 'dbi:SQLite:dbname=so.sqlite', '', '',);
}


package MyDB::db;
use strict;
use warnings;
use base 'DBI::db';

sub prepare {
    my $self = shift;
    warn "  DB: PREPARE\n";
    return $self->SUPER::prepare( @_ );
}


package MyDB::st;
use strict;
use warnings;
use base 'DBI::st';

sub execute {
    my $self = shift;
    warn "  ST: EXECUTE\n";
    return $self->SUPER::execute( @_ );
}

sub fetchrow_hashref {
    my $self = shift;
    warn "  ST: FETCHROW_HASHREF\n";
    return $self->SUPER::fetchrow_hashref( @_ );
}




package main;
use strict;
use warnings;

my $dbh = MyDB::connect();

warn "1) --- DO ---\n";
$dbh->do("CREATE TABLE IF NOT EXISTS ttt (a int)");

warn "2) --- PREPARE/EXECUTE/FETCHROW_HASHREF ---\n";
my $sth = $dbh->prepare( "SELECT * FROM ttt" );
$sth->execute();
$sth->fetchrow_hashref();

warn "3) --- SELECTROW_ARRAY ---\n";
$dbh->selectrow_array( "SELECT * FROM ttt" );

【问题讨论】:

  • 最好准确地说出您打算做什么,因为(例如)DBD::SQlite 已经具备记录语句的能力,因此没有理由拦截查询。 IIRC 类似于$dbh->sqlite_trace=1;
  • DBI 对包做了一些奇怪的事情。我更希望看到您需要挂钩的操作的简单包装器,而不是尝试覆盖继承的方法。
  • @ChrisTurner - 我需要以结构化方式转储一些数据(查询、执行时间、有关程序状态的一些信息)。

标签: perl dbi


【解决方案1】:

虽然文档根据其他方法描述了某些方法,但文档并未声称这些方法调用了其他方法。

每个驱动程序都通过使用 DBI 提供的模板来提供自己的许多 DBI 方法的实现(例如selectrow_array)。用C编写,模板中方法的实现经常直接调用驱动的函数,而不是通过DBI方法调用间接调用。

这使调用速度更快,但正如您所发现的,它使扩展 DBI 变得更加困难。

您可以找到您在DBI.pm 中列出的所有方法的 Perl 实现。复制这些。这些实现都是根据以下方法定义的,限制了您必须更改的内容:

  • prepare
  • execute
  • fetch
  • fetchrow_hashref
  • fetchrow_arrayref
  • bind_col

【讨论】:

    【解决方案2】:

    如果您只想记录查询,则无需拦截任何内容。尝试设置DBI_TRACE 环境变量。

    DBI_TRACE=1=dbitrace.log
    

    1 是日志级别,第二个参数是日志文件的路径。见https://metacpan.org/pod/DBI#DBI_TRACEhttps://metacpan.org/pod/DBI#TRACING

    【讨论】:

    • 我知道DBI_TRACE,但解析日志并不是一个真正的选择。我想转储一些结构化数据,例如查询字符串、执行时间、perl 调用堆栈以及有关程序状态的一些信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-21
    • 2017-02-15
    • 1970-01-01
    • 2011-07-12
    • 2021-10-05
    • 2016-05-19
    • 1970-01-01
    相关资源
    最近更新 更多