Perl DBI Postgresql: возврат undef для имеющихся данных

Я получаю очень странные результаты, и я знаю, что это должно быть что-то маленькое, что я делаю неправильно. Я пытаюсь проверить и посмотреть, существует ли строка в таблице базы данных postgresql, и в первом цикле я получаю фактическое значение. На второй итерации цикла и всех итерациях после того, как я получаю undef. Почему? Есть ли что-то, что я должен сделать, чего я не делаю. Я не использую подготовку, поэтому мне не нужно вызывать финиш и т. д.

Любое понимание очень поможет мне отладить эту проблему.

Извините, код такой неприятный на данный момент. Я был на охоте за отладкой и сделал все довольно уродливо.

Также извините за уродливый пример вывода. Я не знаю, как правильно отформатировать его с помощью stackoverflow.

Пожалуйста, не используйте распечатку «Выберите имя» в образце вывода. Все итерации после первой возвращают undef. Рассматриваемый вызов sql находится ближе к концу файла. Это линия

my $selectSQL = "select name from crawler_url where url='http://www.maccosmetics.com$item->{'uri'}' ";

Перл-код:

#!/usr/bin/perl

use LWP::Simple;                # From CPAN
use JSON qw( decode_json );     # From CPAN
use JSON::Parse 'parse_json';
use Data::Dumper;               # Perl core module
use HTML::TreeBuilder 5 -weak;
use Mojo::DOM;
use DBI;
use String::Util qw(trim);
use strict;                     # Good practice
use warnings;                   # Good practice

my $initialize = 0;
my $debug = 1;

&main;

sub main {
    my $dbh = connect2db();

    unless(defined($dbh)) {
        exit 1;
    }


    my $trendsurl;

    my $sth = $dbh->prepare("SELECT company_name from companies where active=1");
    $sth->execute;
    while( my $company = $sth->fetchrow_hashref() ) {
        #print Dumper($company)."\n";

        my $sth2 = $dbh->prepare("SELECT url from crawlers where company_name='$$company{'company_name'}' ");
        $sth2->execute;
        while( my $url = $sth2->fetchrow_hashref() ) {
            #print " NOW ON URL $$url{'url'} ##########\n";
            $trendsurl = $$url{'url'};
            chomp($trendsurl);
            $trendsurl = trim($trendsurl);
            print "URL: ".$trendsurl."\n";

            my $json = get( $trendsurl );
            die "Could not get $trendsurl!" unless defined $json;

            my $parsed_json = parse_json($json);
            my $items = $parsed_json->{'sections'}[0]->{'items'};

            foreach my $item_hash (@$items) {
                #print Dumper($item_hash)."\n";
                my $category = $item_hash->{'name'};
                print "Lip Product Category: $category\n";

                foreach my $item ( @{ $item_hash->{'items'} } ) {
                    print Dumper($item)."\n";

                    my $selectSQL = "select name from crawler_url where url='http://www.maccosmetics.com$item->{'uri'}' ";

                    print $selectSQL."\n" if($debug);

                    my ($productCount) = $dbh->selectrow_array($selectSQL);

                    my $date = localtime;
                    chomp($productCount);
                    trim($productCount);
                    chomp($item->{'name'});
                    trim($item->{'name'});

                    print "Select Name: '$productCount'\n";
                    print "Item Name: '$item->{'name'}'\n";
                    print "Do they equal: ", index($productCount, $item->{'name'}), " \n";

                    print Dumper($productCount);

                    if( index($productCount, $item->{'name'}) == -1 ) {
                        my $insertSQL = "insert into crawler_url (first_seen,url,name,category,last_checked) values ('$date','http://www.maccosmetics.com$item->{'uri'}','$item->{'name'}','$category','$date') ";
                        print $insertSQL."\n" if($debug);
                        my $retVal = $dbh->do($insertSQL);

                        $insertSQL = "insert into urls (company_name,url) values ('$$company{'company_name'}','http://www.maccosmetics.com$item->{'uri'}') ";
                        print $insertSQL."\n" if($debug);
                        $retVal = $dbh->do($insertSQL);
                    }
                    else {
                        #We have seen this before
                        my $updateSQL = "update crawler_url SET (url,name,category,last_checked) = ('http://www.maccosmetics.com$item->{'uri'}','$item->{'name'}','$category','$date' )";
                        print $updateSQL."\n" if($debug);
                        my $retVal = $dbh->do($updateSQL);
                    }
                }
            }
        }
    }
}

sub connect2db {
    return DBI->connect("dbi:Pg:dbname=xxxxxx", "xxxxx", "XXXXXX");
}

Пример вывода:

URL: http://www.maccosmetics.com/includes/panel_nav/catalog.js?CATEGORY_ID=CAT163&LOCALE=en_US

Lip Product Category: Lipstick

$VAR1 = {
  'uri' => '/product/shaded/168/310/Products/Lips/Lipstick/Lipstick/index.tmpl',
  'description' => 'Colour plus texture for the lips. Stands out on the runway...',
  'name' => 'Lipstick',
  'thumbnail' => '/images/products/56x56/M300.jpg',
  'header' => '/images/pnav/product/headers/pnav_M300_200x12_off.gif',
  'id' => 'CAT168PROD310'
};

select name from crawler_url where url='http://www.maccosmetics.com/product/shaded/168/310/Products/Lips/Lipstick/Lipstick/index.tmpl'

Select Name: 'Lipstick                                                                                                                        '

Item Name: 'Lipstick'


Do they equal: -1

$VAR1 = 'Lipstick                                                                                                                        ';
update crawler_url SET (url,name,category,last_checked) = ('http://www.maccosmetics.com/product/shaded/168/310/Products/Lips/Lipstick/Lipstick/index.tmpl','Lipstick','Lipstick','Wed Jan 28 21:15:40 2015' )

$VAR1 = {
      'id' => 'CAT168PROD34492',
      'thumbnail' => '/images/products/56x56/MX5G8N.jpg',
      'header' => '/images/pnav/product/headers/pnav_MX5G8N_200x12_off.gif',
      'description' => "Miley Cyrus\x{2019}s shade of VIVA GLAM Lipstick. Her super-sexy hot...",
      'name' => 'VIVA GLAM Miley Cyrus Lipstick',
      'uri' => '/product/shaded/168/34492/Products/Lips/Lipstick/VIVA-GLAM-Miley-Cyrus-Lipstick/index.tmpl'
    };

select name from crawler_url where url='http://www.maccosmetics.com/product/shaded/168/34492/Products/Lips/Lipstick/VIVA-GLAM-Miley-Cyrus-Lipstick/index.tmpl'

Select Name: ''

Item Name: 'VIVA GLAM Miley Cyrus Lipstick'


Do they equal: 0

$VAR1 = undef;

insert into crawler_url (first_seen,url,name,category,last_checked) values ('Wed Jan 28 21:15:40 2015','http://www.maccosmetics.com/product/shaded/168/34492/Products/Lips/Lipstick/VIVA-GLAM-Miley-Cyrus-Lipstick/index.tmpl','VIVA GLAM Miley Cyrus Lipstick','Lipstick','Wed Jan 28 21:15:40 2015')

insert into urls (company_name,url) values ('MAC                                                             ','http://www.maccosmetics.com/product/shaded/168/34492/Products/Lips/Lipstick/VIVA-GLAM-Miley-Cyrus-Lipstick/index.tmpl')

$VAR1 = {
      'uri' => '/product/shaded/168/34798/Products/Lips/Lipstick/Isabel-and-Ruben-Toledo-Lipstick/index.tmpl',
      'description' => 'Formulated to shade, define and showcase the lips in a rouge-y...',
      'name' => 'Isabel and Ruben Toledo Lipstick ',
      'header' => '/images/pnav/product/headers/pnav_MWWE1T_200x12_off.gif',
      'thumbnail' => '/images/products/56x56/MWWE1T.jpg',
      'id' => 'CAT168PROD34798'
    };

select name from crawler_url where url='http://www.maccosmetics.com/product/shaded/168/34798/Products/Lips/Lipstick/Isabel-and-Ruben-Toledo-Lipstick/index.tmpl'

Select Name: ''


Item Name: 'Isabel and Ruben Toledo Lipstick '

Do they equal: 0

$VAR1 = undef;

insert into crawler_url (first_seen,url,name,category,last_checked) values ('Wed Jan 28 21:15:40 2015','http://www.maccosmetics.com/product/shaded/168/34798/Products/Lips/Lipstick/Isabel-and-Ruben-Toledo-Lipstick/index.tmpl','Isabel and Ruben Toledo Lipstick ','Lipstick','Wed Jan 28 21:15:40 2015')

insert into urls (company_name,url) values ('MAC                                                             ','http://www.maccosmetics.com/product/shaded/168/34798/Products/Lips/Lipstick/Isabel-and-Ruben-Toledo-Lipstick/index.tmpl')

Обновление: когда я помещаю next перед вызовом $dbh->do, я получаю ожидаемые результаты. Так что это как-то связано с выполнением $dbh->do($insertSQL) или $dbh->do($updateSQL). Должен ли я сделать еще один вызов после этого, прежде чем снова использовать $dbh->selectrow_array($selectSQL) во 2-м взаимодействии? Если да, то почему?


person Nick.D    schedule 29.01.2015    source источник
comment
Да. Иметь 2 заявления не будет проблемой. Я использовал его в других небольших программах, и у меня никогда не было проблем. Я думаю, что это как-то связано с транзакциями и, в частности, со вставками или обновлениями. По умолчанию, согласно документации, флаг AutoCommit включен, поэтому мне не нужно вызывать фиксацию после функции do. Может быть, здесь что-то еще, что мне не хватает в отношениях DBI, DBD::Pg и posgresql.   -  person Nick.D    schedule 29.01.2015
comment
Я не уверен, что произойдет, когда вы обновите или вставите активный курсор в ту же таблицу. Также вы действительно-действительно должны использовать параметры привязки или вы затеваете атаку SQL-инъекцией.   -  person Schwern    schedule 29.01.2015
comment
Я думаю, что обновление и вставка в порядке, основываясь на моем прошлом использовании DBI. Спасибо за подсказку по параметрам привязки. Я буду изменять все свои вызовы sql, чтобы использовать это с этого момента. Вики-страница по SQL-инъекциям была на удивление довольно подробной: en.m.wikipedia.org/wiki/SQL_injection   -  person Nick.D    schedule 29.01.2015
comment
Чтобы лучше получить ответ на этот вопрос, вам нужно либо предоставить дамп схемы (с данными), который воспроизводит проблему, либо начать удаление кода, чтобы получить более минимальный пример.   -  person Schwern    schedule 29.01.2015


Ответы (1)


Вы действительно должны добавить $sth2->finish(); в конце вашего внутреннего цикла while и $sth->finish(); после вашего внешнего цикла while. Невыполнение завершения вашего внутреннего цикла может привести к тому, что первая итерация будет работать, но не все последующие итерации, как вы описываете в своей проблеме.

Мягко говоря, плохо не выполнять завершение чего-либо, хотя обычно это может сойти с рук, если у вас нет вложенных выборок. Если у вас есть вложенные выборки без соответствующих завершений, вы столкнетесь с той проблемой, которую описываете.

person onethreefour    schedule 29.01.2015
comment
Дескрипторы операторов автоматически завершаются, когда они уничтожаются, обычно потому, что переменная выходит за пределы области видимости или ей переназначается. $sth2 завершится в конце каждой итерации цикла. $sth закончится в конце main. - person Schwern; 29.01.2015
comment
@Schwern, как это влияет на операторы do? Согласно документации DBI и DBD::Pg, функция do выполняет только подготовку и выполнение, а не завершение. Подразумевается ли окончание do? search.cpan.org/dist/DBD-Pg/Pg.pm# сделать - person Nick.D; 29.01.2015
comment
@Nick.D У do нет курсора для завершения, он просто выполняет оператор и сообщает вам, сколько строк было выполнено. selectall_* завершится за вас (либо явно, либо позволив внутреннему дескриптору оператора выйти за рамки). По сути, завершать вызов только тогда, когда вы а) знаете, что не получите все данные, и б) вы знайте, что дескриптор не будет уничтожен в ближайшее время. Использование prepare_cached немного усложняет задачу, но оно предупредит вас, если вы повторно используете активный дескриптор. - person Schwern; 29.01.2015