Как получить финальную строку запроса

Доброго времени суток!

Строю запрос по следующему образцу:

my $request = "select * from somewhere where something=? and sometimes=?";
my $sth = $dbh->prepare($statement);
$sth->bind_param(1,  $value, { pg_type => PG_INT4 });
$sth->bind_param(2,  $value_2);
$sth->execute;

Хочу знать, можно ли получить строку финального запроса, в том виде, как она будет выполнена(с подставленными значениями). Например, $value = 2 и $value_2 = 44. В итоге на консоли должна будет быть напечатана строка:

SELECT * FROM somewhere WHERE something=2 AND sometimes=44

?

my $xz = "select * from somewhere where something=? and sometimes=?";
my $value1 = "2";
my $value2 = "55";
$query =~ s/\?\s/$value1 /;
$query =~ s/\?+/$value2/;
print $query,"\n";

------------------
use Source();

Мое мнение...

Насколько я знаю, DBI такой информации не дает. В отладочном логе у него запрос - отдельно, подставляемые значения - отдельно.

О, это уже

О, это уже теплее:) А как можно посмотреть лог? И насколько сильно его включение отражается на производительности? Просто у меня есть задача, которая где-то раз в две недели выдает ошибку в sql. Сейчас вот ищу, какую.

Ссылка...

Про отладочный лог читайте http://search.cpan.org/~timb/DBI-1.601/DBI.pm#TRACING

Я не рекомендую включать лог для вылавливания ошибки, которая происходит раз в две недели. Жаль, что Вы не указали какая именно возникает ошибка, чтобы можно было дать конкретные рекомендации.
В своем коде любые ошибки SQL я обычно отлавливаю при помощи исключений и записываю в журнал исключений. DBI выдает достаточно подробное сообщение об ошибке с указанием использованных для подстановки значений.

Пример из моего проекта:

# в реальном коде такого нет, написал для пояснения с какими параметрами устанавливается соединение
$self->{_dbhw} = DBI->connect( $ENV{DBW_DSN}, $ENV{DBW_USER}, $ENV{DBW_PASS}, { AutoCommit => 1, PrintError => 0, RaiseError => 1, LongReadLen => 500000});
eval {
    $self->{_dbhw}->begin_work(); # начинаем транзакцию
    $query = q{
        INSERT INTO nntp_messages (group_id, thread_id, parent_id, nntp_id, title, author_name, author_email, body,
            coming_date, original_message, message_number, publishing_date, x_ftn_message_id)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, now(), ?, ?, from_unixtime(?), ?)
    };
    my $max_number = $message->get_number();
    my $sth = $self->{_dbhw}->prepare($query);
    $sth->bind_param( 1, $group_id, SQL_INTEGER );
    $sth->bind_param( 2, $thread_id, SQL_INTEGER );
    $sth->bind_param( 3, $parent_id, SQL_INTEGER );
    $sth->bind_param( 4, $message->get_nntp_id(), SQL_VARCHAR );
    $sth->bind_param( 5, $message->get_title(), SQL_VARCHAR );
    $sth->bind_param( 6, $message->get_author_name(), SQL_VARCHAR );
    $sth->bind_param( 7, $message->get_author_email(), SQL_VARCHAR );
    $sth->bind_param( 8, $message->get_body(), SQL_BLOB );
    $sth->bind_param( 9, $message->get_original_message(), SQL_BLOB );
    $sth->bind_param( 10, $max_number, SQL_INTEGER );
    $sth->bind_param( 11, $message->get_date_unixtime(), SQL_INTEGER );
    $sth->bind_param( 12, $message->get_x_ftn_message_id(), SQL_VARCHAR );
    if ( $sth->execute() eq q{0E0} ) {
        Exception::DB::InsertFailed->throw(
            error   => sprintf( q{cannot add message number %s from %s}, $message->get_number(), $group->get_name() ),
        );
    }
    $self->{_dbhw}->commit();
};
if ( my $exception = Exception::Class->caught() ) {
    $self->{_dbhw}->rollback();
    if ( ref $exception ) {
        $exception->rethrow();
    }
    else {
        Exception::DB->throw(
            error   => sprintf( q{%s. Group: %s, message number: %d}, $@, $message->get_group()->get_name(), $message->get_number() ),
        );
    }
}

К сожалению,

К сожалению, это все, что у меня есть

DBD::Pg::db do failed: ERROR: syntax error at or near "," at character 100

Очень странно...

Мне показались странными два момента:
1. синтаксическая ошибка возникает на этапе разбора запроса и построения плана выполнения, подстановка значений - это уже следующий этап.
2. "do failed" по идее говорит о том, что $dbh->do() завершился с ошибкой, но ведь do() не умеет работать с placeholder'ами.

Вероятно,

Вероятно, какое-то из значений представляет собой пустую строку. Мне получение финального sql запроса нужно как раз для этих целей - узнать, какие данные отсутствуют. У меня проблема в том, что одни и те же данные обновляются из нескольких мест, причем есть еще стороннее приложение. Может, оно что-то затирает/обновляет... Сейчас сложно сказать, не зная, что именно:)

Думаю, что надо

Думаю, что надо включить "самописный" лог, и вносить туда что на первый взгляд вызывает ошибку. Я всегда так делаю, когда обнаруживается, что система работает нестабильно и вываливаются какие-то ошибки...
Минус один - влияет на скорость, но что скорость в течении некоторого времени по сравнению с дальнейшей стабильностью :)

________________

#!/usr/bin/perl
print " :) "

пустые значения

А если синтаксис в порядке, почему бы не сделать проверку параметров, допустим как минимум defined? или это не прелставляется возможным?

запросы к БД

может кому будет интересно и полезно:

Мониторинг запросов MySQL при помощи bash-скрипта

http://boombick.org/blog/posts/15

Мысли про мониторинг SQL-запросов к mysql...

Уважаемый Kavkaz!

Предложенный вариант кроме как "извращением", ничем другим назвать не могу :о)
В MySQL есть нормальный журнал запросов, который можно включить при запуске сервера (http://dev.mysql.com/doc/refman/5.0/en/query-log.html).

Сейчас нашел

Сейчас нашел такой вариант:

#!/usr/bin/perl
use DBI;
use Data::Dumper;
my $dbh = DBI->connect(
    'dbi:Oracle:cd_sid',
    'cdaily',
    'pa55wd');
my $sql = "select 'something' from dual where 1 = ? and 2 = ?";
my $sth = $dbh->prepare($sql);
$sth->execute(1, 2);
print Dumper $sth->{ParamValues};

На консоль пойдет что-то вроде этого:

$VAR1 = {
          ':p1' => 1,
          ':p2' => 2
        };

маразм :)

ginnie написал
Предложенный вариант кроме как "извращением", ничем другим назвать не могу :о)

буду иметь ввиду, но мысль того парня тоже имеет право на существование :) а может и правда - нет ))) я хз, просто набрел на статью :)

Напоминание...

Уважаемый rimas!

В предложенном варианте надо учитывать тот факт, что print() при ошибке отработает только если RaiseError == 0.

А причем тут

А причем тут RaiseError? Печать выполняется вручную и ею управляет сам разработчик. Словили ошибку - печатаем в лог параметры. Во всяком случае, я сейчас поступил именно так. Жду данных для анализа;)))

В оправдание...

Уважаемый rimas!

Я слишком буквально воспринял Ваше сообщение, наивно посчитал, что Вы в программе код используете также, как в приведенном примере :о)

предложение

Можно использовать DBIx::Class. Мне он больше всего нравится. Там же есть DBIx::Class::QueryLog, который все показывает=)

вывод в консоль

Тут ковырялся на сервере и нашел такую утилитку:
в общем мускул по умолчанию хранит аккуратненько двоичные логи

вот пример записи:

#080305 18:54:01 server id 1  log_pos 10323236 	Query	thread_id=160040	exec_time=2	error_code=0
SET TIMESTAMP=1204732441;
update article set finished=NOW() where created='2008-03-05 18:32:56' and folder='postpone1';

для просмотра заходите в директорию с вашей бд ( например /home/mysql )
там буду лежать файлы типа:

-rw-rw----   1 mysql  mysql  973886454 Feb 13 14:12 office-bin.023

просмотреть их можно командой

[root@office /home/mysql]# mysqlbinlog office-bin.025

по-моему очень интересная вещь, очень даже может помочь

да, штука

да, штука классная, буду знать.
А что делать, если мы используем PostgreSQL?
Я уже написал небольшой вариан, не самый красивый, но кое-что он показывает. Закину завтра на критику:)

my $statement = 'insert

    my $statement = 'insert into some_table(col1, col2) values(?,?)';
    #warn "Statement: [$statement]\n";
    my $values;
    my $dbh = $self->dbh;
    eval {
        my $sth = $dbh->prepare($statement);
        $sth->bind_param(1,  123);
        $sth->bind_param(2,  456);
        $values = $sth->{ParamValues};
        $sth->execute;
    };
    if (my $error = $@) {
        throw Errors::SQL($error." Parameters: ".( join ',', map { $_."=".$values->{$_} } sort {$a <=> $b} (keys(%$values)) ));
    }

может позно и не в тему, но...

я делаю чуток проще и полезней (как мне кажется)
1. по сколько надо вставлять "правильные" данные в запрос. используем $dbh->quote($_);

2. в таком случае запрос имеет вид:
$sth=$dbh->prepare("select * from somewhere where something=".$dbh->quote($_)." and sometimes=".$dbh->quote($_));

3. а для того что б узнать что оно там выдаст на сервер:
$print=("select * from somewhere where something=".$dbh->quote($_)." and sometimes=".$dbh->quote($_));

В перле все

В перле все намного лучше чем в том-же php, потому что есть конструкции вида:

$rv = $dbh->do($statement, \%attr, @bind_values);
$rv = $sth->execute(@bind_values);
$sth->bind_param(1, 123);

И эти конструкции избавляют от необходимости заморачиваться с квотацией данных для sql запросов (данные передаются в SQL как двоичные).
Кстати, еще один плюс такого подхода в том что это немного быстрей чем когда парсер пытается парсить не только сам запрос но и его данные.