Perl DBI Postgresql: Връща undef за данни, които са там

Получавам много странни резултати и знам, че трябва да е нещо дребно, което правя погрешно. Опитвам се да проверя и да видя дали съществува ред в таблица на база данни postgresql и при първия цикъл получавам действителна стойност. При втората итерация на цикъла и всички итерации там, след като получа undef. Защо? Има ли нещо, което трябва да направя, което не правя. Не използвам подготви, така че не трябва да се обаждам на завършване и т.н.

Всяко прозрение ще ми помогне много да отстраня този проблем.

Съжаляваме, че кодът е толкова неприятен за сега. Бях на лов за отстраняване на грешки и направих нещата доста грозни.

Също така съжалявам за грозната извадка. Не знам как да го форматирам добре със stackoverflow.

Моля, не разпечатката „Изберете име“ в примерния резултат. Всички итерации след първата връщат undef. Въпросното sql извикване е към края на файла. Това е линията

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

Perl код:

#!/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) отново при второто взаимодействие? Ако да, защо?


person Nick.D    schedule 29.01.2015    source източник
comment
да Наличието на 2 твърдения няма да е проблем. Използвал съм го в други малки програми и никога не съм имал проблеми. Мисля, че има нещо общо с транзакции и по-специално вмъквания или актуализации. По подразбиране според документацията флагът AutoCommit е активиран, така че не би трябвало да извиквам commit след функция 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. Неизвършването на завършване на вашия вътрешен цикъл може да доведе до работа на първата итерация, но не и на всички следващи итерации, точно както описвате в проблема си.

Най-малкото е лоша форма да не извършвате завършване на sth, въпреки че обикновено можете да се разминете, ако нямате вложени извличания. След като имате вложени извличания без съответни завършвания, се натъквате на точния проблем, който описвате.

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