File Coverage

blib/lib/Net/DirectConnect/pslib/pssql.pm
Criterion Covered Total %
statement 40 1426 2.8
branch 5 848 0.5
condition 5 937 0.5
subroutine 12 103 11.6
pod 0 8 0.0
total 62 3322 1.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             #$Id: pssql.pm 4848 2014-08-07 21:22:41Z pro $ $URL: svn://svn.setun.net/search/trunk/lib/pssql.pm $
3              
4             =copyright
5             PRO-search sql library
6             Copyright (C) 2003-2011 Oleg Alexeenkov http://pro.setun.net/search/ proler@gmail.com
7              
8             This program is free software: you can redistribute it and/or modify
9             it under the terms of the GNU General Public License as published by
10             the Free Software Foundation, either version 3 of the License, or
11             (at your option) any later version.
12              
13             This program is distributed in the hope that it will be useful,
14             but WITHOUT ANY WARRANTY; without even the implied warranty of
15             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16             GNU General Public License for more details.
17              
18             You should have received a copy of the GNU General Public License
19             along with this program. If not, see .
20             =cut
21              
22             =c
23             todo:
24              
25             pg
26             2009/10/06-13:53:11 dev HandleError DBD::Pg::db do failed: no connection to the server
27             DBI::db=HASH(0x1229568) 7 no connection to the server
28              
29              
30              
31             2009/06/02-19:37:35 dev HandleError DBD::Pg::st execute failed: FATAL: terminating connection due to administrator command
32             server closed the connection unexpectedly
33             This probably means the server terminated abnormally
34             before or while processing the request.
35             DBI::st=HASH(0x271b688) 7 FATAL: terminating connection due to administrator command
36             server closed the connection unexpectedly
37             This probably means the server terminated abnormally
38             before or while processing the request.
39              
40             2009/06/02-19:37:35 dev err_parse st0 ret1 wdi= di= fa= 1 er= 300 1000 fatal 57P01
41             2009/06/02-19:37:36 dev HandleError DBD::Pg::st execute failed: no connection to the server
42             DBI::st=HASH(0x271b718) 7 no connection to the server
43              
44             2009/06/02-19:37:39 dev HandleError DBD::Pg::db do failed: no connection to the server
45             DBI::db=HASH(0x1209d38) 7 no connection to the server
46              
47              
48              
49             $work
50              
51             =cut
52              
53             #our ( %config);
54             package #no cpan
55             pssql;
56 1     1   7 use strict;
  1         2  
  1         53  
57 1     1   5 use utf8;
  1         3  
  1         9  
58 1     1   27 no warnings qw(uninitialized);
  1         2  
  1         53  
59 1     1   6 no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  1         2  
  1         8  
60             our $VERSION = ( split( ' ', '$Revision: 4848 $' ) )[1];
61             #use locale;
62 1     1   7776 use DBI;
  1         63976  
  1         92  
63 1     1   15 use Time::HiRes qw(time);
  1         2  
  1         12  
64 1     1   160 use Data::Dumper; #dev only
  1         2  
  1         145  
65             $Data::Dumper::Sortkeys = $Data::Dumper::Useqq = $Data::Dumper::Indent = $Data::Dumper::Terse = 1;
66             our ( %work, ); #%stat %static, $param,
67             our (%config);
68             #local *config = *main::config;
69             #*pssql::config = *main::config;
70             #*pssql::work = *main::work;
71             #*pssql::stat = *main::stat;
72             *config = *main::config;
73             *work = *main::work;
74             *stat = *main::stat;
75 1     1   5 use lib::abs './';
  1         2  
  1         12  
76 1     1   601 use psmisc;
  1         3  
  1         40  
77             #use psconn;
78             #our ( %config, %work, %stat, %static, $param, );
79 1     1   6 use base 'psconn';
  1         2  
  1         3996  
80             our $AUTOLOAD;
81             #our $VERSION = ( split( ' ', '$Revision: 4848 $' ) )[1];
82             my ( $tq, $rq, $vq );
83             my ( $roworder, $tableorder, );
84             our ( %row, %default );
85             $config{ 'log_' . $_ } = 0 for grep { !exists $config{ 'log_' . $_ } } qw(trace dmpbef);
86             #warn "SQL UESEEDDD" ;
87             sub row {
88 4     4 0 10 my $row = shift @_;
89             return {
90 4 50 33     7 %{ ( defined $config{'row'} ? $config{'row'}{$row} : undef ) || $row{$row} || {} }, %{ $config{'row_all'} || {} },
  4 50       58  
  4 50       75  
91             'order' => --$roworder,
92             @_
93             };
94             }
95              
96             sub table {
97 0     0 0   my $table = shift @_;
98 0           return @_;
99             #{
100             #%{ ( defined $config{'row'} ? $config{'row'}{$row} : undef ) || $row{$row} || {} }, %{ $config{'row_all'} || {} },
101             #'order' => --$tableorder,
102             #@_
103             #};
104             }
105             #}
106             BEGIN {
107 1     1   20 %row = (
108             'time' => {
109             'type' => 'INT',
110             'unsigned' => 1,
111             'default' => 0,
112             'date_time' => 1, #todo
113             },
114             'uint' => { 'type' => 'INTEGER', 'unsigned' => 1, 'default' => 0, },
115             'uint16' => { 'type' => 'SMALLINT', 'unsigned' => 1, 'default' => 0, },
116             'uint64' => { 'type' => 'BIGINT', 'unsigned' => 1, 'default' => 0, },
117             'text' => { 'type' => 'VARCHAR', 'index' => 10, 'default' => '', },
118             'stem' => {
119             'type' => 'VARCHAR',
120             #! 'length' => 128,
121             'fulltext' => 'stemi',
122             'default' => '',
123             'not null' => 1,
124             'stem_index' => 1,
125             },
126             );
127 1   33     11 $row{'id'} ||= row( 'uint', 'auto_increment' => 1, 'primary' => 1 ),
      33        
128             $row{'added'} ||= row( 'time', 'default_insert' => int( time() ), 'no_insert_update' => 1, );
129 1   33     14 $row{'year'} ||= row('uint16');
130 1   33     10 $row{'size'} ||= row('uint64');
131             %default = (
132 0 0       0 'null' => { 'do' => sub { }, 'query' => sub { wantarray ? () : [] }, 'line' => sub { {} }, },
  0         0  
  0         0  
133             'sqlite' => {
134             #'dbi' => 'SQLite2',
135             'dbi' => 'SQLite',
136             'params' => [qw(dbname)],
137             'dbname' => $config{'root_path'} . 'sqlite.db',
138             'no_update_limit' => 1, #pg sux
139             'table quote' => '"',
140             'row quote' => '"',
141             'value quote' => "'",
142             'IF NOT EXISTS' => 'IF NOT EXISTS',
143             'index_IF NOT EXISTS' => 'IF NOT EXISTS',
144             'IF EXISTS' => 'IF EXISTS',
145             'REPLACE' => 'REPLACE',
146             'AUTO_INCREMENT' => 'AUTOINCREMENT',
147             'ANALYZE' => 'ANALYZE',
148             'err_ignore' => [qw( 1 )],
149             'error_type' => sub { #TODO!!!
150 0         0 my $self = shift;
151 0         0 my ( $err, $errstr ) = @_;
152             #$self->log('dev',"ERRDETECT($err, $errstr)");
153 0 0       0 return 'install' if $errstr =~ /no such table:|unable to open database file/i;
154 0 0 0     0 return 'syntax' if $errstr =~ /syntax|unrecognized token/i or $errstr =~ /misuse of aggregate/;
155 0 0       0 return 'retry' if $errstr =~ /database is locked/i;
156 0 0       0 return 'upgrade' if $errstr =~ /no such column/i;
157             #return 'connection' if $errstr =~ /connect/i;
158 0         0 return undef;
159             },
160 2         25564 'pragma' => {
161             map {
162             $_ => $_
163             } 'synchronous = OFF',
164             'auto_vacuum = FULL'
165             },
166             'on_connect' => sub {
167 0         0 my $self = shift;
168 0 0       0 $self->do("PRAGMA $_;") for keys %{ $self->{'pragma'} || {} };
  0         0  
169             #$self->log( 'sql', 'on_connect!' );
170             },
171             'no_dbirows' => 1,
172             },
173             'pg' => {
174             'dbi' => 'Pg',
175             'user' => ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?)|linux)/i ? 'postgres' : 'pgsql' ),
176             #'port' => 5432,
177             'IF EXISTS' => 'IF EXISTS',
178             'CREATE TABLE' => 'CREATE TABLE',
179             'OFFSET' => 'OFFSET',
180             'IF NOT EXISTS' => 'IF NOT EXISTS', #9.2 ok
181             #'unsigned' => 0,
182             'UNSIGNED' => '', #pg sux
183             'no_delete_limit' => 1, #pg sux
184             'table quote' => '"',
185             'row quote' => '"',
186             'value quote' => "'",
187             'index_name_table' => 1,
188             'REPLACE' => 'INSERT',
189             'EXPLAIN' => 'EXPLAIN ANALYZE',
190             'CASCADE' => 'CASCADE',
191             'SET NAMES' => 'SET client_encoding = ',
192             'fulltext_config' => 'pg_catalog.simple',
193             'params' => [
194             qw(host hostaddr port options dbname database db user username password service sslmode), qw(
195             )
196             ],
197             'err_ignore' => [qw( 1 7)],
198             'error_type' => sub {
199 0         0 my $self = shift, my ( $err, $errstr ) = @_;
200             #$self->log('dev',"ERRDETECT($err, [$errstr])");
201 0 0       0 return 'connection' if $errstr eq $err; # 7, [7] # wtf
202 0 0       0 return 'install_db' if $errstr =~ /FATAL:\s*database ".*?" does not exist/i;
203 0 0       0 return 'connection' if $errstr =~ /FATAL:\s*terminating connection/i; #7
204 0 0       0 return 'fatal' if $errstr =~ /fatal/i;
205 0 0       0 return 'syntax' if $errstr =~ /syntax/i;
206 0 0       0 return 'connection' if $errstr =~ /ERROR:\s*prepared statement ".*?" does not exist/i;
207 0 0 0     0 return 'connection' if $errstr =~ /connect|Unknown message type: ''/i and $errstr !~ /(?:column|relation) "/; #"mc
208 0 0       0 return 'install' if $errstr =~ /ERROR:\s*(?:relation \S+ does not exist)/i;
209             #return 'retry' if $errstr =~ /ERROR:\s*cannot drop the currently open database/i;
210 0 0       0 return 'retry' if $errstr =~ /ERROR: database ".*?" is being accessed by other users/i;
211 0 0       0 return 'ignore'
212             if $errstr =~
213             /(?:duplicate key violates unique constraint)|(?:duplicate key value violates unique constraint)|(?:ERROR:\s*(?:database ".*?" already exists)|(?:relation ".*?" already exists)|(?:invalid byte sequence for encoding)|(?:function .*? does not exist)|(?:null value in column .*? violates not-null constraint)|(?:Can't create database '.*?'; database exists))/i;
214 0         0 return undef;
215             },
216             'set' => { 'lc_messages' => 'C' },
217             'on_connect' => sub {
218 0         0 my $self = shift;
219 0         0 $self->{dbh}->{pg_utf8_strings} = $self->{dbh}->{pg_enable_utf8} = 1;
220 0         0 $self->set_names();
221 0 0 0     0 $self->do("select set_curcfg('default');") if $self->{'use_fulltext'} and $self->{'old_fulltext'};
222 0 0       0 $self->do("SET $_=$vq$self->{'set'}{$_}$vq;") for grep {!$self->{'no_set_'.$_}} sort keys %{ $self->{'set'} || {} };
  0         0  
  0         0  
223             },
224             'no_dbirows' => 1,
225             'cp1251' => 'win1251',
226             'fulltext_word_glue' => '&',
227             },
228             'sphinx' => {
229             'dbi' => 'mysql',
230             'user' => 'root',
231             'port' => 9306,
232             'params' => [qw(host port )], # perldoc DBD::mysql
233             'sphinx' => 1,
234             'value quote' => "'",
235             'no_dbirows' => 1,
236             'no_column_prepend_table' => 1,
237             'no_join' => 1,
238             'OPTION' => 'OPTION',
239             'option' => { 'max_query_time' => 20000, 'cutoff' => 1000, 'ranker' => 'sph04', },
240             },
241             'mysql5' => {
242             'dbi' => 'mysql',
243             'user' => 'root',
244             'use_drh' => 1,
245             'mysql_enable_utf8' => 1,
246             'varchar_max' => 65530,
247             'unique_max' => 1000,
248             'primary_max' => 999,
249             'fulltext_max' => 1000,
250             'key_length' => 1000, # maybe 3072 for mariadb
251             'err_connection' => [qw( 1 1040 1053 1129 1213 1226 2002 2003 2006 2013 )],
252             'err_fatal' => [qw( 1016 1046 1251 )], # 1045,
253             'err_syntax' => [qw( 1060 1064 1065 1067 1071 1096 1103 1118 1148 1191 1364 1366 1406 1439)], #1054 #maybe all 1045..1075
254             'err_repair' => [qw( 126 130 144 145 1034 1062 1194 1582 )],
255             'err_retry' => [qw( 1317 )],
256             'err_install' => [qw( 1146)], # 1017 repair?
257             'err_install_db' => [qw( 1049 )],
258             'err_upgrade' => [qw( 1054 )],
259             'err_ignore ' => [qw( 2 1264 1061 )],
260             'error_type' => sub {
261 0         0 my $self = shift, my ( $err, $errstr ) = @_;
262             #$self->log('dev',"MYERRDETECT($err, $errstr)");
263 0         0 for my $errtype (qw(connection retry syntax fatal repair install install_db upgrade)) {
264             #$self->log('dev',"ERRDETECTED($err, $errstr) = $errtype"),
265 0 0       0 return $errtype if grep { $err eq $_ } @{ $self->{ 'err_' . $errtype } };
  0         0  
  0         0  
266             }
267 0         0 return undef;
268             },
269             'table quote' => "`",
270             'row quote' => "`",
271             'value quote' => "'",
272             #'index quote' => "`",
273             #'unsigned' => 1,
274             'quote_slash' => 1,
275             'index in create table' => 1,
276             'utf-8' => 'utf8',
277             'koi8-r' => 'koi8r',
278             'table options' => 'ENGINE = MYISAM DELAY_KEY_WRITE=1',
279             'IF NOT EXISTS' => 'IF NOT EXISTS',
280             'IF EXISTS' => 'IF EXISTS',
281             'IGNORE' => 'IGNORE',
282             'REPLACE' => 'REPLACE',
283             'INSERT' => 'INSERT',
284             'HIGH_PRIORITY' => 'HIGH_PRIORITY',
285             'SET NAMES' => 'SET NAMES',
286             'DEFAULT CHARACTER SET' => 'DEFAULT CHARACTER SET',
287             'USE_FRM' => 'USE_FRM',
288             'EXTENDED' => 'EXTENDED',
289             'QUICK' => 'QUICK',
290             'ON DUPLICATE KEY UPDATE' => 'ON DUPLICATE KEY UPDATE',
291             'UNSIGNED' => 'UNSIGNED',
292             'UNLOCK TABLES' => 'UNLOCK TABLES',
293             'LOCK TABLES' => 'LOCK TABLES',
294             'OPTIMIZE' => 'OPTIMIZE TABLE',
295             'ANALYZE' => 'ANALYZE TABLE',
296             'CHECK' => 'CHECK TABLE',
297             'FLUSH' => 'FLUSH TABLE',
298             'LOW_PRIORITY' => 'LOW_PRIORITY',
299             'on_connect' => sub {
300 0         0 my $self = shift;
301 0         0 $self->{'db_id'} = $self->{'dbh'}->{'mysql_thread_id'};
302 0 0 0     0 $self->set_names() if !( $ENV{'MOD_PERL'} || $ENV{'FCGI_ROLE'} );
303             },
304             'on_user' => sub {
305 0         0 my $self = shift;
306 0 0 0     0 $self->set_names() if $ENV{'MOD_PERL'} || $ENV{'FCGI_ROLE'};
307             },
308             'params' => [
309             qw(host port database mysql_client_found_rows mysql_compression mysql_connect_timeout mysql_read_default_file mysql_read_default_group mysql_socket
310             mysql_ssl mysql_ssl_client_key mysql_ssl_client_cert mysql_ssl_ca_file mysql_ssl_ca_path mysql_ssl_cipher
311             mysql_local_infile mysql_embedded_options mysql_embedded_groups mysql_enable_utf8)
312             ], # perldoc DBD::mysql
313             'insert_by' => 1000, ( !$ENV{'SERVER_PORT'} ? ( 'auto_check' => 1 ) : () ), 'unique name' => 1, # test it
314             'match' => sub {
315 0         0 my $self = shift;
316 0         0 my ( $param, $param_num, $table, $search_str, $search_str_stem ) = @_;
317 0         0 my ( $ask, $glue );
318 0         0 local %_;
319 0 0 0     0 map { $_{ $self->{'table'}{$table}{$_}{'fulltext'} } = 1 }
  0         0  
320 0         0 grep { $self->{'table'}{$table}{$_}{'fulltext'} or ( $self->{'sphinx'} and $self->{'table'}{$table}{$_}{'sphinx'} ) }
321 0         0 keys %{ $self->{'table'}{$table} };
322 0         0 for my $index ( keys %_ ) {
323 0 0       0 if (
324 0         0 $_ = join( ' , ',
325 0         0 map { "$rq$_$rq" }
326 0         0 sort { $self->{'table'}{$table}{$b}{'order'} <=> $self->{'table'}{$table}{$a}{'order'} }
327 0         0 grep { $self->{'table'}{$table}{$_}{'fulltext'} eq $index } keys %{ $self->{'table'}{$table} } )
328             )
329             {
330 0 0       0 my $stem =
331 0         0 grep { $self->{'table'}{$table}{$_}{'fulltext'} eq $index and $self->{'table'}{$table}{$_}{'stem_index'} }
332 0         0 keys %{ $self->{'table'}{$table} };
333             #TODO: maybe some message for user ?
334 0 0 0     0 $self->{'accurate'} = 1, next,
      0        
      0        
335             if ($stem
336             and length $search_str_stem
337             and $self->{'auto_accurate_on_slow'}
338             and $search_str_stem =~ /\b\w{$self->{'auto_accurate_on_slow'}}\b/ );
339 0 0       0 my $double =
340 0         0 grep { $self->{'table'}{$table}{$_}{'fulltext'} and $self->{'table'}{$table}{$_}{'stem'} }
341 0         0 keys %{ $self->{'table'}{$table} };
342 0 0 0     0 next if $double and ( $self->{'accurate'} xor !$stem );
      0        
343 0         0 my $match;
344 0 0       0 if ( $self->{'sphinx'} ) { $match = ' MATCH (' . $self->squotes( $stem ? $search_str_stem : $search_str ) . ')' }
  0 0       0  
345             else {
346 0 0 0     0 $match = ' MATCH (' . $_ . ')' . ' AGAINST (' . $self->squotes( $stem ? $search_str_stem : $search_str ) . (
    0          
347             ( !$self->{'no_boolean'} and $param->{ 'adv_query' . $param_num } eq 'on' )
348             ? 'IN BOOLEAN MODE'
349             #: ( $self->{'allow_query_expansion'} ? 'WITH QUERY EXPANSION' : '' )
350             : $self->{'fulltext_extra'}
351             ) . ') ';
352             }
353 0         0 $ask .= " $glue " . $match;
354 0 0 0     0 $work{'what_relevance'}{$table} ||= $match . " AS $rq" . "relev$rq"
      0        
355             if $self->{'select_relevance'}
356             or $self->{'table_param'}{$table}{'select_relevance'};
357             }
358 0         0 $glue = $self->{'fulltext_glue'};
359             }
360 0         0 return $ask;
361             },
362             },
363 1 50       26 );
    50          
364             }
365              
366             sub new {
367 0     0 0   my $self = bless( {}, shift );
368 0           $self->init(@_);
369 0           $self->psconn::init(@_);
370 0           return $self;
371             }
372              
373             sub cmd {
374 0     0 0   my $self = shift;
375 0           my $cmd = shift;
376 0 0         $self->log( 'trace', "pssql::$cmd [$self->{'dbh'}]", @_ ) if $cmd ne 'log';
377 0 0         $self->{'handler_bef'}{$cmd}->( $self, \@_ ) if $self->{'handler_bef'}{$cmd};
378 0 0         my @ret =
    0          
    0          
    0          
    0          
    0          
    0          
    0          
379             ref( $self->{$cmd} ) eq 'CODE'
380             ? ( wantarray ? ( $self->{$cmd}->( $self, @_ ) ) : scalar $self->{$cmd}->( $self, @_ ) )
381             : (
382             exists $self->{$cmd}
383             ? ( ( defined( $_[0] ) ? ( $self->{$cmd} = $_[0] ) : ( $self->{$cmd} ) ) )
384             : ((!ref $self->{'dbh'}) ? ()
385             : $self->{'dbh'}->can($cmd) ? $self->{'dbh'}->$cmd(@_)
386             : exists $self->{'dbh'}{$cmd} ? ( ( defined( $_[0] ) ? ( $self->{'dbh'}->{$cmd} = $_[0] ) : ( $self->{'dbh'}->{$cmd} ) ) )
387             : undef )
388             );
389 0 0         $self->{'handler'}{$cmd}->( $self, \@_, \@ret ) if $self->{'handler'}{$cmd};
390 0 0         return wantarray ? @ret : $ret[0];
391             }
392              
393             sub AUTOLOAD {
394 0     0     my $self = shift;
395 0 0         my $type = ref($self) or return;
396 0           my $name = $AUTOLOAD;
397 0           $name =~ s/.*://; # strip fully-qualified portion
398             #$self->log('dev', 'autoload', $name, $AUTOLOAD, @_);
399 0           return $self->cmd( $name, @_ );
400             }
401              
402             sub _disconnect {
403 0     0     my $self = shift;
404 0           $self->log( 'trace', 'pssql::_diconnect', "dbh=$self->{'dbh'}" );
405 0 0         $self->flush_insert() unless $self->{'in_disconnect'};
406 0           $self->{'in_disconnect'} = 1;
407 0           return 0;
408             }
409              
410             sub _dropconnect {
411 0     0     my $self = shift;
412 0           $self->log( 'trace', 'pssql::_dropconnect' );
413 0           $self->{'in_disconnect'} = 1;
414 0 0         $self->{'sth'}->finish() if $self->{'sth'};
415 0 0 0       $self->{'dbh'}->disconnect(), $self->{'dbh'} = undef if $self->{'dbh'} and keys %{ $self->{'dbh'} };
  0            
416 0           delete $self->{'in_disconnect'};
417 0           return 0;
418             }
419              
420             sub _check {
421 0     0     my $self = shift;
422 0 0 0       return 1 if !$self->{'dbh'} or !$self->{'connected'}; #or !keys %{$self->{'dbh'}};
423 0           return !$self->{'dbh'}->ping();
424             }
425              
426             sub init {
427 0     0 0   my $self = shift;
428             #warn Dumper $self, \@_;
429             local %_ = (
430             'log' => sub (@) {
431 0     0     shift;
432 0           psmisc::printlog(@_);
433             },
434             'trace'=>sub(@) {
435 0     0     shift;
436 0           psmisc::trace(@_);
437             },
438             'driver' => 'mysql5',
439             'host' => ( $^O eq 'cygwin' ? '127.0.0.1' : 'localhost' ),
440             'database' => 'pssqldef',
441             #'connect_tries' => 100,
442             'error_sleep' => ( $ENV{'SERVER_PORT'} ? 1 : 3600 ),
443             'error_tries' => ( $ENV{'SERVER_PORT'} ? 1 : 1000 ),
444             'error_chain_tries' => ( $ENV{'SERVER_PORT'} ? 1 : 100 ),
445             #($ENV{'SERVER_PORT'} ? ('connect_tries'=>1) : ()),
446             #'reconnect_tries' => 10, #look old
447             'connect_tries' => ( $ENV{'SERVER_PORT'} ? 1 : 0 ),
448             'connect_chain_tries' => 0,
449             'connect_auto' => 0,
450             'connect_params' => {
451             'RaiseError' => 0,
452             'AutoCommit' => 1,
453             'PrintError' => 0,
454             'PrintWarn' => 0,
455             'HandleError' => sub {
456 0     0     $self->trace( 'dev', 'HandleError', @_, $DBI::err, $DBI::errstr );
457 0           $self->err(join ', ', grep {$_} $DBI::err, $DBI::errstr);
  0            
458 0 0 0       push @{$self->{error_log}||=[]},$self->err() if $self->{'error_collect'};
  0            
459             #psmisc::caller_trace(15)
460             },
461             },
462             #'connect_check' => 1, #check connection on every keep()
463 0 0         ( $ENV{'SERVER_PORT'} ? () : ( 'auto_repair' => 10 ) ), # or number 10-30
    0          
    0          
    0          
    0          
    0          
464             'auto_repair_selected' => 0, # repair all tables
465             'auto_install' => 1, 'auto_install_db' => 1, 'err_retry_unknown' => 0,
466             #'reconnect_sleep' => 3600, #maximum sleep on connect error
467             'codepage' => 'utf-8',
468             #'cp_in' => 'utf-8',
469             'index_postfix' => '_i', 'limit_max' => 1000, 'limit_default' => 100,
470             #'limit' => 100,
471             'page_min' => 1, 'page_default' => 1,
472             #'varchar_max' => 255,
473             'varchar_max' => 65535,
474             'row_max' => 65535,
475             'primary_max' => 65535,
476             'fulltext_max' => 65535,
477             'AUTO_INCREMENT' => 'AUTO_INCREMENT',
478             'EXPLAIN' => 'EXPLAIN',
479             'statable' => { 'queries' => 1, 'connect_tried' => 1, 'connects' => 1, 'inserts' => 1 },
480             'statable_time' => { 'queries_time' => 1, 'queries_avg' => 1, },
481             'param_trans_int' => { 'on_page' => 'limit', 'show_from' => 'limit_offset', 'page' => 'page', 'accurate' => 'accurate' },
482             #'param_trans' => { 'codepage'=>'cp_out' ,},
483             'connect_cached' => 1,
484             'char_type' => 'VARCHAR',
485             'true' => 1,
486             'fulltext_glue' => 'OR',
487             'retry_vars' => [qw(auto_repair connect_tries connect_chain_tries error_sleep error_tries auto_check)],
488             'err' => 0,
489             'insert_cached_time' => 60,
490             'stat_every' => 60,
491             'auto_repairs_max' => 2,
492             @_,
493             );
494 0           @{$self}{ keys %_ } = values %_;
  0            
495             #$self->{$_} //= $_{$_} for keys %_;
496             #%_ = @_;
497             #$self->{$_} = $_{$_} for keys %_;
498             #$self->log( 'dev', 'initdb', "$self->{'database'},$self->{'dbname'};");
499 0 0         $self->{'database'} = $self->{'dbname'} if $self->{'dbname'};
500 0   0       $self->{'dbname'} ||= $self->{'database'};
501 0           $self->calc();
502 0           $self->functions();
503 0           ( $tq, $rq, $vq ) = $self->quotes();
504 0 0 0       DBI->trace( $self->{'trace_level'}, $self->{'trace'} ) if $self->{'trace_level'} and $self->{'trace'};
505 0           return 0;
506             }
507              
508             sub calc {
509 0     0 0   my $self = shift;
510 0   0       $self->{'default'} ||= \%default;
511             $self->{'default'}{'pg'}{'match'} = sub {
512 0     0     my $self = shift;
513 0 0         return undef unless $self->{'use_fulltext'};
514 0           my ( $param, $param_num, $table, $search_str, $search_str_stem ) = @_;
515 0           my ( $ask, $glue );
516 0           s/(?:^\s+)|(?:\s+$)//, s/\s+/$self->{'fulltext_word_glue'}/g for ( $search_str, $search_str_stem );
517 0           local %_;
518 0           map { $_{ $self->{'table'}{$table}{$_}{'fulltext'} } = 1 }
  0            
519 0           grep { $self->{'table'}{$table}{$_}{'fulltext'} } keys %{ $self->{'table'}{$table} };
  0            
520 0           for my $index ( keys %_ ) {
521 0 0         my $stem =
522 0           grep { $self->{'table'}{$table}{$_}{'fulltext'} eq $index and $self->{'table'}{$table}{$_}{'stem_index'} }
523 0           keys %{ $self->{'table'}{$table} };
524 0 0         my $double =
525 0           grep { $self->{'table'}{$table}{$_}{'fulltext'} and $self->{'table'}{$table}{$_}{'stem'} }
526 0           keys %{ $self->{'table'}{$table} };
527 0 0 0       next if $double and ( $self->{'accurate'} xor !$stem );
      0        
528 0 0         $ask .= " $glue $index @@ to_tsquery( ${vq}$self->{'fulltext_config'}${vq}, "
529             . $self->squotes( $stem ? $search_str_stem : $search_str ) . ")";
530 0   0       $glue ||= $self->{'fulltext_glue'};
531             }
532 0           return $ask;
533             }
534 0 0         if $self->{'use_fulltext'};
535 0           %{ $self->{'default'}{'mysql6'} } = %{ $self->{'default'}{'mysql5'} };
  0            
  0            
536 0           %{ $self->{'default'}{'mysql4'} } = %{ $self->{'default'}{'mysql5'} };
  0            
  0            
537 0           $self->{'default'}{'mysql4'}{'SET NAMES'} = $self->{'default'}{'mysql4'}{'DEFAULT CHARACTER SET'} =
538             $self->{'default'}{'mysql4'}{'ON DUPLICATE KEY UPDATE'} = '';
539 0           $self->{'default'}{'mysql4'}{'varchar_max'} = 255;
540 0           %{ $self->{'default'}{'mysql3'} } = %{ $self->{'default'}{'mysql4'} };
  0            
  0            
541 0           $self->{'default'}{'mysql3'}{'table options'} = '';
542 0           $self->{'default'}{'mysql3'}{'USE_FRM'} = '';
543 0           $self->{'default'}{'mysql3'}{'no_boolean'} = 1;
544             #%{ $self->{'default'}{'sqlite2'} } = %{ $self->{'default'}{'sqlite'} };
545             #$self->{'default'}{'sqlite2'}{'IF NOT EXISTS'} = $self->{'default'}{'sqlite2'}{'IF EXISTS'} = '';
546 0 0         $self->{'default'}{'pg'}{'fulltext_config'} = 'default' if $self->{'old_fulltext'};
547 0           %{ $self->{'default'}{'pgpp'} } = %{ $self->{'default'}{'pg'} };
  0            
  0            
548 0           $self->{'default'}{'pgpp'}{'dbi'} = 'PgPP';
549 0           $self->{'default'}{'pgpp'}{'params'} = [qw(dbname host port path debug)];
550 0           %{ $self->{'default'}{'mysqlpp'} } = %{ $self->{'default'}{'mysql5'} };
  0            
  0            
551 0           $self->{'default'}{'mysqlpp'}{'dbi'} = 'mysqlPP';
552 0           $self->{'default'}{'sphinx'}{'match'} = $self->{'default'}{'mysql5'}{'match'};
553 0   0       $self->{'driver'} ||= 'mysql5';
554 0 0         $self->{'driver'} = 'mysql5' if $self->{'driver'} eq 'mysql';
555             #print "U0:", $self->{user};
556             #print "D0:", $self->{dbi};
557 0   0       $self->{$_} //= $self->{'default'}{ $self->{'driver'} }{$_} for keys %{ $self->{'default'}{ $self->{'driver'} } };
  0            
558             #print "U1:", $self->{user};
559             #print "D1:", $self->{dbi};
560             #$self->log( 'dev', "calc dbi[$self->{'dbi'} ||= $self->{'driver'}]");
561 0 0 0       $self->{'dbi'} ||= $self->{'driver'}, $self->{'dbi'} =~ s/\d+$//i unless $self->{'dbi'};
562 0           $self->{'codepage'} = psmisc::cp_normalize( $self->{'codepage'} );
563 0   0       local $_ = $self->{ $self->{'codepage'} } || $self->{'codepage'};
564 0           $self->{'cp'} = $_;
565 0   0       $self->{'cp_set_names'} ||= $_;
566             #$self->{'cp_int'} ||= 'cp1251'; # internal
567 0   0       $self->{'cp_int'} ||= 'utf-8'; # internal
568 0   0       $self->{'cp_out'} ||= 'utf-8'; # internal
569 0           $self->cp_client( $self->{'codepage'} );
570             }
571              
572             sub _connect {
573 0     0     my $self = shift;
574              
575             =c
576             $self->log(
577             'dev', 'conn',
578             "dbi:$self->{'dbi'}:"
579             #"dbi:$self->{'default'}{ $self->{'driver'} }{'dbi'}:database=$self->{'base'};"
580             #map {"$_:$self->{$_}"} qw(dbi database)
581             . join(
582             ';',
583             map( { $_ . '=' . $self->{$_} }
584             grep { defined( $self->{$_} ) } @{ $self->{'params'} } )
585             ),
586             $self->{'user'},
587             $self->{'pass'},
588             #\%{ $self->{'connect_params'} }
589             $self->{'connect_params'}
590             );
591             =cut
592              
593 0           local @_ = (
594             "dbi:$self->{'dbi'}:"
595 0           . join( ';', map( { $_ . '=' . $self->{$_} } grep { defined( $self->{$_} ) } @{ $self->{'params'} } ) ),
  0            
  0            
596             $self->{'user'}, $self->{'pass'}, $self->{'connect_params'}
597             );
598             #$self->log('dmp', "connect_cached = ",$self->{'connect_cached'}, Dumper(\@_));
599 0 0         $self->{'dbh'} = ( $self->{'connect_cached'} ? DBI->connect_cached(@_) : DBI->connect(@_) );
600 0           local $_ = $self->err_parse( \'Connection', $DBI::err, $DBI::errstr );
601 0           return $_;
602             }
603              
604             sub sleep {
605 0     0 0   my $self = shift;
606 0           return psmisc::sleeper(@_);
607             }
608              
609             sub functions {
610 0     0 0   my $self = shift;
611             $self->{'user_params'} ||= sub {
612 0     0     my $self = shift;
613 0           ( $tq, $rq, $vq ) = $self->quotes();
614 0           my $param = { map { %$_ } @_ };
  0            
615 0           for my $from ( keys %{ $self->{'param_trans_int'} } ) {
  0            
616 0   0       my $to = $self->{'param_trans_int'}{$from} || $from;
617 0 0         $param->{$from} = 1 if $param->{$from} eq 'on';
618 0           $self->{$to} =
619             psmisc::check_int( $param->{$from}, ( $self->{ $to . '_min' } ), $self->{ $to . '_max' }, $self->{ $to . '_default' } );
620             }
621 0   0       $self->cp_client( $work{'codepage'} || $param->{'codepage'} || $config{'codepage'} );
622 0   0       };
623             $self->{'dump'} ||= sub {
624 0     0     my $self = shift;
625 0           $self->log( 'dmp', caller, ':=', join( ':', %$self ) );
626 0           return 0;
627 0   0       };
628             $self->{'quotes'} ||= sub {
629             #sub quotes { # my ($tq, $rq, $vq) = $self->quotes();
630 0     0     my $self = shift;
631 0   0       $self->{'tq'} ||= $self->{'table quote'};
632 0   0       $self->{'rq'} ||= $self->{'row quote'};
633 0   0       $self->{'vq'} ||= $self->{'value quote'};
634             return (
635 0           $self->{'table quote'}, #$tq
636             $self->{'row quote'}, #$rq
637             $self->{'value quote'}, #$vq
638             );
639 0   0       };
640             $self->{'sleep'} ||= sub {
641 0     0     my $self = shift;
642 0           $self->log( 'dev', 'sql_sleeper', @_ );
643 0           return psmisc::sleeper(@_);
644 0   0       };
645             $self->{'drh_init'} ||= sub {
646 0     0     my $self = shift;
647 0   0       $self->{'drh'} ||= DBI->install_driver( $self->{'dbi'} );
648 0           return 0;
649 0   0       };
650             $self->{'repair'} ||= sub {
651 0     0     my $self = shift;
652 0           my $tim = psmisc::timer();
653 0 0         @_ = sort keys %{ $self->{'table'} } unless @_;
  0            
654 0 0         @_ = grep { $_ and $self->{'table'}{$_} } @_;
  0            
655 0           $self->log( 'info', 'Repairing table...', @_ );
656 0 0         $self->flush() unless $self->{'no_repair_flush'};
657 0           local $self->{'error_tries'} = 0; #!
658 0 0         $self->query_log( "REPAIR TABLE "
    0          
    0          
659             . join( ',', map( $self->tquote("$self->{'table_prefix'}$_"), @_ ) )
660             . ( $self->{'rep_quick'} ? ' ' . $self->{'QUICK'} : '' )
661             . ( $self->{'rep_ext'} ? ' ' . $self->{'EXTENDED'} : '' )
662             . ( $self->{'rep_frm'} ? ' ' . $self->{'USE_FRM'} : '' ) );
663 0           $self->flush();
664 0           $self->log( 'time', 'Repair per', psmisc::human( 'time_period', $tim->() ) );
665 0           return 0;
666 0   0       };
667             $self->{'query_time'} ||= sub {
668 0     0     my $self = shift;
669             #$self->log( 'dev', 'query_time ', $_[0]);
670 0           ++$self->{'queries'};
671 0           $self->{'queries_time'} += $_[0];
672 0   0       $self->{'queries_avg'} = $self->{'queries_time'} / $self->{'queries'} || 1;
673 0   0       };
674             $self->{'do'} ||= sub {
675 0     0     my $self = shift;
676             #$self->log( 'dev', 'do', @_);
677 0           my $ret;
678 0 0         return $ret if $self->keep();
679 0           $self->err(0);
680 0           for my $cmd (@_) {
681 0 0         next unless $cmd;
682 0   0       do {
683             {
684 0           $self->log( 'dmpbef', 'do(' . $self->{database} . '):[', $cmd, '] ' );
  0            
685 0           my $tim = psmisc::timer();
686 0 0         $ret += $self->{'dbh'}->do($cmd) if $self->{'dbh'};
687 0   0       $self->log(
688             'dmp', 'do(' . $self->{database} . '):[',
689             $cmd, '] = ', $ret, ' per', psmisc::human( 'time_period', $tim->() ),
690             'rps', psmisc::human( 'float', $ret / ( $tim->() || 1 ) )
691             );
692 0           $self->query_time( $tim->() );
693             }
694             } while ( $self->can_query() and $self->err_parse( \$cmd, $DBI::err, $DBI::errstr ) );
695             }
696 0           return $ret;
697 0   0       };
698             $self->{'can_query'} ||= sub {
699 0     0     my $self = shift;
700             return
701 0   0       !( $work{'die'} or $self->{'die'} or $self->{'fatal'} )
702             && ( !$self->{'error_chain_tries'} or $self->{'errors_chain'} < $self->{'error_chain_tries'} )
703             && ( !$self->{'error_tries'} or $self->{'errors'} < $self->{'error_tries'} );
704 0   0       };
705             $self->{'prepare'} ||= sub {
706 0     0     my $self = shift;
707 0           my ($query) = @_;
708 0 0         return 1 if $self->keep();
709 0           $self->log( 'dmpbef', "prepare query {$query}" );
710 0 0         return 2 unless $query;
711             #warn $self->err();
712 0           $self->err(0);
713 0           my $ret;
714 0           my $tim = psmisc::timer();
715             #$self->log('dbg', "prepare", __LINE__, );
716 0   0       do {
717             {
718 0 0         next unless $self->{'dbh'};
  0            
719 0 0         $self->{'sth'}->finish() if $self->{'sth'};
720 0           $self->{'sth'} = $self->{'dbh'}->prepare($query);
721 0 0 0       redo if $self->can_query() and $self->err_parse( \$query, $DBI::err, $DBI::errstr, 1 );
722 0 0         last unless $self->{'sth'};
723 0           $ret = $self->{'sth'}->execute();
724             }
725             } while ( $self->can_query() and $self->err_parse( \$query, $DBI::err, $DBI::errstr ) );
726 0           $self->query_time( $tim->() );
727             #$self->log('dbg', "prepare", __LINE__, );
728 0 0         return 3 if $DBI::err;
729 0 0         $self->{'dbirows'} = 0 if ( $self->{'dbirows'} = $DBI::rows ) == 4294967294;
730 0 0         $self->{'dbirows'} = $self->{'limit'} if $self->{'no_dbirows'};
731             #$self->log('dbg', "prepare", __LINE__, ':',$ret, $DBI::rows,'=',(($self->{'no_dbirows'} && $ret) ? '0E0' : !int $ret), 'dr=', $self->{'dbirows'});
732 0 0 0       return ( ( $self->{'no_dbirows'} && $ret ) ? undef : !int $ret );
733 0   0       };
734             $self->{'line'} ||= sub {
735 0     0     my $self = shift;
736             #$self->log('dev', "line prep");
737 0 0 0       return {} if @_ and $self->prepare(@_);
738             #$self->log('dev', "line sth");
739 0 0 0       return {} if !$self->{'sth'} or $self->{'sth'}->err;
740 0           my $tim = psmisc::timer();
741             #$self->log('dev', "line fetch");
742 0   0       local $_ = $self->{'sth'}->fetchrow_hashref() || {};
743 0 0         $_ = scalar( psmisc::cp_trans_hash( $self->{'codepage'}, $self->{'cp_out'}, $_ ) ) if $self->{'codepage'} ne $self->{'cp_out'};
744 0           $self->{'queries_time'} += $tim->();
745 0 0         $self->log(
746             'dmp', 'line(' . $self->{database} . '):[', @_, '] = ', scalar keys %$_, ' per', psmisc::human( 'time_period', $tim->() ),
747             'err=', $self->err(),
748             #( caller(2) )[0]);
749             ) if ( caller(2) )[0] ne 'pssql';
750 0           return $_;
751 0   0       };
752             $self->{'query'} ||= sub {
753 0     0     my $self = shift;
754             #$self->log("qrun");
755 0           my $tim = psmisc::timer();
756 0           my @hash;
757 0           for my $query (@_) {
758 0 0         next unless $query;
759 0 0 0       local $self->{'explain'} = 0, $self->query_log( $self->{'EXPLAIN'} . ' ' . $query )
760             if $self->{'explain'} and $self->{'EXPLAIN'};
761 0           local $_ = $self->line($query);
762 0 0         next unless keys %$_;
763 0           push( @hash, $_ );
764 0 0 0       next unless $self->{'sth'} and keys %$_;
765 0           my $tim = psmisc::timer();
766             #$self->log("Db[",%$_,"]($self->{'codepage'}, $self->{'cp_out'})"),
767 0           while ( $_ = $self->{'sth'}->fetchrow_hashref() ) {
768 0 0         if ($self->{'codepage'} ne $self->{'cp_out'}) {
769 0           push @hash, scalar psmisc::cp_trans_hash( $self->{'codepage'}, $self->{'cp_out'}, $_ );
770             } else {
771 0           push @hash, $_;
772             }
773             }
774             #$self->log("Da[",%$_,"]"),
775 0           $self->{'queries_time'} += $tim->();
776             }
777             $self->log(
778 0   0       'dmp', 'query(' . $self->{database} . '):[',
779             @_, '] = ', scalar @hash, ' per', psmisc::human( 'time_period', $tim->() ),
780             'rps', psmisc::human( 'float', ( scalar @hash ) / ( $tim->() || 1 ) ),
781             'err=', $self->err()
782             );
783 0 0 0       $self->{'dbirows'} = scalar @hash if $self->{'no_dbirows'} or $self->{'dbirows'} <= 0;
784             #$self->log('dbirows=', $self->{'dbirows'});
785             #$self->query_print($_) for @hash;
786             #$self->log('qcp', $self->{'codepage'}, Dumper \@hash);
787 0 0         if ( $self->{'codepage'} eq 'utf-8' ) {
788 0           for (@hash) { utf8::decode $_ for grep {!ref} %$_; }
  0            
  0            
789             }
790 0 0         return wantarray ? @hash : \@hash;
791 0   0       };
792             $self->{'query_log'} ||= sub {
793 0     0     my $self = shift;
794 0           my @ret;
795 0           for (@_) { push( @ret, $self->query_print( $self->query($_) ) ); }
  0            
796 0 0         return wantarray ? @ret : \@ret;
797 0   0       };
798             $self->{'query_print'} ||= sub {
799 0     0     my $self = shift;
800 0           my @hash = @_;
801 0 0 0       return unless @hash and %{ $hash[0] };
  0            
802 0           $self->log( 'dbg', 'sql query', $_ );
803 0 0         $self->log( 'dbg', '|', join "\t|", keys %{ $hash[0] } ) if keys %{ $hash[0] };
  0            
  0            
804 0           $self->log( 'dbg', '|', join( "\t|", values %{$_} ) ) for @hash;
  0            
805 0 0         return wantarray ? @_ : \@_;
806 0   0       };
807             $self->{'quote'} ||= sub {
808 0     0     my $self = shift;
809 0           my ( $s, $q, $qmask ) = @_;
810 0 0 0       return $s if $self->{'no_quote_null'} and $s =~ /^null$/i;
811 0 0 0       return $self->{'dbh'}->quote( defined $s ? $s : '' ) if $self->{'dbh'} and !$q;
    0          
812 0   0       $q ||= "'"; # mask "|', q='
813 0 0         if ( $self->{'quote_slash'} ) { $s =~ s/($q|\\)/\\$1/g; }
  0            
814 0           else { $s =~ s/($q)/$1$1/g; }
815 0           return $q . $s . $q;
816 0   0       };
817             $self->{'squotes'} ||= sub {
818 0     0     my $self = shift;
819 0           return ' ' . $self->quote(@_) . ' ';
820 0   0       };
821             $self->{'tquote'} ||= sub {
822 0     0     my $self = shift;
823 0           return $self->{'tq'} . $_[0] . $self->{'tq'};
824 0   0       };
825             $self->{'rquote'} ||= sub {
826 0     0     my $self = shift;
827 0           return $self->{'rq'} . $_[0] . $self->{'rq'};
828 0   0       };
829 0   0       $self->{'vquote'} ||= $self->{'quote'};
830             $self->{'filter_row'} ||= sub {
831 0     0     my $self = shift;
832 0           my ( $table, $filter, $values ) = @_;
833 0           local %_;
834 0           map { $_{$_} = $values->{$_} } grep { $self->{'table'}{$table}{$_}{$filter} } keys %{ $self->{'table'}{$table} };
  0            
  0            
  0            
835 0 0         return wantarray ? %_ : \%_;
836 0   0       };
837             $self->{'err_parse'} ||= sub {
838 0     0     my $self = shift;
839 0           my ( $cmd, $err, $errstr, $sth ) = @_;
840 0   0       $err ||= $DBI::err;
841 0   0       $errstr ||= $DBI::errstr;
842 0 0         my $state = $self->{'dbh'}->state if $self->{'dbh'};
843 0           my $errtype = $self->error_type( $err, $errstr );
844 0 0 0       $errtype ||= 'connection' unless $self->{'dbh'};
845 0 0         $self->{'fatal'} = 1 if $errtype eq 'fatal';
846             #$self->log('dev','error entry', $errtype, $err, $errstr, 'wdi=', $work{'die'}, 'di=', $self->{'die'}, 'fa=', $self->{'fatal'});
847              
848             =c
849              
850             ok
851             no dbi ret1
852             install act ret1
853             repair act ret1
854             syntax ret0
855             fatal ret0
856             ignore ret0
857             other ret1 n times
858              
859             tries total
860             tries
861              
862             =cut
863              
864 0 0 0       $self->log(
      0        
      0        
      0        
      0        
      0        
865             'dev', "err_parse st0 ret1 ",
866             'wdi=', $work{'die'}, 'di=', $self->{'die'}, 'fa=', $self->{'fatal'}, 'er=',
867             ( $self->{'errors'} >= $self->{'error_tries'} ),
868             $self->{'errors'}, $self->{'error_tries'},
869             $errtype, $state
870             ),
871             CORE::sleep(1), return $self->err(1)
872             if $work{'die'}
873             or $self->{'die'}
874             or $self->{'fatal'}
875             or ( $self->{'error_tries'} and $self->{'errors'} > $self->{'error_tries'} )
876             or ( $self->{'error_chain_tries'} and $self->{'errors_chain'} > $self->{'error_chain_tries'} );
877 0 0 0       $self->log( 'err', 'err_parse: IMPOSIBLE! !$err and !$self->{sth}' ), $self->err('sth'), return 0
      0        
878             if $sth and ( !$err and !$self->{'sth'} );
879 0 0 0       $self->{'errors_chain'} = 0, return $self->err(0) if !$err and $self->{'dbh'};
880 0           ++$self->{'errors_chain'};
881 0           ++$self->{'errors'};
882 0           $self->log( 'err',
883             "SQL: error[$err,$errstr,$errtype,$state] on executing {$$cmd} [sleep:$self->{'error_sleep'}] dbh=[$self->{'dbh'}]" );
884 0           $self->log( 'dev', "err_parse st3 ret0 fatal=$errtype" ), $self->err($errtype), return (0)
885 0 0 0       if $errtype and grep { $errtype eq $_ } qw(fatal syntax ignore);
886 0           $self->log( 'dev', "err_parse sleep($self->{'error_sleep'}), ret1 ", );
887 0 0         $self->sleep( $self->{'error_sleep'}, 'sql_parse' ) if $self->{'error_sleep'};
888 0           $self->log( 'dev', "err_parse st3 ret1 fatal=$errtype" ), return $self->err($errtype)
889 0 0 0       if $errtype and grep { $errtype eq $_ } qw(retry);
890              
891 0 0 0       if ( $errtype eq 'install_db' and $self->{'auto_install_db'}-- > 0 ) {
892 0           $self->log( 'info', "SQL: trying automatic install db" );
893 0           $self->create_databases(@_);
894 0           return $self->err($errtype);
895             }
896 0 0         $self->log( 'info', "SQL: trying reconnect[$self->{'connected'}]" ), $self->reconnect(), return $self->err('dbh')
897             if !$self->{'dbh'};
898 0 0 0       if ( $errtype eq 'install' or $errtype eq 'upgrade' ) {
899 0 0         if ( $self->{'auto_install'}-- > 0 ) {
900 0           $self->log( 'dev', "SQL:install err " );
901 0           $self->log( 'info', "SQL: trying automatic install" );
902 0           $self->$errtype();
903 0           return $self->err($errtype);
904             } else {
905 0           $self->log( 'dev', "SQL:NOinstall err " );
906 0           $self->err($errtype);
907 0           return (0);
908             }
909             }
910 0 0         $self->log( 'err', "SQL: connection error, trying reconnect and retry last query" ), $self->dropconnect(),
911             $self->reconnect(), return $self->err($errtype)
912             if $errtype eq 'connection';
913 0 0 0       if ( $self->{'auto_repair'} and $errtype eq 'repair' ) {
914 0           my ($repair) = $errstr =~ /'(?:.*[\\\/])*(\w+)(?:\.my\w)?'/i;
915 0 0         $repair = $self->{'current_table'} unless %{ $self->{'table'}{$repair} || {} };
  0 0          
916 0 0         if ( $self->{'auto_repairs'}{$repair} < $self->{'auto_repairs_max'} ) {
917 0           my $sl = int( rand( $self->{'auto_repair'} + 1 ) );
918 0           $self->log( 'info', 'pre repair sleeping', $sl );
919 0           $self->sleep($sl);
920 0 0 0       if ( $sl == 0 or $self->{'force_repair'} ) {
921 0 0 0       $self->log( 'info', 'denied repair', $repair ), return $self->err(1)
      0        
922             if $self->{'auto_repair_selected'}
923             and ( !$repair or $self->{'auto_repair_selected'} and $self->{'table_param'}{$repair}{'no_auto_repair'} );
924 0           ++$self->{'auto_repairs'}{$repair};
925 0           $self->log( 'info', "SQL: trying automatic repair", $repair );
926 0           $self->repair($repair);
927 0           $self->{'rep_ext'} = $self->{'rep_frm'} = 1;
928 0           $self->{'rep_quick'} = 0;
929 0           return $self->err($errtype);
930             }
931             }
932             }
933 0 0         $self->log( 'dev', "err_parse st2 ret1 no dbh", $err, $errstr ), return $self->err('dbh') if !$self->{'dbh'};
934 0           $self->log( 'dev', "err_parse unknown error ret($self->{'err_retry_unknown'}), end: [$err], [$errstr], [$errtype]" );
935 0           return $self->err( $self->{'err_retry_unknown'} );
936 0   0       };
937             $self->{'install'} ||= sub {
938 0     0     my $self = shift;
939 0           return $self->create_databases(@_) + $self->create_tables();
940 0   0       };
941             $self->{'create_database'} ||= sub {
942 0     0     my $self = shift;
943 0           my $ret;
944 0           local $_;
945 0 0         local @_ = ( $self->{'database'} ) unless @_;
946 0 0         $self->drh_init() if $self->{'use_drh'};
947 0           for my $db (@_) {
948 0 0         if ( $self->{'use_drh'} ) {
    0          
949 0           $ret += $_ = $self->{'drh'}->func( 'createdb', $db, $self->{'host'}, $self->{'user'}, $self->{'pass'}, 'admin' );
950             } elsif ( $self->{'driver'} =~ /pg/i ) {
951             {
952 0           my $db = $self->{'dbname'};
  0            
953 0           local $self->{'dbname'} = 'postgres';
954 0           local $self->{'database'} = undef;
955 0           local $self->{'in_connect'} = undef;
956 0           $self->do("CREATE DATABASE $rq$db$rq WITH ENCODING $vq$self->{'cp'}$vq");
957             }
958 0           $self->reconnect();
959             }
960 0           $self->log( 'info', 'install database ', $db, '=', $ret );
961             }
962 0           return $ret;
963 0   0       };
964             $self->{'create_databases'} ||= sub { #http://dev.mysql.com/doc/refman/5.1/en/create-table.html
965 0     0     my $self = shift;
966 0           return $self->create_database( $self->{'database'} );
967 0   0       };
968             $self->{'create_tables'} ||= sub { #http://dev.mysql.com/doc/refman/5.1/en/create-table.html
969 0     0     my $self = shift;
970 0 0         my (%table) = %{ $self->{'table'} or {} };
  0            
971 0           my @ret;
972 0           for my $tab ( sort keys %table ) {
973 0           $self->log( 'dev', 'creating table', $tab );
974 0           push( @ret, $self->{'create_table'}->( $self, $tab, $table{$tab} ) );
975 0 0         push( @ret, $self->{'create_index'}->( $self, $tab, $table{$tab} ) ) unless $self->{'index in create table'};
976             }
977 0           return @ret;
978 0   0       };
979             $self->{'create_table'} ||= sub { #http://dev.mysql.com/doc/refman/5.1/en/create-table.html
980 0     0     my $self = shift;
981 0           my ( $tab, $table ) = @_;
982 0           my ( @subq, @ret );
983 0 0         return undef if $tab =~ /^\W/;
984 0           my ( @primary, %unique, %fulltext, @do );
985 0           for my $row ( sort { $table->{$b}{'order'} <=> $table->{$a}{'order'} } keys %$table ) {
  0            
986 0 0         push( @primary, $rq . $row . $rq ) if $table->{$row}{'primary'}
987             #!and $self->{'driver'} ne 'sqlite'
988             ;
989 0 0         push( @{ $fulltext{ $table->{$row}{'fulltext'} } }, $rq . $row . $rq ) if $table->{$row}{'fulltext'};
  0            
990 0 0 0       push( @{ $unique{ $table->{$row}{'unique'} } }, $rq . $row . $rq )
  0            
991             if $table->{$row}{'unique'} and $table->{$row}{'unique'} =~ /\D/;
992             }
993 0 0 0       if ( $self->{'driver'} =~ /pg/i and $self->{'use_fulltext'} ) {
994             #$self->log('dev', 'ftdev',$tab,Dumper(\%fulltext),
995 0 0         1 || $self->{'fulltext_trigger'}
996             ? push(
997             @do,
998             "DROP TRIGGER $self->{'IF EXISTS'} ${tab}_update_$_ ON $tab",
999             $self->{'old_fulltext'}
1000             ? ( "CREATE TRIGGER ${tab}_update_$_ BEFORE UPDATE OR INSERT ON $tab FOR EACH ROW EXECUTE PROCEDURE tsearch2($rq$_$rq, "
1001 0 0         . ( join( ', ', @{ $fulltext{$_} || [] } ) )
1002             . ")" )
1003             : (
1004             "CREATE TRIGGER ${tab}_update_$_ BEFORE UPDATE OR INSERT ON $tab FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger($rq$_$rq, ${vq}$self->{'fulltext_config'}${vq}, "
1005             . ( join( ', ', @{ $fulltext{$_} || [] } ) )
1006             . ")" )
1007             )
1008             : (),
1009             #),
1010 0 0         $table->{$_} = { 'order' => -9999, 'type' => 'tsvector', } for keys %fulltext;
1011             #push(@do,"update pg_ts_cfg set locale = 'en_US.UTF-8' where ts_name = 'default'") ,
1012             #push(@do,"select set_curcfg('default');") if @do;
1013             }
1014 0           for my $row ( grep { keys %{ $table->{$_} } } keys %$table ) {
  0            
  0            
1015 0 0         $table->{$row}{'varchar'} = 1 if $table->{$row}{'type'} =~ /^varchar$/i; #
1016             }
1017             #$self->log('dev', Dumper $table);
1018 0           for my $row ( sort { $table->{$b}{'order'} <=> $table->{$a}{'order'} } grep { keys %{ $table->{$_} } } keys %$table ) {
  0            
  0            
  0            
1019 0 0         next if $row =~ /^\W/;
1020 0           $table->{$row}{'length'} = psmisc::min( $self->{'varchar_max'}, $table->{$row}{'length'} );
1021 0           my $length = $table->{$row}{'length'};
1022 0 0         if ( !defined $length ) {
1023             {
1024 0           my ( @types, @maxs, );
  0            
1025 0 0 0       push @types, 'primary' if $table->{$row}{'primary'} and $table->{$row}{'type'} =~ /char/i;
1026 0 0         push @types, 'fulltext' if $table->{$row}{'fulltext'};
1027 0 0         push @types, 'unique' if $table->{$row}{'unique'};
1028 0 0         push( @types, 'varchar' ) if $table->{$row}{'varchar'}; #= 1 if $table->{$row}{'type'} =~ /^varchar$/i;
1029 0 0         last unless @types;
1030             #$self->log( 'dev', ' ======= ', $row, ' length detect start', @types );
1031 0           for my $type (@types) {
1032 0           my $max;
1033             #$type = $types[0];
1034 0           $max = $self->{ $type . '_max' }; # if $type ne 'varchar';
1035             #$self->log('dev',"type $type start ", $row, " max=$max");
1036 0 0 0       $max /= 3 if $self->{'codepage'} eq 'utf-8' and $self->{'driver'} =~ /mysql/;
1037             #$max-=2;
1038             #$self->log('dev','lenmax:',$row, "type=$type; max=$max, ", Dumper($table));
1039 0           my $same;
1040             my $nowtotal;
1041 0           for (
1042 0           grep {
1043 0           $_
1044             #$table->{ $_ }{$type} and $_ ne $row
1045             #and $table->{ $_ }{$type} eq $table->{ $row }{$type}
1046             } keys %{$table}
1047             )
1048             {
1049 0 0 0       $nowtotal += 2 if $type eq 'varchar' and $table->{$_}{'type'} =~ /^smallint$/i;
1050 0 0 0       $nowtotal += 4 if $type eq 'varchar' and $table->{$_}{'type'} =~ /^int$/i;
1051 0 0 0       $nowtotal += 8 if $type eq 'varchar' and $table->{$_}{'type'} =~ /^bigint$/i;
1052             #$self->log('dev', $row, 'look', $_, $type, $table->{ $_ }{$type} , $table->{ $_ }{'length'}, $table->{ $_ }{'type'});
1053 0 0         next unless $table->{$_}{$type} eq $table->{$row}{$type};
1054 0 0 0       next if !( $table->{$_}{$type} and $_ ne $row );
1055             #$self->log( 'dev', $row, 'minus', $_, $table->{$_}{'length'} ),
1056             #$max -= $table->{ $_ }{'length'};
1057 0           $nowtotal += $table->{$_}{'length'};
1058 0 0         ++$same,
1059             #$self->log('dev', $row, 'same', $_, $same),
1060             if !( $table->{$_}{'length'} );
1061             }
1062 0           $max -= $nowtotal;
1063 0           my $want = $max / ( $same + 1 );
1064             #$self->log('dev', $row, 'same', $same, 'tot:', $nowtotal,);
1065             #$self->log('dev','len0:',$row, "type=$type; max=$max, same=$same totalwo=$nowtotal want=$want el=", scalar keys %$table);
1066 0           $nowtotal = 0;
1067 0           for (
1068 0 0 0       grep {
      0        
1069 0           $table->{$_}{$type}
1070             and $_ ne $row
1071             and $table->{$_}{$type} eq $table->{$row}{$type}
1072             and !$table->{$_}{'length'}
1073             } keys %{$table}
1074             )
1075             {
1076 0 0 0       --$same,
1077             #$max += $want - $table->{ $_ }{'length_max'} ,
1078             $max -= $table->{$_}{'length_max'}, $nowtotal += $table->{$_}{'length_max'},
1079             #$self->log('dev','maxlen:',$row, "look=$_ type=$type; max=$max, same=$same totalwo=$nowtotal want=$want lenmax=$table->{$_}{'length_max'} ret=",$want - $table->{ $_ }{'length_max'}),
1080             if $table->{$_}{'length_max'} and $table->{$_}{'length_max'} < $want;
1081             }
1082             #|| $table->{ $_ }{'length_max'}
1083             #$self->log( 'dev', $row, 'same', $same, 'tot:', $nowtotal );
1084             #$self->log('dev','len1:',$row, "type=$type; max=$max, ");
1085             #$self->log('dev','lenpresame',$row, "type=$type; max=$max, same=$same totalwo=$nowtotal el=", scalar keys %$table);
1086 0 0         $max /= $same + 1 if $same;
1087 0           $max = int($max);
1088             #$self->log('dev','tot:',$row, $nowtotal+(($same+1) * $max));
1089             #$self->log('dev','len:',$row, "type=$type; max=$max, same=$same totalwo=$nowtotal el=", scalar keys %$table);
1090             #$max /= ( scalar @primary or 1 ) if $table->{$row}{'primary'} and $table->{$row}{'primary'};
1091             #$length /= ( scalar keys(%unique) + 1 ) if $table->{$row}{'unique'} and $table->{$row}{'unique'} =~ /\D/;
1092 0           push @maxs, $max;
1093             }
1094 0 0         push @maxs, $table->{$row}{'length_max'} if $table->{$row}{'length_max'};
1095 0 0         push @maxs, $self->{'varchar_max'} if $table->{$row}{'type'} =~ /^varchar$/i;
1096             #push @maxs, 1000 / 3
1097 0 0 0       push @maxs, $self->{'key_length'} / 3
      0        
1098             if $table->{$row}{'type'} =~ /^varchar$/i
1099             and $table->{$row}{'primary'}
1100             and $self->{'codepage'} eq 'utf-8';
1101             #$self->log( 'dev', $row, "key=$self->{'key_length'}", 'maxs:', @maxs , Dumper $table->{$row});
1102             #print "mx:",@maxs;
1103 0           $length = psmisc::min( grep { $_ > 0 } @maxs );
  0            
1104             #$table->{$row}{'length'} ||= $length if $table->{$row}{'type'} eq 'varchar';
1105 0   0       $table->{$row}{'length'} ||= $length;
1106              
1107             =z
1108             $length ||= $self->{'primary_max'} if $table->{$row}{'primary'} and $table->{$row}{'type'} =~ /char/i;
1109             $length ||= $self->{'fulltext_max'} if $table->{$row}{'fulltext'};
1110             $length ||= $self->{'unique_max'} if $table->{$row}{'unique'};
1111             $length ||= $self->{'varchar_max'} if $table->{$row}{'type'} =~ /^varchar$/i;
1112             $self->log('dev', 'crelenbef',$row, $length, 'prim=', $table->{$row}{'primary'});
1113             my $maxl = $self->{'row_max'} / scalar keys %$table; #todo better counting
1114             #my $maxl = $length / scalar keys %$table; #todo better counting
1115             $self->log('dev', 'maxl',$row, $maxl);
1116             $length = $maxl if $length > $maxl;
1117             $length /= 3 if $self->{'codepage'} eq 'utf-8' and $self->{'driver'} =~ /mysql/;
1118             #$self->log('dev', 'crelen',$row, $length, scalar keys(%unique) +1);
1119             #$self->log('dev', 'crelen',$row, $length, scalar @primary );
1120             $length /= ( scalar @primary or 1 ) if $table->{$row}{'primary'} and $table->{$row}{'primary'};
1121             $length /= ( scalar keys(%unique) + 1 ) if $table->{$row}{'unique'} and $table->{$row}{'unique'} =~ /\D/;
1122             #$length=int($length/(4));
1123             $self->log('dev', 'crelenaft',$row, $length);
1124             =cut
1125              
1126 0           $length = int($length);
1127             }
1128             }
1129             #$self->log('dev', "$row NN= $table->{$row}{'not null'}");
1130             push(
1131 0 0 0       @subq,
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
1132             $rq
1133             . $row
1134             . $rq
1135             . " $table->{$row}{'type'} "
1136             #. ( $table->{$row}{'length'} ? "($table->{$row}{'length'}) " : '' )
1137             . ( $length ? "($length) " : '' )
1138             . ( ( $table->{$row}{'unsigned'} and $self->{'UNSIGNED'} ) ? ' ' . $self->{'UNSIGNED'} : '' )
1139             . ( (
1140             #!S$self->{'driver'} ne 'sqlite' or
1141             !$table->{$row}{'auto_increment'}
1142             )
1143             #? ( ( $table->{$row}{'null'} ) ? ' NULL ' : ' NOT NULL ' )
1144             #? ( ( $table->{$row}{'null'} ) ? '' : ' NOT NULL ' )
1145             ? ( ( $table->{$row}{'not null'} ) ? ' NOT NULL ' : '' )
1146             : ''
1147             )
1148             . (
1149             ( defined( $table->{$row}{'default'} ) and !$table->{$row}{'auto_increment'} )
1150             ? " DEFAULT " . ( $table->{$row}{'default'} eq 'NULL' ? 'NULL' : "$vq$table->{$row}{'default'}$vq" ) . " "
1151             : ''
1152             )
1153             . ( ( $table->{$row}{'unique'} and $table->{$row}{'unique'} =~ /^\d+$/ ) ? ' UNIQUE ' : '' )
1154             #.( ( $self->{'driver'} eq '!Ssqlite' and $table->{$row}{'primary'} ) ? ' PRIMARY KEY ' : '' )
1155             . ( (
1156             $table->{$row}{'auto_increment'} and (
1157             #TEST S! $self->{'driver'} ne '!Ssqlite' or
1158             #$table->{$row}{'primary'} #?
1159             1
1160             )
1161             )
1162             ? ' '
1163             . $self->{'AUTO_INCREMENT'} . ' '
1164             : ''
1165             )
1166             . "$table->{$row}{'param'}"
1167             );
1168             }
1169             #iwh
1170 0 0         push( @subq, "PRIMARY KEY (" . join( ',', @primary ) . ")" ) if @primary;
1171 0           for my $row ( sort { $table->{$b}{'order'} <=> $table->{$a}{'order'} } keys %$table ) {
  0            
1172 0 0 0       push(
    0 0        
1173             @subq,
1174             "INDEX "
1175             . $rq
1176             . $row
1177             . $self->{'index_postfix'}
1178             . $rq . " ("
1179             . $rq
1180             . $row
1181             . $rq
1182             . (
1183             ( $table->{$row}{'index'} > 1 and $table->{$row}{'index'} < $table->{$row}{'length'} )
1184             ? '(' . $table->{$row}{'index'} . ')'
1185             : ''
1186             )
1187             . ")"
1188             ) if $table->{$row}{'index'} and $self->{'index in create table'};
1189              
1190             =c
1191             push(
1192             @subq,
1193             "INDEX UNIQUE"
1194             . $rq
1195             . $row
1196             . $self->{'index_postfix'} . 'u'
1197             . $rq . " ("
1198             . $rq
1199             . $row
1200             . $rq
1201             . ")"
1202             )
1203             if $table->{$row}{'unique'}
1204             and $self->{'index in create table'};
1205             =cut
1206              
1207 0 0         push( @primary, $rq . $row . $rq ) if $table->{$row}{'primary'};
1208             }
1209 0           push( @subq, "UNIQUE " . ( $self->{'unique name'} ? $rq . $_ . $rq : '' ) . " (" . join( ',', @{ $unique{$_} } ) . ")" )
  0            
1210 0 0         for grep @{ $unique{$_} }, keys %unique;
1211 0 0         if ( $self->{'index in create table'} ) {
1212 0           push( @subq, "FULLTEXT $rq$_$rq (" . join( ',', @{ $fulltext{$_} } ) . ")" ) for grep @{ $fulltext{$_} }, keys %fulltext;
  0            
  0            
1213             #push( @subq, "UNIQUE $rq$_$rq (" . join( ',', @{ $unique{$_} } ) . ")" ) for grep @{ $unique{$_} }, keys %unique;
1214             }
1215             #push(
1216             #$self->log('dev', "[cp:$self->{'cp'}; dcs:$self->{'DEFAULT CHARACTER SET'}]");
1217             #@ret,
1218             return
1219             #print
1220 0           map { $self->do($_) }
  0            
1221 0           grep { $_ }
1222             ( !@subq
1223             ? ()
1224             : 'CREATE TABLE '
1225             . $self->{'IF NOT EXISTS'}
1226             . " $tq$self->{'table_prefix'}$tab$tq ("
1227             . join( ",", @subq )
1228 0 0 0       . ( join ' ', '', grep { $_ } $self->{'table_constraint'}, $self->{'table_param'}{$tab}{'table_constraint'} ) . ") "
    0          
1229             . $self->{'table options'} . ' '
1230             . $self->{'table_param'}{$tab}{'table options'}
1231             . ( $self->{'cp'} && $self->{'DEFAULT CHARACTER SET'} ? " $self->{'DEFAULT CHARACTER SET'} $vq$self->{'cp'}$vq " : '' )
1232             . ';' ), @do;
1233             #return undef;
1234 0   0       };
1235             #$self->{'do'}{'create_index'} = 1;
1236             $self->{'create_index'} ||= sub {
1237             #sub create_index {
1238 0     0     my $self = shift;
1239 0           my @ret;
1240 0           my ( $tab, $table ) = @_;
1241             #for my $table( @_){
1242             #for my $tab ( keys %$table ) {
1243 0           my (@subq);
1244             #next if $tab =~ /^\W/;
1245 0           for my $row ( sort { $table->{$b}{'order'} <=> $table->{$a}{'order'} } keys %$table ) {
  0            
1246 0 0         next if $row =~ /^\W/;
1247 0 0         push( @ret,
    0          
1248             'CREATE INDEX '
1249             . $self->{'index_IF NOT EXISTS'} . ' '
1250             . $rq
1251             . $row
1252             . ( $self->{'index_name_table'} ? '_' . $tab : '' )
1253             . $self->{'index_postfix'}
1254             . $rq . ' ON '
1255             . " $tq$self->{'table_prefix'}$tab$tq ( $rq$row$rq )" )
1256             if $table->{$row}{'index'};
1257             }
1258             #}
1259 0           return $self->do(@ret);
1260 0   0       };
1261             $self->{'create_indexes'} ||= sub {
1262             #$self->log values %{ $self->{'table'}};
1263 0     0     $self->create_index( $_, $self->{'table'}{$_} ) for keys %{ $self->{'table'} };
  0            
1264 0   0       };
1265             #$self->{'do'}{'drop_table'} = 1;
1266             $self->{'drop_table'} ||= sub {
1267             #sub drop_table {
1268 0     0     my $self = shift;
1269 0           my @ret;
1270 0           for my $tab (@_) {
1271 0           my ($sql);
1272 0 0 0       next if $tab =~ /^\W/ or $tab !~ /\w/;
1273 0           $sql .= "DROP TABLE " . $self->{'IF EXISTS'} . " $tq$self->{'table_prefix'}$tab$tq $self->{'CASCADE'}";
1274 0           push( @ret, $sql );
1275             }
1276 0           return $self->do(@ret);
1277 0   0       };
1278             $self->{'drop_database'} ||= sub {
1279             #sub drop_table {
1280 0     0     my $self = shift;
1281 0           my @ret;
1282 0 0         @_ = $self->{'database'} if !@_;
1283 0 0 0       my $rec = 1 if $self->{'driver'} =~ /pg/i and grep { $self->{'database'} eq $_ } @_;
  0            
1284 0 0         if ($rec) {
1285             #$self->log('dev','tryreconnect', $self->{'connected'});
1286 0           local $self->{'dbname'} = undef;
1287 0           local $self->{'database'} = undef;
1288 0 0         $self->{'dbname'} = $self->{'database'} = 'postgres' if $self->{'driver'} =~ /pg/i; #TODO MYSQL
1289             #$self->dropconnect();
1290 0           $self->reconnect();
1291             }
1292 0           for my $tab (@_) {
1293 0           my ($sql);
1294 0 0 0       next if $tab =~ /^\W/ or $tab !~ /\w/;
1295 0           $sql .= "DROP DATABASE " . $self->{'IF EXISTS'} . " $tq$self->{'table_prefix'}$tab$tq";
1296 0           push( @ret, $sql );
1297             }
1298 0           @ret = $self->do(@ret);
1299 0 0         if ($rec) { $self->reconnect(); }
  0            
1300 0           return @ret;
1301 0   0       };
1302             $self->{'drop_tables'} ||= sub {
1303 0     0     my $self = shift;
1304 0 0         @_ = keys %{ $self->{'table'} or {} } if !@_;
  0 0          
1305 0           return $self->drop_table(@_);
1306 0   0       };
1307             #{
1308             #my (%buffer);
1309             #$processor{'out'}{'array'} ||= sub {
1310             $self->{'insert_fields'} ||= sub {
1311 0     0     my $self = shift;
1312 0   0       my $table = shift || $self->{'current_table'};
1313 0           return grep {
1314 0           $self->{'table'}{$table}{$_}{'array_insert'}
1315             #or !defined $self->{'table'}{$table}{$_}{'default'}
1316 0           } keys %{ $self->{'table'}{$table} };
1317 0   0       };
1318             $self->{'insert_order'} ||= sub {
1319 0     0     my $self = shift;
1320 0   0       my $table = shift || $self->{'current_table'};
1321 0           return sort { $self->{'table'}{$table}{$b}{'order'} <=> $self->{'table'}{$table}{$a}{'order'} } $self->insert_fields($table)
  0            
1322             #grep { $self->{'table'}{$table}{$_}{'array_insert'} or !defined $self->{'table'}{$table}{$_}{'default'}
1323             #} keys %{ $self->{'table'}{$table} }
1324 0   0       };
1325             $self->{'insert_cached'} ||= sub {
1326 0     0     my $self = shift;
1327 0           my $table = shift;
1328 0   0       my $table_insert = $table || $self->{'current_table'};
1329             #$self->log('dev','insert_cached', $table,Dumper(\@_), 'by', ( $self->{'table_param'}{$table}{'insert_by'} or $self->{'insert_by'} ), 'bytime=', $self->{'insert_cached_time'});
1330 0           my @dummy;
1331             #my ( $tq, $rq, $vq ) = sql_quotes();
1332 0 0 0       ++$self->{'inserts'}, ++$self->{'table_updated'}{$table_insert}, push( @{ $self->{'insert_buffer'}{$table_insert} }, \@_ )
  0            
1333             if $table_insert and @_;
1334             #$self->log('dev', 'cached', keys %{ $self->{'insert_buffer'} });
1335 0 0         for my $table ( $table ? ($table) : ( keys %{ $self->{'insert_buffer'} } ) ) {
  0            
1336 0   0       $self->{'insert_block'}{$table} //= $self->{'table_param'}{$table}{'insert_by'} || $self->{'insert_by'};
      0        
1337             #unless defined $self->{'insert_block'}{$table};
1338             #$self->log('ict', $table,int(time() - $self->{'insert_buffer_time'}{$table}));
1339             #$self->{'insert_buffer_time'}{$table}||=time();
1340 0 0 0       if (
      0        
      0        
1341             $self->{'insert_block'}{$table}-- <= 1
1342             or !scalar(@_)
1343             #or time() - $self->{'insert_buffer_time'}{$table} > $self->{'insert_cached_time'}
1344             or time() - ( $self->{'insert_buffer_time'}{$table} ||= time() ) > $self->{'insert_cached_time'}
1345             )
1346             {
1347 0 0         $self->{'stat'}{'time'}{'count'} += scalar @{ $self->{'insert_buffer'}{$table_insert} || [] };
  0            
1348 0           $self->{'insert_buffer_time'}{$table} = time();
1349 0           $self->{'current_table'} = $table;
1350             #$self->log('doing insert', $table, Dumper $self->{'insert_buffer'}{$table});
1351 0           $self->do(
1352             join(
1353             '',
1354             ( $self->{'ON DUPLICATE KEY UPDATE'} ? $self->{'INSERT'} : $self->{'REPLACE'} )
1355             . " $self->{$self->{'insert_options'}} INTO $tq$self->{'table_prefix'}$table$tq (",
1356 0           join( ',', map { $rq . $_ . $rq } $self->insert_order($table) ),
1357             ") VALUES\n",
1358             join(
1359             ",\n",
1360             map {
1361 0           join(
1362             '', '(',
1363             join(
1364             ',',
1365             #map { $self->quote( scalar cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $$_ ), $self->{'value quote'} ) }
1366 0           map { $self->quote( scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $$_ ) ) }
1367 0           @{$_}[ 0 .. scalar( $self->insert_fields($table) ) - 1 ],
1368             @dummy =
1369 0           ( map { \$self->{'table'}{$table}{$_}{'default'} } $self->insert_order($table) )
1370 0           [ scalar( @{$_} ) .. scalar( $self->insert_fields($table) ) - 1 ]
1371             ),
1372             ')'
1373             )
1374 0           } @{ $self->{'insert_buffer'}{$table} }
1375             ), (
1376             !$self->{'ON DUPLICATE KEY UPDATE'} ? '' : " \n" . $self->{'ON DUPLICATE KEY UPDATE'} . ' ' . join(
1377             ',',
1378             map {
1379 0           $rq . $_ . $rq . '=VALUES(' . $rq . $_ . $rq . ')'
1380             } sort {
1381 0 0 0       $self->{'table'}{$table}{$b}{'order'} <=> $self->{'table'}{$table}{$a}{'order'}
1382             } grep {
1383 0           $self->{'table'}{$table}{$_}{'array_insert'}
1384             and !$self->{'table'}{$table}{$_}{'no_insert_update'}
1385             and !$self->{'table'}{$table}{$_}{'added'}
1386 0 0         } keys %{ $self->{'table'}{$table} }
1387             )
1388             ),
1389             ';'
1390             )
1391             ),
1392             delete $self->{'insert_buffer'}{$table}
1393 0 0         if scalar @{ $self->{'insert_buffer'}{$table} || [] };
    0          
    0          
1394 0   0       $self->{'insert_block'}{$table} = $self->{'table_param'}{$table}{'insert_by'} || $self->{'insert_by'};
1395 0           $self->{'stat'}{'time'}{'time'} += time - $self->{'insert_buffer_time'}{$table};
1396             psmisc::schedule(
1397             [ $self->{'stat_every'}, $self->{'stat_every'} ],
1398             sub {
1399             #my $per = $self->{'stat'}{'time'}{'time'};
1400 0   0       $self->log(
      0        
1401             'time',
1402             'inserts',
1403             $self->{'stat'}{'time'}{'count'},
1404             'per',
1405             psmisc::human( 'time_period', $self->{'stat'}{'time'}{'time'} ),
1406             'at',
1407             psmisc::human( 'float', $self->{'stat'}{'time'}{'count'} / ( $self->{'stat'}{'time'}{'time'} || 1 ) ),
1408             'rps',
1409             'full',
1410             psmisc::human( 'float', $self->{'stat'}{'time'}{'count'} / ( time - $self->{'stat'}{'time'}{'full'} or 1 ) ),
1411             'rps'
1412             );
1413 0           $self->{'stat'}{'time'} = { 'full' => time };
1414             }
1415 0 0         ) if $self->{'stat_every'};
1416             }
1417             }
1418             #$self->log('dev', 'insert:',@{ $buffer{$table} });
1419 0           return undef;
1420 0   0       };
1421             #}
1422             #$self->{'flush'} ||= $self->{'insert'};
1423             $self->{'flush_insert'} ||= sub {
1424 0     0     my $self = shift;
1425 0           $self->insert_cached(@_);
1426             #pg tsearch
1427             #push( @{ $fulltext{ $table->{$row}{'fulltext'} } }, $rq . $row . $rq ) if $table->{$row}{'fulltext'};
1428 0           if ( 0 and $self->{'driver'} =~ /pg/i and $self->{'use_fulltext'} ) {
1429             for my $tablen ( grep { $_ and $self->{'table_updated'}{$_} } keys %{ $self->{'table_updated'} || {} } ) {
1430             my $table = $self->{'table'}{$tablen};
1431             my (%fulltext);
1432             for my $row ( sort { $table->{$b}{'order'} <=> $table->{$a}{'order'} } keys %$table ) {
1433             push( @{ $fulltext{ $table->{$row}{'fulltext'} } }, $rq . $row . $rq ) if $table->{$row}{'fulltext'};
1434             }
1435             my @do;
1436             #local @_ = map {$self->rquote($_)}grep {$table->{$_}{'fulltext'}} keys %{%$table || {}} or next;
1437             push @do,
1438             "SELECT tsvector_update_trigger($rq$_$rq, ${vq}$self->{'fulltext_config'}${vq}, "
1439             . ( join( ', ', @{ $fulltext{$_} || [] } ) )
1440             . ") FROM $tq$tablen$tq"
1441             for keys %fulltext;
1442             #$self->log('dev', 'ftup',$self->{'table_updated'}{$table},$table, $do);
1443             $self->do(@do);
1444             $self->{'table_updated'}{$tablen} = 0;
1445             }
1446             }
1447 0   0       };
1448             $self->{'insert'} ||= sub {
1449 0     0     my $self = shift;
1450 0           my @ret = $self->insert_cached(@_);
1451 0 0         $self->flush_insert( $_[0] ) if scalar @_ > 1;
1452 0           return @ret;
1453 0   0       };
1454             $self->{'update'} ||= sub {
1455 0     0     my $self = shift;
1456 0   0       my $table = ( shift or $self->{'current_table'} );
1457             #sub update { #v5
1458             #my $self = shift;
1459 0           my ( $by, $values, $where, $set, $setignore, $whereignore ) = @_;
1460             #$self->log('dev','sql_update:', $self->{database}, join ':',@_, "PREUPVAL=",%{$values} );
1461             #$self->log('dev','sql_update:', "[$set],[$setignore]" );
1462 0 0         return unless %{ $self->{'table'}{$table} or {} };
  0 0          
1463 0           $self->{'current_table'} = $table;
1464             #my ( $tq, $rq, $vq ) = sql_quotes();
1465             #$self->log('dev','HIRUN', $table, $self->{'handler_insert'} ,
1466             #$self->log( 'filter', 'f2', $self->{'table_param'}{$table}{'filter'} );
1467             next
1468 0 0 0       if ref $self->{'table_param'}{$table}{'filter'} eq 'CODE'
1469             and $self->{'table_param'}{$table}{'filter'}->( $self, $values );
1470 0 0         $self->{'handler_insert'}->( $table, $values ) if ref $self->{'handler_insert'} eq 'CODE';
1471 0           $self->stem_insert( $table, $values );
1472             #$self->{'handler_insert'}->( $table, \%{$values} ) if $self->{'handler_insert'};
1473 0           local $self->{'handler_insert'} = undef;
1474 0           local $self->{'stem_insert'} = sub { };
  0            
1475 0           local @_;
1476 0 0         $by ||=
1477 0 0         [ grep { $self->{'table'}{$table}{$_}{'primary'} or $self->{'table'}{$table}{$_}{'unique'} }
1478 0   0       keys %{ $self->{'table'}{$table} || {} } ];
1479 0           my $bymask = '^(' . join( ')|(', @$by ) . ')$';
1480 0 0         my $bywhere = join(
1481             ' AND ',
1482             map ( "$rq$_$rq=" . $self->quote( $values->{$_} ),
1483             grep {
1484 0 0 0       %{ $self->{'table'}{$table}{$_} || {} }
  0   0        
      0        
1485             and ( $self->{'table'}{$table}{$_}{'primary'} or $self->{'table'}{$table}{$_}{'unique'} )
1486             and $self->{'table'}{$table}{$_}{'type'} ne 'serial'
1487             and !$self->{'table'}{$table}{$_}{'auto_increment'} #todo mysql
1488             } @$by )
1489             );
1490 0           $set ||= join(
1491             ', ', (
1492             map {
1493             #$self->log('dev','sql_update:', "[$_:$values->{$_}]" );
1494             $rq . $_ . $rq . "=" . $self->quote(
1495             $self->cut( $values->{$_}, $self->{'table'}{$table}{$_}{'length'} )
1496             #$values->{$_}
1497             )
1498             } (
1499 0 0         @_ = grep( ( ( $_ !~ $bymask ) and $_ and %{ $self->{'table'}{$table}{$_} || {} } and defined( $values->{$_} ) ),
1500             keys %$values ), (
1501             @_ ? () : grep {
1502 0 0 0       $_ and %{ $self->{'table'}{$table}{$_} or {} } and defined( $values->{$_} )
  0 0 0        
      0        
1503             } keys %$values
1504             )
1505             )
1506             )
1507             );
1508 0 0         $set = 'SET ' . $set if $set;
1509 0           my $lwhere = $where;
1510 0 0         $where = '' if $where eq 1;
1511 0 0 0       $where = ' AND ' . $where if $where and $bywhere;
1512 0 0 0       $whereignore = ' AND ' . $whereignore if $whereignore and ( $where or $bywhere );
      0        
1513 0           local $_;
1514             #$processor{'out'}{'sql'}
1515 0 0 0       $_ = $self->do(
      0        
      0        
1516             "UPDATE $self->{$self->{'update_options'}} $self->{'IGNORE'} $tq$self->{'table_prefix'}$table$tq $set $setignore WHERE $bywhere $where $whereignore"
1517             )
1518             if ( $set or $lwhere or !$self->{'ON DUPLICATE KEY UPDATE'} )
1519             and ( $bywhere or $where or $whereignore );
1520             #$self->log( 'dev','by', Dumper $by);
1521             #$self->log( 'dev', "WHERE[" . $where . "] BYwhere[" . $bywhere . "] whereignore[$whereignore] ", " UPVAL=", %{$values}, "UPSET=", $set, "RES[$_]" , Dumper $self->{'table'}{$table});
1522             #$processor{'out'}{'hash'}->
1523             #$self->hash($table, { '' => $values } ), #$processor{'out'}{'array'}->($table)
1524             #$self->log( 'dev',"insert_hash run? ", "( !$set or !int($_) ) and !$where");
1525             #$self->log( 'dev',"insert_hash run "),
1526 0 0 0       $self->insert_data( $table, $values ), #$processor{'out'}{'array'}->($table)
      0        
1527             $self->flush_insert($table) if ( !$set or !int($_) ) and !$lwhere;
1528 0           return undef;
1529 0   0       };
1530             $self->{'insert_hash'} ||= sub {
1531 0     0     my $self = shift;
1532 0 0         return $self->insert_data(@_) unless $self->{'driver'} =~ /pg/i;
1533 0   0       my $table = shift || $self->{'current_table'};
1534 0           my $ret;
1535 0           for (@_) {
1536             #$self->log( 'dev',"insert_hash run "),
1537 0           $ret += $self->update( $table, undef, $_ );
1538             }
1539 0           return $ret;
1540 0   0       };
1541             #=z
1542             $self->{'cut'} ||= sub {
1543 0     0     my $self = shift;
1544 0 0         return $_[0] unless $_[1];
1545             #return $_[0] = substr( $_[0], 0, $_[1] - ( ( $self->{'codepage'} eq 'utf-8' and $self->{'driver'} =~ /mysql/ ) ? 2 : 0 ) ),
1546             # ( $self->{'codepage'} eq 'utf-8' ? $_[0] =~ s/[\xD0\xD1]+$// : () );
1547 0           return $_[0] = substr( $_[0], 0, $_[1] );
1548 0   0       };
1549             #=cut
1550             $self->{'insert_data'} ||= sub {
1551 0     0     my $self = shift;
1552             #$self->log('dmp','insertdata=',Dumper(\@_));
1553 0   0       my $table = ( shift or $self->{'current_table'} ); #or $self->{'tfile'}
1554             #$self->log('dev','hash!', $table);
1555             #$processor{'out'}{'hash'} ||= sub {
1556             #my $self = shift;
1557             #my $table = ( shift or $self->{'tfile'} );
1558 0           for my $hash (@_) {
1559             #$self->log('dev','hash1=',Dumper($hash));
1560             #for my $col ( keys %$hash ) {
1561             #$self->log('dev','hash col',$col );
1562             #$self->log('dev','hash col2',$col , $hash->{$col}{'path'});
1563             #$self->log('dev','hash col2',$col , $hash->{$col}{'path'});
1564 0 0         next if !$hash;
1565             #$self->log('dev',"def for $_", $self->{'table'}{$table}{$_}{'array_insert'}),
1566             #$self->log('dev',"hash[$hash]",Dumper($hash) ) if ref $hash eq 'REF';
1567 0           $hash->{$_} = (
1568             $self->{'table'}{$table}{$_}{'default_insert'}
1569             or ( $self->{'table'}{$table}{$_}{'array_insert'} ? $self->{'table'}{$table}{$_}{'default'} : undef )
1570             ),
1571             #$self->log('dev','hash def',$_, $hash->{$_}),
1572 0   0       for grep { !defined $hash->{$_} } #$self->{'table'}{ $table }{$_}{'array_insert'} and
  0            
1573             keys %{ $self->{'table'}{$table} };
1574             #$self->log('dev','hash next insert_min', $hash->{$col}, grep { $self->{'table'}{$table}{$_}{'insert_min'} and $hash->{$_} } keys %{ $self->{'table'}{$table} }),
1575             #$self->log('dev','hash2=',Dumper($hash),
1576             #grep { $self->{'table'}{$table}{$_}{'insert_min'} and !$hash->{$_} } keys %{ $self->{'table'}{$table} }
1577             #),
1578             #$self->log('dev','SKIP'),
1579             next if #!$hash->{$col}
1580             #and !(grep { $self->{'table'}{$table}{$_}{'insert_min'} } keys %{ $self->{'table'}{$table} })
1581             #or
1582 0 0         grep { $self->{'table'}{$table}{$_}{'insert_min'} and !$hash->{$_} } keys %{ $self->{'table'}{$table} };
  0 0          
  0            
1583             #$self->log('dev','hash1');
1584             #########not here
1585 0           $self->handler_insert0( $table, $hash );
1586             #if $self->{'handler_insert0'};
1587             #########not here
1588             #$self->log('dev','hash3=',Dumper($hash));
1589             #( $self->{'filter_handler'} ? $self->{'filter_handler'}->($hash) : () ), next
1590             #if grep { $self->{'table'}{$table}{$_}{'skip_mask'} and $hash->{$_} =~ /$self->{'table'}{ $table }{$_}{'skip_mask'}/i }
1591             #keys %{ $self->{'table'}{$table} };
1592             #$self->log('filter', 'f1', $self->{'table_param'}{$table}{'filter'});
1593             next
1594 0 0 0       if ref $self->{'table_param'}{$table}{'filter'} eq 'CODE'
1595             and $self->{'table_param'}{$table}{'filter'}->( $self, $hash );
1596             #$self->handler_insert( $table, $hash );
1597 0           $self->handler_insert( $table, $hash ); # if $self->{'handler_insert'};
1598 0           $self->stem_insert( $table, $hash );
1599             #$self->log('dev',"lenCUT[$hash->{$_}]"),
1600 0 0 0       $self->cut( $hash->{$_}, $self->{'table'}{$table}{$_}{'length'} )
1601             #$hash->{$_} = substr( $hash->{$_}, 0, $self->{'table'}{$table}{$_}{'length'} - ( $self->{'codepage'} eq 'utf-8' ? 2 : 0 ) ),($self->{'codepage'} eq 'utf-8' ? $hash->{$_} =~ s/[\xD0\xD1]+$// : ()),
1602             #$self->log('dev',"lenCUT[$self->{'codepage'}][$hash->{$_}]"),
1603 0           for grep {
1604 0           ( $self->{'table'}{$table}{$_}{'type'} eq $self->{'char_type'} )
1605             and $self->{'table'}{$table}{$_}{'length'}
1606             and length( $hash->{$_} ) >
1607             ( $self->{'table'}{$table}{$_}{'length'} )
1608             } keys %{ $self->{'table'}{$table} };
1609             #$processor{'out'}{'array'}->
1610             #$self->log('dev','ic from here=');
1611 0           local $self->{'table'}{$table} = $self->{'table'}{$table};
1612             #$self->log('dev', $self->{'table'}{$table});
1613 0           my $chanded;
1614             #$self->log('dev', 'set array_insert', $table, $_, ),
1615             (
1616 0           ++$chanded == 1
1617             ? (
1618             #$self->log('dev', 'flush on change', $table, $_),
1619             $self->flush_insert($table)
1620             )
1621             : ()
1622             ),
1623             $self->{'table'}{$table}{$_}{'array_insert'} = 1
1624 0 0         for grep {
1625 0           defined $hash->{$_}
1626             and length $hash->{$_}
1627             #and ($hash->{$_} ne $self->{'table'}{$table}{$_}{'default'} )
1628 0 0 0       and keys %{ $self->{'table'}{$table}{$_} } and !$self->{'table'}{$table}{$_}{'array_insert'}
      0        
1629             } keys %{ $self->{'table'}{$table} };
1630             #$self->log('dmp','insertdata2($table)=',Dumper(\@_));
1631 0           $self->insert_cached(
1632             $table,
1633             \@{$hash}{
1634 0           $self->insert_order($table)
1635             #sort { $self->{'table'}{$table}{$b}{'order'} <=> $self->{'table'}{$table}{$a}{'order'} }
1636             #grep { $self->{'table'}{$table}{$_}{'array_insert'} } keys %{ $self->{'table'}{$table} }
1637             }
1638             );
1639             #########not here
1640 0           $self->handler_insert2( $table, $hash );
1641             #########not here
1642             }
1643             #}
1644 0           return undef;
1645 0   0       };
1646             $self->{'insert_hash_hash'} ||= sub {
1647 0     0     my $self = shift;
1648 0   0       my $table = ( shift or $self->{'current_table'} ); #or $self->{'tfile'}
1649 0           for my $hash (@_) { $self->insert_hash( $table, values %$hash ); }
  0            
1650 0           return undef;
1651 0   0       };
1652             $self->{'q_file'} ||= sub {
1653 0     0     my $self = shift;
1654 0   0       my $table = shift || $self->{'current_table'};
1655 0           my $search_str = shift;
1656 0           my %tparam;
1657             #return () if $self->{'sphinx'};
1658             #if ( $self->{'table'}{$table}{'name'} and $self->{'table'}{$table}{'ext'} and $search_str =~ /^\s*(\S+)\.+(\S+)\s*$/ ) {
1659 0 0 0       if ( $self->{'table'}{$table}{'name'}
    0 0        
      0        
1660             and $self->{'table'}{$table}{'ext'}
1661             and $search_str =~ m{^([^/|"]+[^\s/|"])\.([^/\."|]+)$} ) #"
1662             {
1663 0           %tparam = ( 'name' => $1, 'ext' => $2 );
1664             } elsif ( $self->{'table'}{$table}{'tiger'} and $search_str =~ /^\s*([A-Z0-9]{39})\s*$/i ) {
1665 0           %tparam = ( 'tiger' => uc $1 );
1666             }
1667 0           return %tparam;
1668 0   0       };
1669             $self->{'where_body'} ||= sub {
1670 0     0     my $self = shift;
1671 0           my ( $param_orig, $param_num, $table, $after ) = @_;
1672 0 0         my $param = { %{ $param_orig || {} } };
  0            
1673             #my $param = $param_orig;
1674 0   0       $table ||= $self->{'current_table'};
1675 0           my ( $search_str_add, $ask, $close );
1676             #$self->log('dev', 'where_body', 1, $table, %$param, $self->{'current_table'});
1677 0           my $questions = 0;
1678 0           map ++$questions, grep defined( $param->{ $_ . $param_num } ),
1679 0 0         @{ $config{'user_param_founded'} || [ 'q', keys %{ $self->{'table'}{$table} } ] };
  0            
1680             #$self->log('dev', 'recstop' , $param_num),
1681 0 0 0       return if ( $param_num and !$questions ) or ++$self->{'rec_stop'} > 20;
      0        
1682 0           my $first = 1;
1683 0           my $local_cond = 0;
1684             #my ( $tq, $rq, $vq ) = sql_quotes();
1685             #$self->log('dev', 'where_body', 1.1, $param->{ 'q' . $param_num });
1686 0   0       while ( defined( $param->{ 'q' . $param_num } ) and $param->{ 'q' . $param_num } =~ s/(\w+\S?[=:](?:".+?"|\S+))// ) {
1687             #$self->log('dev', 'where_body', 1.2, $param->{ 'q' . $param_num }, $1);
1688             #$self->log('dev', 'where_body selected', $1);
1689             #$self->log('dev', 'where_body selected',
1690             #get_params_one( $param, $1 );
1691 0           local $_ = $1;
1692 0           s/^(\S+):/$1=/;
1693 0           my $lparam = get_params_one( undef, $_ );
1694 0           $lparam->{$_} =~ s/^"|"$//g, $param->{$_} = $lparam->{$_} for keys %$lparam;
1695             #$self->log('dev', 'where_body selected', $1, %$param); #%$lparam,
1696             }
1697             #$self->log('dev', 'where_body', 2);
1698 0           for my $preset ( $param->{ 'q' . $param_num } =~ /:(\S+)/g ) {
1699 0           for my $sets ( keys %{ $config{'preset'} } ) {
  0            
1700 0 0         if ( $config{'preset'}{$sets}{$preset} ) {
1701 0           $param->{ 'q' . $param_num } =~ s/:$preset//;
1702 0           for ( keys %{ $config{'preset'}{$sets}{$preset}{'set'} } ) {
  0            
1703 0 0         $param->{ $_ . $param_num } .=
1704             ( $param->{ $_ . $param_num } ? ' ' : '' ) . $config{'preset'}{$sets}{$preset}{'set'}{$_};
1705             }
1706             }
1707             }
1708             }
1709 0           my $search_str = $param->{ 'q' . $param_num };
1710             #$self->log( 'dev', 'where_body', 3, $search_str, $param_num );
1711 0 0         my $glueg = $param->{ 'glueg' . $param_num } eq 'or' ? ' OR ' : ' AND ';
1712 0 0         my $gluel = $param->{ 'gluel' . $param_num } eq 'or' ? ' OR ' : ' AND ';
1713 0 0 0       $glueg = ' XOR ' if $self->{'enable_xor_query'} and $param->{ 'glueg' . $param_num } eq 'xor';
1714 0 0 0       $gluel = ' XOR ' if $self->{'enable_xor_query'} and $param->{ 'gluel' . $param_num } eq 'xor';
1715 0 0 0       if ( my ($days) = $param->{ 'search_days' . $param_num } =~ /(\d+)/ and $1 and %{ $self->{'table'}{$table}{'time'} or {} } )
  0 0 0        
1716             {
1717 0 0         $ask .= " " . ( $self->{'no_column_prepend_table'} ? () : "$tq$self->{'table_prefix'}$table$tq." ) . "$rq" . "time$rq ";
1718 0 0         if ( $param->{ 'search_days_mode' . $param_num } eq 'l' ) { $ask .= '<'; }
  0            
1719 0           else { $ask .= '>'; }
1720 0           $days = int( time() ) - $days * 24 * 60 * 60;
1721 0 0         $ask .= '= ' . ( $self->{'sphinx'} ? $days : $self->squotes($days) );
1722             }
1723             #$self->log('dev', 'online1', Dumper($param));
1724 0 0 0       if ( !$self->{'no_online'} and defined( $param->{ 'online' . $param_num } ) ) {
1725 0 0         if ( $param->{ 'online' . $param_num } eq 'on' ) { $param->{ 'online' . $param_num } = $config{'online_minutes'}; }
  0            
1726             #$self->log('dev', 'online2', Dumper($param));
1727 0 0         if ( $param->{ 'online' . $param_num } > 0 ) {
1728 0           $param->{ 'live' . $param_num } = int( time() ) + $self->{'timediff'} - int( $param->{ 'online' . $param_num } ) * 60;
1729 0           $param->{ 'live_mode' . $param_num } = 'g';
1730             #$self->log('dev', $param->{ 'live' . $param_num });
1731             }
1732             }
1733 0 0 0       if (
      0        
      0        
      0        
      0        
1734             $self->{'path_complete'}
1735             and $param->{ 'path' . $param_num }
1736             and !( $param->{ 'path' . $param_num } =~ /^[ !\/\*]/ )
1737             and ( $param->{ 'path' . $param_num } ne 'EMPTY' )
1738             and !( (
1739             !$self->{'no_regex'}
1740             and
1741             ( $param->{ 'path' . $param_num } =~ /^\s*reg?e?x?p?:\s*/i or $param->{ 'path' . '_mode' . $param_num } =~ /[r~]/i )
1742             )
1743             )
1744             )
1745             { # bad idea ?
1746 0           $search_str_add .= ' /' . $param->{ 'path' . $param_num } . '/ ';
1747 0           delete $param->{ 'path' . $param_num };
1748             }
1749 0 0         for my $item ( (
  0            
1750             sort {
1751 0 0 0       $self->{'table'}{$table}{$b}{'weight'} <=> $self->{'table'}{$table}{$a}{'weight'}
      0        
1752             || $self->{'table'}{$table}{$b}{'order'} <=> $self->{'table'}{$table}{$a}{'order'}
1753             } grep {
1754 0           $self->{'nav_all'}
1755             or $self->{'table'}{$table}{$_}{'nav_num_field'}
1756             or $self->{'table'}{$table}{$_}{'nav_field'}
1757             or $self->{'table'}{$table}{$_}{'nav_hide'}
1758 0           } keys %{ $self->{'table'}{$table} }
1759             ),
1760             @{ $self->{'table_param'}{$table}{'join_fields'} }
1761             )
1762             {
1763             #$self->log('dev', 'where_body', 4, $item);
1764             next
1765 0 0 0       if $self->{'no_index'}
      0        
      0        
      0        
1766             or $self->{'ignore_index'}
1767             or $self->{'table_param'}{$table}{'no_index'}
1768             or $self->{'table_param'}{$table}{'ignore_index'}
1769             or $param->{ $item . $param_num } !~ /\S/;
1770             #$self->log('dev', 'where_body', 4, $item);
1771 0           my $lask;
1772 0 0         ++$local_cond, $lask .= $gluel if $ask;
1773 0           my $pib = $param->{ $item . $param_num };
1774 0           $pib =~ s/^\s*|\s*$//g;
1775 0           my ( $group_not, $group_not_close ); #,
1776             #$self->log('dev', 'where_body', 5, $item, $self->{$item}, $_, 'C=', $config{$item});
1777 0           $pib =~ s/\:$_(\W|$)/$config{$item}{$_}{'to'}$1/g and ++$group_not
1778 0 0 0       for grep { defined $config{$item}{$_}{'to'} } keys %{ ref $config{$item} eq 'HASH' ? $config{$item} : {} };
  0            
1779             #for grep {defined $self->{$item}{$_}{'to'}} keys %{ $self->{$item} or {}};
1780 0 0         next if $pib eq '';
1781 0           my ( $brstr, $space );
1782 0 0 0       if ( $self->{'table'}{$table}{$item}{'no_split_space'}
      0        
      0        
1783             or ( !$self->{'no_regex'} and ( $pib =~ /\s*reg?e?x?p?:\s*/ or $param->{ $item . '_mode' . $param_num } =~ /[r~]/i ) ) )
1784             {
1785             #$self->log('dev', 'SPA') ;
1786 0           $space = '\s+';
1787             } else {
1788 0           $brstr = '|\s+';
1789             }
1790             #$brstr = $space . '\&+' . $space . '|' . '\|+' . '|(\s+AND\s+)|\s+OR\s+' . $brstr;
1791 0           $brstr = $space . '\&+' . $space . '|' . $space . '\|+' . $space . '|(\s+AND\s+)|\s+OR\s+' . $brstr;
1792 0           my $num_cond = 0;
1793 0           my $next_cond;
1794             my $llask;
1795 0   0       do {
1796 0           my ( $pi, $cond );
1797 0           $cond = $next_cond;
1798             #$self->log('dev', "split[$pib] with [($brstr)]");
1799 0 0         if ( $pib =~ /($brstr)/ ) { ( $pib, $pi, $next_cond ) = ( $', $`, $1 ); }
  0            
1800 0           else { $pi = $pib, $pib = ''; }
1801 0 0         if ( $num_cond++ ) {
1802             #$self->log('dev', "andf1, $llask");
1803 0 0 0       if ( $cond =~ /(and)|\&+/i ) { $llask .= ' AND '; }
  0 0          
    0          
1804 0           elsif ( $self->{'enable_xor_query'} and $cond =~ /(xor)/i ) { $llask .= ' XOR '; } #too slow
1805 0           elsif ( $cond =~ /(or)|\|+|\s+|^$/i ) { $llask .= ' OR '; }
1806             #$self->log('dev', "andf2, $llask");
1807             }
1808             #$self->log('dev', "$pib, $pi, $next_cond, $llask");
1809 0 0 0       my $not = 1 if ( !$self->{'no_slow'} or $self->{'table'}{$table}{$item}{'fast_not'} ) and ( $pi =~ s/^\s*[\!\-]\s*//g );
      0        
1810 0 0         $llask .= ' NOT ' . ( $group_not ? ( ++$group_not_close, ' ( ' ) : '' ) if $not;
    0          
1811             #$self->log('dev', "not1 $llask");
1812 0 0         if ( $self->{'table_param'}{$table}{'name_to_base'}{$item} ) {
1813             #$self->log('dev', "here", $self->{'table_param'}{$table}{'name_to_base'}{$item});
1814             #$llask .= ' ' . $tq . $self->{'table_prefix'} . $self->{'table_param'}{$table}{'name_to_base'}{$item} . $tq . ' ';
1815 0           $llask .= ' ' . $self->{'table_param'}{$table}{'name_to_base'}{$item} . ' ';
1816             } else {
1817 0 0         $llask .=
1818             " " . ( $self->{'no_column_prepend_table'} ? () : "$tq$self->{'table_prefix'}$table$tq." ) . "$rq$item" . "$rq ";
1819             }
1820 0           my ($dequote_); #, $dequotesl
1821             #$self->log('dev', !$self->{'no_regex'});
1822 0 0 0       if ( !$self->{'no_regex'}
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
1823             and ( $pi =~ s/^\s*reg?e?x?p?:\s*//ig or $param->{ $item . '_mode' . $param_num } =~ /[r~]/i ) )
1824             {
1825 0           $llask .= ' REGEXP ';
1826             #++$dequotesl;
1827             } elsif ( !$self->{'no_soundex'}
1828             and ( $pi =~ s/^\s*sou?n?d?e?x?:\s*//ig or $param->{ $item . '_mode' . $param_num } =~ /[s@]/i ) )
1829             {
1830 0           $llask .= ' SOUNDS LIKE ';
1831             } elsif ( $pi =~ /[*?]/ ) {
1832 0           $pi =~ s/%/\\%/g;
1833 0 0         $pi =~ s/_/\\_/g and ++$dequote_;
1834 0           $pi =~ tr/*?/%_/;
1835 0 0 0       next if $self->{'no_empty'} and ( $pi !~ /\S/ or $pi =~ /^\s*[%_]+\s*$/ );
      0        
1836             #$self->log('dev', 'pi_:', $pi);
1837 0           $llask .= ' LIKE ';
1838             }
1839             #} else {
1840 0           elsif ( $param->{ $item . '_mode' . $param_num } =~ /notnull/i ) { $llask .= 'IS NOT NULL'; next; }
  0            
1841 0           elsif ( $param->{ $item . '_mode' . $param_num } =~ /null/i ) { $llask .= 'IS NULL'; next; }
  0            
1842 0 0         elsif ( $param->{ $item . '_mode' . $param_num } =~ /[g>]/i ) { $llask .= ( $not ? '<' : '>' ) . '= '; }
1843 0 0         elsif ( $param->{ $item . '_mode' . $param_num } =~ /[l<]/i ) { $llask .= ( $not ? '>' : '<' ) . '= '; }
1844 0           else { $llask .= '= '; }
1845             #}
1846 0           $pi =~ s/(^\s*)|(\s*$)//g;
1847 0 0         $pi = psmisc::human( 'number_k', $pi ) if $item eq 'size';
1848 0           $work{ 'bold_' . $item } .= ' ' . $pi;
1849 0 0 0       if ( !( $self->{'sphinx'} and $self->{'table'}{$table}{$item}{'nav_num_field'} and $pi =~ /^\d+$/ ) ) {
      0        
1850 0 0         $pi = ( $pi ne 'EMPTY' ? $self->squotes($pi) : $self->squotes('') );
1851             }
1852 0 0         $pi =~ s|\\_|\_|g if $dequote_;
1853             #$self->log('dev', '$pi:', $pi, $dequotesl);
1854             #$pi =~ s|\\{2}|\\|g if $dequotesl;
1855             #$self->log('dev', '$pi a:', $pi);
1856 0           $llask .= $pi;
1857             #$self->log('dev', '$llask:', $llask);
1858             } while ( $pib and $num_cond < 50 );
1859             #$self->log('dev', '1 $llask:', $llask);
1860 0           $llask .= " ) " x $group_not_close;
1861 0           $group_not_close = 0;
1862 0 0         $lask .= ( $num_cond > 1 ? ' ( ' : '' ) . $llask . ( $num_cond > 1 ? ' ) ' : '' );
    0          
1863             #$self->log('dev', '1 $lask:', $lask);
1864 0   0       $ask .=
1865             ( ( !$self->{'no_slow'} or $self->{'table'}{$table}{$item}{'fast_not'} )
1866             and $param->{ $item . '_mode' . $param_num } =~ /[n!]/i ? ' NOT ' : ' ' )
1867             . $lask;
1868             #$self->log('dev', '1 $ask:', $ask);
1869             }
1870 0           $work{'search_str'} .= ' ' . $search_str . ' ' . $search_str_add;
1871             #$self->log('dev', 'Sstr', $work{'search_str'});
1872 0 0 0       if ( $search_str =~ /\S/ or $search_str_add ) {
1873 0 0 0       unless ( $param->{'page'} > 1 or $param->{'order'} or $param->{'no_querystat'} ) {
      0        
1874             #$self->log('dev', '2 $ask:', $search_str);
1875             #$self->dump_cp();
1876 0           ++$work{'query'}{$search_str};
1877 0           map { ++$work{'word'}{$_} } grep $_, split /[\W_]+/, $search_str; #if $self->{'codepage'} ne 'utf-8';
  0            
1878             }
1879             #$self->log('dev', '2 $ask:', $ask, Dumper %work);
1880 0 0         ++$local_cond, $ask .= $gluel if $ask;
1881             #$self->log('dev', '3 $ask:', $ask, $search_str, $search_str_add);
1882 0 0 0       $param->{ 'adv_query' . $param_num } = 'on'
      0        
1883             if $search_str =~ /\S+\*+\s*/
1884             or $search_str =~ /(^|\s+)(([+\-><~]+\()|\")[^"()]*\S+\s+\S+[^"()]*[\"\)]($|\s+)/
1885             or $search_str =~ /(^|\s+)[\~\+\-\<\>]\S+/;
1886 0 0 0       $search_str =~ s/(\S+)/\+$1/g
      0        
1887             if $param->{ 'adv_query' . $param_num } eq 'on'
1888             and !( $search_str =~ /((^|\s)\W+\S)|\S\W+(\s|$)/ )
1889             and $search_str =~ /\s/;
1890 0 0         $ask .= ( $search_str =~ s/^\s*\!\s*// ? ' NOT ' : '' );
1891             #!
1892 0 0 0       if ( !$self->{'use_q_file_fallback'} and my %tparam = $self->q_file( $table, $search_str ) ) {
    0 0        
      0        
      0        
      0        
      0        
1893             #$search_str =~ /^\s*(\S+)\.+(\S+)\s*$/ and $self->{'table'}{$table}{'name'} and $self->{'table'}{$table}{'ext'} ) {
1894             #my %tparam = ( 'name' => $1, 'ext' => $2 );
1895 0           $ask .= ' ( ' . $self->where_body( \%tparam, undef, $table ) . ' ) ';
1896             } elsif ( !$self->{'sphinx'}
1897             and !$self->{'no_slow'}
1898             and $search_str =~ /^\s*\*+\S+/
1899             and $self->{'table'}{$table}{'path'}
1900             and $self->{'table'}{$table}{'name'}
1901             and $self->{'table'}{$table}{'ext'} )
1902             {
1903 0           my %tparam = ( 'path' => '/' . $search_str, 'name' => $search_str, 'ext' => $search_str, 'gluel' => 'or' );
1904 0           $ask .= ' ( ' . $self->where_body( \%tparam, undef, $table ) . ' ) ';
1905             #!
1906             } else {
1907             #my $search_str = $search_str . $search_str_add;
1908             #$self->log('ss', $search_str);
1909 0           $search_str .= $search_str_add;
1910 0 0         $self->{'handler_search_str'}->( $table, \$search_str ) if ref $self->{'handler_search_str'} eq 'CODE';
1911 0           my $search_str_stem = $self->stem($search_str)
1912 0 0         if grep { $self->{'table'}{$table}{$_}{'stem'} } keys %{ $self->{'table'}{$table} };
  0            
1913             #$self->log('ss1',$param_num, $search_str);
1914 0 0 0       local $param->{ 'adv_query' . $param_num } = 'on'
1915             if $self->{'ignore_index'}
1916             or $self->{'table_param'}{$table}{'ignore_index'};
1917             #$self->log( 'dev', 'where_body', 6, $search_str, $table, $ask, grep { $self->{'table'}{$table}{$_}{'fulltext'} } keys %{ $self->{'table'}{$table} } );
1918 0 0 0       if ( ( #!$self->{'use_sphinx'} and
      0        
      0        
1919             !$param->{ 'adv_query' . $param_num } and (
1920             $self->{'ignore_index_fulltext'} or !grep {
1921             $self->{'table'}{$table}{$_}{'fulltext'}
1922             or ( $self->{'sphinx'} and $self->{'table'}{$table}{$_}{'sphinx'} )
1923             } keys %{ $self->{'table'}{$table} }
1924             )
1925             )
1926             or !$self->{'match'}
1927             )
1928             {
1929             #my $sl = $self->squotes( '%' . $search_str . '%' );
1930             #$self->log( 'dev', 'where_body', 7, $search_str, $table , $ask);
1931 0 0 0       $_ = join(
1932             ' OR ',
1933             #map{ "$rq$_$rq LIKE $sl"} grep{ defined $self->{'table'}{$table}{$_}{'fulltext'}} keys %{ $self->{'table'}{$table} }
1934             map {
1935 0 0 0       "$rq$_$rq LIKE "
1936             . $self->squotes( ( (
1937             !$self->{'no_slow'}
1938             and $self->{'table'}{$table}{$_}{'like_bef'}
1939             || $self->{'table_param'}{$table}{'like_bef'}
1940             || $self->{'like_bef'}
1941             ) ? '%' : ''
1942             )
1943             . $search_str . '%'
1944             )
1945             } grep {
1946 0           $self->{'table'}{$table}{$_}{'q'} || $self->{'table'}{$table}{$_}{'nav_field'}
1947             and !$self->{'table'}{$table}{$_}{'q_skip'}
1948 0           } keys %{ $self->{'table'}{$table} }
1949             );
1950             #$self->log( 'dev', 'where_body', 8, $_ , $ask);
1951 0 0         $ask .= ' ( ' . $_ . ' ) ' if $_;
1952             #$self->log( 'dev', 'where_body', 9, $search_str, $ask );
1953             } else {
1954             #$self->log( 'dev', 'where_body', 10, $search_str );
1955 0           $ask .= $self->match( $param, $param_num, $table, $search_str, $search_str_stem );
1956             }
1957             }
1958             }
1959             #$self->log( 'dev', 'ask1:', $ask);
1960             #$ask = ( $local_cond > 1 ? ' ( ' : '' ) . $ask . ( $local_cond>1 ? ' ) ' : '' );
1961 0 0 0       if ( !$self->{'sphinx'} and $local_cond > 1 ) { $ask = ' ( ' . $ask . ' ) '; }
  0            
1962             #$self->log( 'dev', 'ask2:', $ask);
1963 0 0 0       $ask = $glueg . $ask if $after and $ask;
1964             #$self->log( 'dev', 'ask3:', $ask);
1965             #$self->log( 'dbg', $local_cond, ' lret: ', $ask . ( $ask and $close ? ' ) ' x $close : '' ), 'after=', $after, '$glueg', $glueg, $param->{'search_prev'}, );
1966             #"RET=[$ask]"
1967             #
1968             #. ( $ask and $close ? ' ) ' x $close : '' )
1969             #. $self->where_body(
1970             #$param, $param_num + ( defined($param_num) ? 1 : ( $param->{'search_prev'} ? 0 : 1 ) ),
1971             #$table, ( $ask ? 1 : 0 )
1972             #)
1973             #
1974             #);
1975             return
1976 0 0 0       $ask
    0          
    0          
1977             . ( $ask and $close ? ' ) ' x $close : '' )
1978             . $self->where_body( $param, $param_num + ( defined($param_num) ? 1 : ( $param->{'search_prev'} ? 0 : 1 ) ),
1979             $table, ( $ask ? 1 : 0 ) );
1980 0   0       };
1981             $self->{'where'} ||= sub {
1982             #sub where {
1983 0     0     my $self = shift;
1984 0           my ( $param, undef, $table ) = @_;
1985             #my $where = sql_where_body(@_);
1986 0           $self->{'rec_stop'} = 0;
1987 0           my $where = $self->where_body(@_);
1988             #$self->log( 'dbg', "WHERE($table):[$where]", Dumper(\@_) , "$self->{'cp_in'} -> $self->{'codepage'} [extra=$self->{'table_param'}{$table}{'where_extra'}]");
1989             #return ' WHERE ' . scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $where ) if $where;
1990 0 0         if ( $self->{'table_param'}{$table}{'where_extra'} ) {
1991             #$self->log( 'dbg','where_extra', $self->{'table_param'}{$table}{'where_extra'});
1992 0 0         $where .= (' AND ') if length $where;
1993 0           $where .= $self->{'table_param'}{$table}{'where_extra'};
1994             }
1995 0 0         return ' WHERE ' . $where if $where;
1996 0           return undef;
1997 0   0       };
1998             $self->{'count'} ||= sub {
1999             #sub query_count {
2000 0     0     my $self = shift;
2001 0           my ( $param, $table ) = @_;
2002             #my ( $tq, $rq, $vq ) = sql_quotes();
2003 0           $self->limit_calc( $self, $param, $table );
2004             return undef
2005 0 0 0       if $self->{'query_count'}{$table}++
      0        
2006             or $self->{'ignore_index'}
2007             or $self->{'table_param'}{$table}{'ignore_index'};
2008 0           my @ask;
2009 0 0         $param->{'count_f'} = 'on' if $self->{'page'} eq 'rnd';
2010 0 0         push( @ask, ' COUNT(*) ' ) if $param->{'count_f'} eq 'on';
2011 0           push( @ask, " SUM($tq$table$tq.$rq$_$rq) " )
2012 0   0       for grep(
2013             ( ( $self->{'allow_count_all'} or $self->{'table'}{$table}{$_}{'allow_count'} ) and $param->{ 'count_' . $_ } eq 'on' ),
2014             sort keys %{ $self->{'table'}{$table} } );
2015 0 0         if (@ask) {
2016 0           my %tmp_para = %$param;
2017 0           local $self->{'dbirows'};
2018 0           delete $tmp_para{'online'};
2019 0           my $where = $self->where( \%tmp_para, undef, $table );
2020 0 0 0       return unless $self->{'allow_null_count'} or $where;
2021 0           my $from = join ' ', $tq . $self->{'table_prefix'} . $table . $tq, $self->join_what( undef, $param, $table );
2022 0           my $req = ' SELECT ' . join( ' , ', @ask ) . " FROM $from $where ";
2023 0           psmisc::flush();
2024             #$self->log( 'dmp', 'query:[', @_, '] = ', scalar @hash, ' per', psmisc::human( 'time_period', $tim->() ), 'err=',$self->err() );
2025 0           @ask = values %{ $self->query($req)->[0] };
  0            
2026             #@ask = values %{ $self->line($req) };
2027 0 0         $self->{'stat'}{'found'}{'files'} = pop(@ask) if $param->{'count_f'} eq 'on';
2028 0   0       for (
2029 0           grep( ( $self->{'table'}{$table}{$_}{'allow_count'} and $param->{ 'count_' . $_ } eq 'on' ),
2030             sort keys %{ $self->{'table'}{$table} } )
2031             )
2032             {
2033 0           my $t = pop(@ask);
2034 0 0         $self->{'stat'}{'found'}{$_} = $t if $t;
2035             }
2036             }
2037 0           $self->{'calc_count'}->( $self, $param, $table );
2038 0           return undef;
2039 0   0       };
2040             $self->{'can_select'} ||= sub {
2041 0     0     my $self = shift;
2042 0           my ( $param, $table, ) = @_;
2043 0           my $where = $self->where( $param, undef, $table );
2044 0 0         return $where if $where;
2045 0 0 0       return '0E0' if $self->{'use_sphinx'} and $self->{'sphinx_dbi'} and length $param->{'q'};
      0        
2046 0   0       };
2047             $self->{'select'} ||= sub {
2048 0     0     my $self = shift;
2049 0           my ( $table, $param, $opt ) = @_;
2050 0   0       $opt ||= {};
2051 0           $self->{'current_table'} = $table;
2052             #$self->log( 'dbg', "SELECTR[$self->{'sphinx'}]", ,Dumper($param));
2053             #$self->log( 'dbg', "$self->{'cp_in'} -> $self->{'codepage'}");
2054             #return ' WHERE ' . scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $where ) if $where;
2055             #$self->log( 'dbg', 'q1', scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $self->select_body( $self->where($param), $param, $table ) ));
2056             #$self->log( 'dbg', 'q2', $self->select_body( $self->where($param, undef, $table), $param, $table ) );
2057             #$self->log( 'dbg', 'q3', $self->where($param));
2058 0           my $select;
2059 0           my $ids = [];
2060 0           my $idsh = {};
2061 0           my %id;
2062 0           my $ret = [];
2063 0           $self->{'founded_max'} = $self->{'dbirows'} = 0;
2064             #my ($fail_q, $fail_n);
2065 0           my @fail;
2066             my @selects;
2067 0           my $file_fallback;
2068 0           my $n;
2069             my $post_process = sub ($) {
2070 0           my ($ret) = @_;
2071 0           for my $r (@$ret) {
2072 0 0 0       $r->{$_} ||= $idsh->{ $r->{'id'} }{$_} for keys %{ $idsh->{ $r->{'id'} } || {} };
  0            
2073             #$self->log( 'dev123', Dumper $r, );
2074             }
2075 0           @$ret = sort { $idsh->{ $a->{'id'} }{'n'} <=> $idsh->{ $b->{'id'} }{'n'} } @$ret;
  0            
2076             #@$ret = sort { $ids{ $a->{'weight'} } <=> $ids{ $b->{'weight'} } } @$ret;
2077             #$self->log( 'dev124', Dumper $ret );
2078             #$self->log( 'devFail', Dumper \@fail);
2079 0           for (@fail) {
2080 0 0         next if scalar @$ret < $_->{'n'};
2081 0           $ret->[ $_->{'n'} ]{'__fulltext_fail'} = $_->{'q'};
2082             #$self->log( 'setFail', $_->{'n'});
2083             }
2084 0           @fail = ();
2085 0           };
2086             my $do_select = sub {
2087             #my ($s, $ids) = @_;
2088             #$self->log('do_select', Dumper \@_);
2089             # $self->log('devids', Dumper $ids);
2090 0           my $count;
2091 0           for my $s (@_) {
2092             #my ( $select, $ids );
2093 0           my ($select);
2094             #( $select, $ids ) = $s->() if psmisc::is_code $s;
2095 0           my ( $count_add, $idst );
2096 0 0         ( $select, $idst, $count_add ) = $s->() if psmisc::is_code $s;
2097 0 0         if ( psmisc::is_array_size $idst) {
2098 0           $ids = $idst;
2099             #$self->log('do_select', $count_add, $self->{'limit_offset'}, $self->{'sphinx_dbi'}{'limit_offset'});
2100 0   0       my $nn = $self->{'sphinx_dbi'}{'limit_offset'} || $self->{'limit_offset'};
2101 0   0       $idsh = { map { $_->{'n'} //= ++$nn; $_->{'id'} => $_ } @$ids };
  0            
  0            
2102             }
2103 0           $count += $count_add;
2104 0 0         ( $select, ) = $s if psmisc::is_hash $s;
2105 0           local $self->{'limit_body'} = sub { }
2106 0 0         if psmisc::is_array_size $ids;
2107             #$self->log('select extracted:', $s, $select, Dumper $param);
2108             #my $idsh = {};
2109 0 0         if ( psmisc::is_hash($select) ) {
2110 0           for my $s ( sort { $select->{$a} <=> $select->{$b} } keys %$select ) {
  0            
2111             #$self->log('r', $s, ':', Dumper $select->{$s});
2112 0           my $r;
2113             #sleep 2;
2114 0 0         $r =
2115             $self->{'shard_dbis'}{ $select->{$s} }
2116             ->query( scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $self->select_body( $s, $param ) ) )
2117             if $s;
2118 0 0         next unless $r;
2119             # map {
2120             #$self->log('r1', Dumper $idsh->{$_->{id}});
2121             # $_->{id} //= psmisc::join_url($_) } @$r; #unless $self->{'use_sphinx'};
2122 0           for my $l (@$r) {
2123             #$self->log('r1',$l, $idsh->{$l->{id}});
2124             #$l->{$_} ||= $idsh->{$l->{id}}{$_} for keys %{$idsh->{$l->{id}} || {}};
2125 0   0       $l->{id} //= psmisc::join_url($l);
2126             }
2127             #$self->log('r1', Dumper $r);
2128 0           $r = [ grep { !$id{ $_->{id} }++ } @$r ];
  0            
2129 0           $post_process->($r);
2130             # @$r = sort { $idsh->{ $a->{'id'} }{'n'} <=> $idsh->{ $b->{'id'} }{'n'} } @$r;
2131             # for (@fail) {next if scalar @$r < $_->{'n'};
2132             # $r->[ $_->{'n'} ]{'__fulltext_fail'} = $_->{'q'}
2133             #}
2134 0           $count += scalar @$r;
2135 0 0         $opt->{row}->(@$r), psmisc::code_run( $opt->{flush} ),
2136             #$self->log('flush!'),
2137             next if psmisc::is_code $opt->{row};
2138 0           push @$ret, @$r;
2139             }
2140             } else {
2141 0           for my $select ( psmisc::array $select) { #select from sphinx
2142 0           my $r;
2143             #sleep 2;
2144 0 0         $r = $self->query(
2145             scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'codepage'}, $self->select_body( $select, $param ) ) )
2146             if $select;
2147 0 0         next unless $r;
2148             #$self->log('SSSs', "sp[$self->{'use_sphinx'}]");
2149 0   0       map { $_->{id} //= psmisc::join_url($_) } @$r; # unless $self->{'use_sphinx'};
  0            
2150 0           $r = [ grep { !$id{ $_->{id} }++ } @$r ];
  0            
2151             #my %already = map { $_->{'id'} => 1 } @$ret;
2152             #$self->log('SSSs', Dumper $r);
2153             #$self->log('SSSsRRb', Dumper $ret);
2154             #$self->log('SSSsRRbr', Dumper $r);
2155             #push @fail, { 'n' => scalar @$ids, 'q' => $param->{'q'} } if $n ;
2156             #$self->log('SSSsid5', Dumper \%id);
2157             #$ret += scalar @$r;
2158 0           $count += scalar @$r;
2159 0           push @$ret, @$r;
2160             #$self->log('SSSsRRa', Dumper $ret);
2161             #$self->log('SSSsid6', Dumper \%id);
2162             #++$id{$_->{id}} for @$r;
2163             #push @$ids, grep { !$already{ $_->{id} } } @$r;
2164             #$self->log('SS', scalar @$ret , 'L', $self->{'limit'}, map { $_->{'id'} } @$r);
2165             }
2166             }
2167             #$self->log('cnts:', $count, scalar @$ret , $self->{'limit'});
2168             #last if @$ret >= $self->{'limit'};
2169 0 0         last if $count >= $self->{'limit'};
2170             } continue {
2171 0           ++$n;
2172             #$self->log('continue', $n);
2173             }
2174 0           return $count;
2175 0           };
2176             push @selects, sub { # try LIKE by name
2177             # $self->log( 'dbg', 'selectrun', __LINE__);
2178 0           my $ask;
2179 0           my $search_str = $param->{'q'};
2180 0 0         if ( my %tparam = $self->q_file( $table, $search_str ) ) {
2181             #$self->log( 'qf', %tparam );
2182             #$search_str =~ /^\s*(\S+)\.+(\S+)\s*$/ and $self->{'table'}{$table}{'name'} and $self->{'table'}{$table}{'ext'} ) {
2183             #my %tparam = ( 'name' => $1, 'ext' => $2 );
2184 0           $ask .= ' ( ' . $self->where_body( \%tparam, undef, $table ) . ' ) ';
2185             }
2186             #$self->log('S1', $ask);
2187             #$file_fallback = 1, return ' WHERE ' . $ask if $ask;
2188             }
2189 0 0 0       if $self->{'use_q_file_fallback'} and !$self->{'sphinx'};
2190             push @selects, sub {
2191             # $self->log( 'dbg', 'selectrun', __LINE__);
2192             #$self->log( 'dbg', 'selectrun2 sph');
2193 0           my $ids = [];
2194 0           my %id;
2195 0 0 0       if ( $self->{'use_sphinx'}
      0        
      0        
      0        
2196             and $self->{'sphinx_dbi'}
2197             and length $param->{'q'}
2198             and ( $file_fallback or !$self->q_file( $table, $param->{'q'} ) ) )
2199             {
2200             #$self->log( 'dbg', 'selectin', __LINE__);
2201 0           ( $tq, $rq, $vq ) = $self->quotes();
2202 0 0         local $self->{'sphinx_dbi'}->{'option'}{'max_query_time'} = 2000 if $config{'client_bot'};
2203             # my %already = map { $_->{'id'} => 1 } @$ids, @$ret;
2204 0           my $idsl = [];
2205             # $self->log('SSSsid1', Dumper \%id);
2206 0           push @$idsl, grep { !$id{ $_->{id} }++ } @{ $self->{'sphinx_dbi'}->select( $table, $param ) };
  0            
  0            
2207             # $self->log('SSSsid2', Dumper \%id);
2208             #my $ids = $self->{'sphinx_dbi'}->select( $table, $param );
2209             #++$id{$_->{id}} for @$ids;
2210 0           $self->{'founded_max'} = $self->{'sphinx_dbi'}{'option'}{'cutoff'};
2211             #$self->log ('d1', "fmax", $self->{'founded_max'}, Dumper $ids,$idsl);
2212             # $self->log ('cnt',scalar @$ids , scalar @$idsl , $self->{'limit'});
2213 0 0         if (
2214             ( @$ids + @$idsl < $self->{'limit'} ) #and (!$self->{'use_sphinx'} or !$config{'client_bot'})
2215             )
2216             {
2217             #warn "limit[]"
2218             # $self->log( 'dbg','q', $param->{'q'});
2219             #local $self->{'sphinx_dbi'}->{'select_append'} = ' OPTION ranker=wordcount ';
2220 0 0         ++$work{'fulltext_fail'} unless @$ids;
2221 0           local $param->{'q'} = $param->{'q'};
2222 0 0         for my $func ( sub { $_[0] =~ s/^\s*"\s*// and $_[0] =~ s/\s*"\s*$// }, sub { $_[0] =~ s/(\w\s+)(\w)/$1 | $2/g }, ) {
  0            
  0            
2223 0 0         if ( $func->( $param->{'q'} ) ) {
2224 0           local $param->{'no_querystat'} = 1;
2225             #$self->log( 'idn', scalar @$ids, scalar @$idsl );
2226             #my %already = map { $_->{'id'} => 1 } @$ids, @$ret;
2227 0           local $self->{'sphinx_dbi'}{'limit_minus'} = scalar @$idsl;
2228 0           local $self->{'sphinx_dbi'}{'limit_offset'};
2229 0 0         local $self->{'sphinx_dbi'}{'page'} = 0 if $self->{'sphinx_dbi'}{'limit_minus'};
2230 0           my $ids_add = $self->{'sphinx_dbi'}->select( $table, $param );
2231 0           $self->{'founded_max'} = $self->{'sphinx_dbi'}{'option'}{'cutoff'};
2232             #TODO: info about changed query
2233             #$self->log('dev', "setfail $#$ids:$ids->[$#$ids]{id};"),
2234             #$self->log('dev', "setfail ", scalar @$ids, scalar @$idsl , scalar @$ids_add),
2235 0 0         push @fail, { 'n' => scalar @$ids + scalar @$idsl, 'q' => $param->{'q'} } if @$ids_add;
2236 0 0         unless (@$ids_add) { ++$work{'fulltext_fail_or'}; }
  0            
2237             #$self->log('SSSsid3', Dumper \%id, $ids_add);
2238 0           push @$idsl, grep { !$id{ $_->{id} }++ } @$ids_add;
  0            
2239             #$self->log('SSSsid4', Dumper \%id, $idsl);
2240             #++$id{$_->{id}} for @$ids_add;
2241             }
2242 0 0         last if @$ids + @$idsl >= $self->{'limit'};
2243             }
2244             }
2245             #psmisc::dmp ('dbiSmin', $self->{'sphinx_dbi'}{'limit_minus'});
2246             #psmisc::dmp ('dbiSoff', $self->{'sphinx_dbi'}{'limit_offset'});
2247             #psmisc::dmp ('dbimin', $self->{'limit_minus'});
2248             #psmisc::dmp ('dbioff', $self->{'limit_offset'});
2249 0 0         if (@$idsl) {
2250             # $self->log(__LINE__, 'prep idsl');
2251             my $wheregen = sub {
2252 0           @_ = psmisc::array @_;
2253             # $self->log('dmp','wheregen', Dumper \@_);
2254 0 0         return " WHERE ${rq}id${rq} IN (" . ( join ',', map { $_->{'id'} } @_ ) . ')' if @_;
  0            
2255             #();
2256 0           };
2257             #$self->log('joining',$select, 'sh=', $self->{'shard'}, Dumper $ids, $idsl);
2258 0 0 0       if ( !$self->{'sphinx'} and $self->{'shard'} ) {
2259             # $self->log('shard',keys %{$self->{'shard_dbis'}} );
2260 0           my %ids;
2261 0           for my $r (@$idsl) {
2262 0           for my $from ( reverse sort keys %{ $self->{'shard_dbis'} } ) {
  0            
2263             #$self->log('shardC',$from, $self->{'shard_dbis'}{$from}{database}, $self->{'shard_dbis'}{$from}{dbname});
2264             # $self->log('shardC',$from, $r);
2265 0 0         if ( $r->{id} >= $from ) {
2266 0   0       push @{ $ids{$from} ||= [] }, $r;
  0            
2267 0           last;
2268             }
2269             }
2270             }
2271             #$self->log('sh', Dumper \%ids);
2272 0           $select = {};
2273 0           for my $from ( keys %{ $self->{'shard_dbis'} } ) {
  0            
2274 0   0       my $w = $ids{$from} || next;
2275 0           $select->{ $wheregen->($w) } = $from; #scalar @{$ids{$from}};
2276             #$self->log('sh22', $from, $ids{$from});
2277             }
2278             #$self->log('sh22', Dumper $select,);
2279             } else {
2280             # $self->log('wgen', Dumper $select,);
2281 0           $select = $wheregen->($idsl);
2282             # $self->log('simple', Dumper $select,);
2283             }
2284 0           push @$ids, @$idsl;
2285             }
2286             }
2287 0           local $self->{'limit_body'} = sub { }
2288 0 0         if @$ids;
2289 0           ( $tq, $rq, $vq ) = $self->quotes();
2290             #unless ($select) {
2291             #local $self->{'table'}{$table}{$table}{'ext'}{'nav_field'} = 0;
2292             #local $self->{'table'}{$table}{'ext'}{'q_skip'} = 1;
2293             # $self->log( 'dev', "!$select and !$config{'client_bot'} and sp!$self->{'use_sphinx'}");
2294 0           my $count;
2295 0 0 0       if ( !$select and ( !$self->{'use_sphinx'} or !$config{'client_bot'} ) ) {
      0        
2296 0 0 0       if ( !$self->{'use_sphinx'} or !$self->{'no_sphinx_like'} ) {
2297             #$select = $self->where( $param, undef, $table );
2298             # $self->log( 'dev', "!$select and !$config{'client_bot'}");
2299             #$self->log( 'dbg', 'selectshrd', __LINE__);
2300 0 0 0       if ( !$self->{'sphinx'} and $self->{'shard'} ) {
2301             # $self->log('shard',keys %{$self->{'shard_dbis'}} );
2302             #$self->log('sh', Dumper \%ids);
2303             #$select = {};
2304 0           for my $from ( sort keys %{ $self->{'shard_dbis'} } ) {
  0            
2305             #my $w = $ids{$from} || next;
2306             #$select->{ $wheregen->( $w ) } = $from; #scalar @{$ids{$from}};
2307             #local $self->{limit} = $self->{limit} - $count;
2308 0 0         local $self->{'limit_from'} = $self->{'limit_offset'} + $count if $count;
2309 0           local $self->{'limit_minus'} = $count;
2310             # $self->log( 'limi', $from, $count, $self->{limit}, $self->{'limit_offset'}, $self->{'limit_from'}, $self->{'limit_minus'} );
2311 0           $select = {};
2312 0           $select->{ "/* $from */" . $self->where( $param, undef, $table ) } = $from;
2313             #$do_select->($select, $ids);
2314 0           $count += $do_select->($select);
2315             #$self->log('shard lst', $from, $count, $self->{limit});
2316 0           $select = undef;
2317 0 0         last if $count >= $self->{limit};
2318             #$self->log('shard after', $from, $count);
2319             }
2320             #$self->log( 'sh22', Dumper $param, $self->{limit} );
2321             } else {
2322             # $self->log( 'noshard',);
2323 0           $select = $self->where( $param, undef, $table );
2324             }
2325             }
2326 0           $self->{'founded_max'} = 0;
2327             }
2328             #$self->log( 'S2', $select, Dumper \%id);
2329             #$self->log( 'S2', $select, Dumper $ids);
2330 0           return $select, $ids, $count;
2331 0           };
2332             #push @selects, $select if $select;
2333             #}
2334             #$self->log('devpredoselect',Dumper \@selects);
2335 0           my $count = $do_select->(@selects);
2336             #{'use_q_file_fallback'}
2337             #if (@$ids) {
2338             #my %byid = map { $_->{id} => $_ } @$ret;
2339             #for my $s (@$ids) { $byid{ $s->{id} }{$_} //= $s->{$_} for keys %$s; }
2340             #$self->log('devret', Dumper $ret);
2341             #$self->log('dev1', "sph[$self->{'use_sphinx'}]", Dumper $ids);
2342 0 0 0       if ( $self->{'use_sphinx'} and @$ids ) {
2343 0           my $n = 0;
2344             #my %ids = map { $_->{'id'} => ++$n } @$ids;
2345             #my %ids = map {$_->{'n'} = ++$n; $_->{'id'} => $_ } @$ids;
2346             #$self->log( 'dev123', map { $_->{'id'} } @$ret);
2347             #$self->log( 'dev123', Dumper \%ids );
2348             #$self->log( 'dev123', Dumper $ids );
2349             #$self->log( 'dev123r', Dumper $ret );
2350 0           $post_process->($ret);
2351              
2352             =no
2353             for my $r (@$ret) {
2354             $r->{$_} ||= $idsh->{$r->{'id'}}{$_} for keys %{$idsh->{$r->{'id'}}||{}};
2355             #$self->log( 'dev123', Dumper $r, );
2356            
2357             }
2358             @$ret = sort { $ids{ $a->{'id'} }{'n'} <=> $ids{ $b->{'id'} }{'n'} } @$ret;
2359             #@$ret = sort { $ids{ $a->{'weight'} } <=> $ids{ $b->{'weight'} } } @$ret;
2360             #$self->log( 'dev124', Dumper $ret );
2361             #$self->log( 'devFail', Dumper \@fail);
2362             for (@fail) {
2363             next if scalar @$ret < $_->{'n'};
2364             $ret->[ $_->{'n'} ]{'__fulltext_fail'} = $_->{'q'}
2365             }
2366             =cut
2367              
2368             }
2369             #$self->log( 'devFIN', $self->{'dbirows'}, $count, Dumper $ret );
2370             #}
2371             #$self->log( 'fnd', "$self->{'founded'}, $self->{'dbirows'},$count;" );
2372             #$self->log( 'devFIN', $self->{'dbirows'}, $count, Dumper $param );
2373 0   0       $self->{'dbirows'} ||= $count;
2374 0 0         return wantarray ? @$ret : $ret;
2375 0   0       };
2376             $self->{'select_log'} ||= sub {
2377 0     0     my $self = shift;
2378 0           my ( $table, $param, ) = @_;
2379 0           return $self->query_log( $self->select_body( $self->where( $param, undef, $table ), $param, $table ) );
2380 0   0       };
2381             $self->{'join_what'} ||= sub {
2382             #sub select {
2383 0     0     my $self = shift;
2384 0           my ( $where, $param, $table ) = @_;
2385 0   0       $table ||= $self->{'current_table'};
2386 0           my @join;
2387             #=dev
2388 0           for my $jt ( keys %{ $self->{'table_join'}{$table} } ) {
  0            
2389 0 0         local @_ = ( #(
2390             #map { $_ }
2391 0           grep { $_ and $self->{'table'}{$jt}{$_} } keys %{ $self->{'table_join'}{$table}{$jt}{'on'} }
  0            
2392             #)
2393             );
2394             #$self->log('dev','join', %{$self->{'table_join'}});
2395             #$self->log('dev', "JOd $table -> $jt,",@_,"::", keys %{ $self->{'table_join'}{$table}{$jt}{'on'} });
2396             #push @join, " $tq$self->{'table_prefix'}$table$tq LEFT JOIN " .$tq. $self->{'table_prefix'} . $jt . $tq.
2397 0           push @join, " LEFT JOIN " . $tq . $self->{'table_prefix'} . $jt . $tq . ' ON ' . '(' . join(
2398             ', ',
2399             map {
2400 0 0         $tq
2401             . $self->{'table_prefix'}
2402             . $table
2403             . $tq . '.'
2404             . $rq
2405             . $self->{'table_join'}{$table}{$jt}{'on'}{$_}
2406             . $rq . ' = '
2407             . $tq
2408             . $self->{'table_prefix'}
2409             . $jt
2410             . $tq . '.'
2411             . $rq
2412             . $_
2413             . $rq
2414             } @_
2415             )
2416             . ')'
2417             if @_;
2418 0 0         unless (@_) {
2419 0 0         @_ = (
2420             #(
2421             #map { $_ }
2422 0           grep { $_ and $self->{'table'}{$jt}{$_} } keys %{ $self->{'table_join'}{$table}{$jt}{'using'} }
  0            
2423             #)
2424             #or (grep { $self->{'table'}{$jt}{$_}{'primary'} }
2425             #keys %{ $self->{'table'}{$jt} })
2426             );
2427             #$self->log('dev',"joprim{$jt}{$_}",
2428             #keys (%{ $self->{'table'}{$jt} }),"oooooo",
2429             #grep( { $self->{'table'}{$jt}{$_}{'primary'} }
2430             #keys (%{ $self->{'table'}{$jt} })),
2431             #"j[".join(':',@_)."]", scalar @_),
2432             #$self->log('dev',"joprim{$jt} keys:", map( {'[', keys %{ $self->{'table'}{$jt}{$_}} , ']'} ,keys %{ $self->{'table'}{$jt} }),'prim:',grep { $self->{'table'}{$jt}{$_}{'primary'} }
2433             #keys %{ $self->{'table'}{$jt} }
2434             #);
2435             #$self->log('dev',"joprim{$jt:",%{$self->{'table'}{$jt}{'host'}});
2436             #$self->log('dev','jop1',@_, "::", Dumper($self->{'table'}{$jt} ));
2437 0 0         @_ = ( grep { $self->{'table'}{$jt}{$_}{'primary'} } keys %{ $self->{'table'}{$jt} } ) unless @_;
  0            
  0            
2438             #$self->log('dev','jop2',@_);
2439             #$self->log('dev','jop', "j[$jt][$_][".join(':',@_)."]", scalar @_);
2440             #$self->log('dev', 'jo:',@_, ',,,:',grep { $self->{'table'}{$jt}{$_} }
2441             #grep { $_ }keys %{ $self->{'table_join'}{$table}{$jt}{'using'} });
2442             #push @join, "$tq$self->{'table_prefix'}$table$tq LEFT JOIN " .$tq. $self->{'table_prefix'} . $jt . $tq.' USING ' . '(' . join( ', ', map { $rq . $_ . $rq } @_ ) . ')'
2443             #$self->log('dev', "JO1 $table -> $jt,@_ [".join(':',@_)."]::", keys %{ $self->{'table_join'}{$table}{$jt}{'on'} });
2444 0           push @join,
2445             " LEFT JOIN "
2446             . $tq
2447             . $self->{'table_prefix'}
2448             . $jt
2449             . $tq
2450             . ' USING ' . '('
2451 0 0         . join( ', ', map { $rq . $_ . $rq } @_ ) . ')'
2452             if @_;
2453             }
2454             #=cut
2455             }
2456             #=cut
2457 0           return join( ' ', @join );
2458 0   0       };
2459             $self->{'join_where'} ||= sub {
2460             #sub select {
2461 0     0     my $self = shift;
2462 0           my ( $where, $param, $table ) = @_;
2463 0           my @what;
2464 0   0       $table ||= $self->{'current_table'};
2465 0           for my $jt ( sort keys %{ $self->{'table_join'}{$table} } ) {
  0            
2466             #$self->log('dev', "here $jt");
2467 0           local $_ = join ', ', map {
2468 0           $tq
2469             . $self->{'table_prefix'}
2470             . $jt
2471             . $tq . '.'
2472             . $rq
2473             . $self->{'table_join'}{$table}{$jt}{'fields'}{$_}
2474             . $rq . ' AS '
2475             . $rq
2476             . $_
2477             . $rq
2478             } grep {
2479 0           $self->{'table'}{$jt}{ $self->{'table_join'}{$table}{$jt}{'fields'}{$_} }
2480 0           } sort keys %{ $self->{'table_join'}{$table}{$jt}{'fields'} };
2481 0   0       $_ ||= "$tq$self->{'table_prefix'}$jt" . "$tq.*";
2482             #$join .= $_
2483 0           push( @what, $_ );
2484             }
2485             #$join = ', ' . $join if $join;
2486             #$sql = " $tq$self->{'table_prefix'}$table" . "$tq.* $work{'what_relevance'}{$table}".($join ? ', ' : ''). $join . " " . $sql;
2487             #$self->log('dev', join(':',@what));
2488             #@what = ('*');
2489 0           return join( ', ', grep { $_ } @what );
  0            
2490 0   0       };
2491 0           for my $by (qw(order group)) {
2492             $self->{ $by . 'by' } ||= sub {
2493             #sub select {
2494 0     0     my $self = shift;
2495 0           my ( $param, $table ) = @_;
2496 0   0       $table ||= $self->{'current_table'};
2497 0           my $sql;
2498             my %order;
2499 0           for my $ordern ( '', 0 .. 10 ) {
2500 0   0       my $order = ( $param->{ $by . $ordern } or next );
2501 0 0 0       last if ( $self->{'ignore_index'} or $self->{'table_param'}{$table}{'ignore_index'} );
2502             #$self->log('dev',1, $ordern, $order);
2503 0           my $min_data;
2504 0 0         ++$min_data
2505 0           for grep { $self->{'table'}{$table}{$_}{'sort_min'} and defined( $param->{$_} ) } keys %{ $self->{'table'}{$table} };
  0            
2506 0 0 0       last if $self->{'no_slow'} and !$min_data;
2507             #$self->log('dev',2, $ordern, $order);
2508 0           for my $join (
  0            
2509 0 0         grep { $order eq $_ } (
2510 0           grep { $self->{'table'}{$table}{$_}{'sort'} or !$self->{'table'}{$table}{$_}{'no_order'} }
2511 0           keys %{ $self->{'table'}{$table} }
2512             ),
2513             @{ $self->{'table_param'}{$table}{'join_fields'} }
2514             )
2515             {
2516 0           my ($intable) = grep { keys %{ $self->{'table'}{$_}{$join} } } $table, keys %{ $config{'sql'}{'table_join'}{$table} };
  0            
  0            
  0            
2517             #print "INTABLE[$intable]";
2518             #$order{ $tq . $table . $tq . '.' . $rq . $_ . $rq
2519 0 0         $order{ ( $self->{'no_column_prepend_table'} ? '' : $tq . $intable . $tq . '.' )
    0          
2520             . $rq
2521             . $join
2522             . $rq
2523             . ( ( $param->{ $by . '_mode' . $ordern } ) ? ' DESC ' : ' ASC' ) }
2524             = #$param->{ 'order_rev' . $ordern } eq 'on' or
2525             $ordern;
2526             }
2527             }
2528 0 0         if ( keys %order ) {
2529 0           $sql .= ' ' . uc($by) . ' BY ' . join ', ', sort { $order{$a} <=> $order{$b} } keys %order;
  0            
2530             }
2531             #print 'ORDERBY', Dumper($param,$table,$sql, $self->{'table_param'}{$table}{'join_fields'} );
2532 0           return $sql;
2533 0   0       };
2534             }
2535             $self->{'select_body'} ||= sub {
2536             #sub select {
2537 0     0     my $self = shift;
2538 0           my ( $where, $param, $table ) = @_;
2539 0   0       $table ||= $self->{'current_table'};
2540 0           ( $tq, $rq, $vq ) = $self->quotes();
2541             #$self->log( 'dev', 'select_body', $where );
2542             #my ( $tq, $rq, $vq ) = sql_quotes();
2543 0           $self->limit_calc( $param, $table );
2544             #limit(_calc( $param, $table );
2545 0 0 0       if ( ( $self->{'ignore_index'} or $self->{'table_param'}{$table}{'ignore_index'} )
      0        
      0        
2546             and !( $self->{'no_index'} or $self->{'table_param'}{$table}{'no_index'} ) )
2547             {
2548 0           local @_ = ();
2549 0           local %_ = ();
2550 0           for ( keys %{ $self->{'table'}{$table} } ) {
  0            
2551 0 0         ++$_{ $self->{'table'}{$table}{$_}{'fulltext'} } if $self->{'table'}{$table}{$_}{'fulltext'};
2552 0 0         push( @_, $_ ) if $self->{'table'}{$table}{$_}{'index'};
2553             }
2554 0 0 0       push( @_, keys %_ ) unless $self->{'ignore_index_fulltext'} and $self->{'table_param'}{$table}{'ignore_index_fulltext'};
2555 0           $work{'sql_select_index'} = 'IGNORE INDEX (' . join( ',', @_ ) . ')';
2556             }
2557             #my $join = ;
2558             #!!!
2559 0           my $from;
2560 0 0         if ($table) {
2561 0 0 0       if ( $self->{'sphinx'} and $self->{'table_param'}{$table}{'stemmed_index'} and !$param->{'accurate'} ) {
      0        
2562 0           $from .= "$tq$self->{'table_param'}{$table}{'stemmed_index'}$tq ";
2563             } else {
2564 0           $from .= "$tq$self->{'table_prefix'}$table$tq ";
2565             }
2566             }
2567 0 0         unless ( $self->{'no_join'} ) { $from .= $work{'sql_select_index'} . ' ' . $self->join_what( $where, $param, $table ); }
  0            
2568 0 0         $from = "FROM " . $from if $from;
2569 0           my $sql = $from . ' ' . $where;
2570 0 0 0       my @what = (
2571             ( ( $table and !$self->{'no_column_prepend_table'} ) ? $tq . $self->{'table_prefix'} . $table . $tq . '.' : '' ) . '*',
2572             $work{'what_relevance'}{$table},
2573             #$param->{'what_extra'}
2574             $self->{'table_param'}{$table}{'what_extra'}
2575             );
2576 0 0         if ( defined( $self->{'table'}{$table}{ $param->{'distinct'} } ) ) {
2577             #$sql = " DISTINCT $rq$param->{'distinct'}$rq " . $sql . " ";
2578 0           @what = ( "DISTINCT $rq$param->{'distinct'}$rq", $self->{'table_param'}{$table}{'what_extra'} );
2579             } else {
2580             #my $join ;
2581             #@join = ()
2582             #!!
2583 0 0         unless ( $self->{'no_join'} ) { @what = ( $self->join_where( $where, $param, $table ), @what ); }
  0            
2584             }
2585             #$self->log('dmp', "SP=", $self->{'sphinx'} );
2586 0           $sql = join( ', ', grep { $_ } @what, ) . ' ' . $sql;
  0            
2587 0           my $priority;
2588 0 0 0       $priority = $self->{'HIGH_PRIORITY'} if !$config{'client_bot'} and !$config{'client_no_high_priority'};
2589 0           $sql = " SELECT $self->{'SELECT_FLAGS'} $priority " . $sql; #SQL_CALC_FOUND_ROWS
2590 0           $sql .= $self->groupby( $param, $table );
2591 0           $sql .= $self->orderby( $param, $table );
2592             #$work{'on_page'} = 10 unless defined $work{'on_page'};
2593             #my $limit = psmisc::check_int( ( $param->{'limit'} or $work{'on_page'} ), 0, $self->{'limit_max'}, $self->{'on_page'} );
2594             #$sql .= ' LIMIT ' . ( $param->{'show_from'} ? $param->{'show_from'} . ',' : '' ) . " $limit"
2595             #if $param->{'show_from'}
2596             #or $limit;
2597             #$self->{'limit'} = 10 unless defined $self->{'limit'};
2598             #my $limit = psmisc::check_int( ( $param->{'limit'} or $self->{'limit'} ), 0, $self->{'results_max'}, $self->{'on_page'} );
2599             #$sql .= ' LIMIT ' . ( $param->{'show_from'} ? $param->{'show_from'} . ',' : '' ) . " $limit" if $param->{'show_from'}
2600 0           $sql .= $self->limit_body();
2601 0 0 0       if ( $self->{'OPTION'} and psmisc::is_hash $self->{'option'} ) { #sphinx
2602 0           $sql .= $self->{'OPTION'} . ' ' . join ', ', map { "$_=$self->{'option'}{$_}" } keys %{ $self->{'option'} };
  0            
  0            
2603             }
2604 0           $sql .= $self->{'select_append'};
2605 0           return $sql;
2606 0   0       };
2607             $self->{'limit_body'} ||= sub {
2608             #sub calc_count {
2609 0     0     my $self = shift;
2610 0 0 0       return unless $self->{'limit_offset'} or $self->{'limit'};
2611             return
2612 0 0 0       ' LIMIT '
    0 0        
2613             . ( $self->{'limit_offset'} && !$self->{'OFFSET'} ? $self->{'limit_offset'} . ',' : '' )
2614             . $self->{'limit'}
2615             . ( $self->{'OFFSET'} && $self->{'limit_offset'} ? ' ' . $self->{'OFFSET'} . ' ' . $self->{'limit_offset'} : '' ) . ' ';
2616 0           return '';
2617 0   0       };
2618             $self->{'calc_count'} ||= sub {
2619             #sub calc_count {
2620 0     0     my $self = shift;
2621 0           my ( $param, $table, $count ) = @_;
2622 0 0         return if $work{'calc_count'}{$table}++;
2623             #$self->log( 'dev', "calc_count0 : founded=$self->{'founded'}; page=$self->{'page'} page_last=$self->{'page_last'} dbirows=$self->{'dbirows'} limit=$self->{'limit'} ", );
2624 0   0       $self->{'founded'} = $count
2625             || ( ( $self->{'dbirows'} > $self->{'stat'}{'found'}{'files'} and $self->{'dbirows'} < $self->{'limit'} )
2626             ? $self->{'dbirows'} + $self->{'limit_offset'}
2627             : $self->{'stat'}{'found'}{'files'} );
2628 0 0 0       $self->{'founded'} = 0 if $self->{'founded'} < 0 or !$self->{'founded'}; #or !$self->{'dbirows'} !!!experemental!
2629 0 0 0       $self->{'page_last'} =
    0 0        
2630             $self->{'limit'} > 0
2631             ? ( int( $self->{'founded'} / ( $self->{'limit'} or 1 ) ) + ( $self->{'founded'} % ( $self->{'limit'} or 1 ) ? 1 : 0 ) )
2632             : 0; #3
2633 0 0 0       $self->{'page'} = int( rand( $self->{'page_last'} ) ) if $self->{'page'} eq 'rnd' and $param->{'count_f'} eq 'on'; #4
2634             #$self->log( 'dev', "calc_count : founded=$self->{'founded'}; page=$self->{'page'} page_last=$self->{'page_last'} dbirows=$self->{'dbirows'} limit=$self->{'limit'} ", );
2635 0   0       };
2636             $self->{'limit_calc'} ||= sub {
2637             #sub pre_query {
2638 0     0     my $self = shift;
2639 0           my ($param) = @_;
2640             #return if $work{'pre_query'}{$table}++;
2641             #$self->{'page'} = int( $param->{'page'} > 0 ? $param->{'page'} : 1 );
2642             #$self->{'page'} = psmisc::check_int( $param->{'page'}, 1, $self->{'page_max'}, 1 );
2643             #$self->{'limit'} = psmisc::check_int( $param->{'on_page'}, 0, $self->{'limit_max'}, $self->{'on_page'} );
2644             #$self->{'limit'} ||= psmisc::check_int( $param->{'on_page'}, 0, $self->{'results_max'}, $self->{'on_page'} );
2645 0 0 0       $self->{'limit_offset'} =
2646             int( $self->{'page'} > 0 ? $self->{'limit'} * ( $self->{'page'} - 1 ) : ( ( $param->{'show_from'} ) or 0 ) );
2647 0 0         $self->{'limit_offset'} -= $self->{'limit_from'} - $self->{'limit_minus'} if $self->{'limit_offset'};
2648 0           $self->{'limit'} -= $self->{'limit_minus'};
2649             #$self->log( 'dev',"limit_calc : limit_offset=$self->{'limit_offset'}; page=$self->{'page'} limit= $self->{'limit'} from=$self->{'limit_from'}" );
2650             #; #caller(), caller(1), caller(2)
2651 0           return undef;
2652 0   0       };
2653             $self->{'lock_tables'} ||= sub {
2654             #sub lock_tables {
2655 0     0     my $self = shift;
2656             #local $_ = $self->do( $self->{'LOCK TABLES'}.' ' . join ' ', @_ );
2657             #$work{'sql_locked'} = join ' ', @_ if $_;
2658 0 0         return $self->do( $self->{'LOCK TABLES'} . ' ' . join ' ', @_ ) if $self->{'LOCK TABLES'};
2659 0   0       };
2660             $self->{'unlock_tables'} ||= sub {
2661             #sub unlock_tables {
2662 0     0     my $self = shift;
2663             #$work{'sql_locked'} = '';
2664             #return $self->do( 'UNLOCK TABLES ' . join ' ', @_ );
2665 0 0         return $self->do( $self->{'UNLOCK TABLES'} . ' ' . join ' ', @_ ) if $self->{'UNLOCK TABLES'};
2666 0   0       };
2667             $self->{'stat_string'} ||= sub {
2668 0     0     my $self = shift;
2669             #print "\nSTRAAAA\n";
2670 0           return 'sqlstat: '
2671             . join(
2672             ' ',
2673 0           ( map { "$_=$self->{$_};" } grep { $self->{$_} } ( @_ or sort keys %{ $self->{'statable'} } ) ),
  0            
2674             (
2675 0           map { "$_=" . psmisc::human( 'time_period', $self->{$_} ) . ';' }
2676 0   0       grep { $self->{$_} } ( @_ or sort keys %{ $self->{'statable_time'} } )
      0        
2677             )
2678             );
2679 0   0       };
2680             $self->{'log_stat'} ||= sub {
2681 0     0     my $self = shift;
2682 0           $self->log( 'stat', $self->stat_string(@_) );
2683 0   0       };
2684             $self->{'check_data'} ||= sub {
2685 0     0     my $self = shift;
2686 0           local @_ = sort grep { $_ } keys %{ $self->{'table'} };
  0            
  0            
2687 0 0         return 0 unless @_;
2688             #$self->log('dev',@_);
2689 0           return 0;
2690 0           return $self->query( 'SELECT * FROM ' . ( join ',', map { "$tq$_$tq" } @_ ) . ' WHERE 1 LIMIT 1' );
  0            
2691 0   0       };
2692             $self->{'check_data_every_table'} ||= sub {
2693 0     0     my $self = shift;
2694 0           local @_ = sort grep { $_ } keys %{ $self->{'table'} };
  0            
  0            
2695 0 0         return 0 unless @_;
2696 0           for my $table (@_) {
2697             #$self->log('check', $table,
2698 0           $self->query_log("SELECT * FROM $tq$table$tq LIMIT 1"); #);
2699             }
2700 0   0       };
2701             $self->{'on_connect1'} ||= sub {
2702 0     0     my $self = shift;
2703             #$self->log( 'dev', 'ONCON1');
2704 0 0         $self->check_data() if $self->{'auto_check'};
2705             #use Data::Dumper;
2706             #$self->log( 'dev', Dumper($config{'sql'}));
2707 0   0       };
2708             $self->{'table_stat'} ||= sub {
2709 0     0     my $self = shift;
2710 0           $self->log( 'info', 'totals:', @_,
2711 0           map { ( $_, '=', values %{ $self->line("SELECT COUNT(*) FROM $rq$self->{'table_prefix'}$_$rq ") } ) }
  0            
2712 0   0       grep { $_ } ( @_ or keys %{ $self->{'table'} } ) );
2713 0   0       };
2714             $self->{'next_user_prepare'} ||= sub {
2715 0     0     my $self = shift;
2716             #$self->{'queries'} = $self->{''} = $self->{''} = $self->{''} = $self->{''} = 0;
2717             #delete $self->{error_log} if $self->{'error_collect'};
2718              
2719 0           delete $self->{$_} for qw(founded queries queries_time errors_chain errors connect_tried error_log);
2720 0           $self->{'stat'}{'found'} = {};
2721 0           $self->{ 'on_user' . $_ }->($self) for grep { ref $self->{ 'on_user' . $_ } eq 'CODE' } ( '', 1 .. 5 );
  0            
2722             #$self->{ 'on_user' }->($self) for grep { ref $self->{ 'on_user' } eq 'CODE'}('');
2723             #$self->log('dev', 'nup');
2724              
2725 0   0       };
2726             $self->{'next_user'} ||= sub {
2727 0     0     my $self = shift;
2728 0           $self->user_params(@_);
2729 0           $self->next_user_prepare(@_);
2730 0 0         $self->{'sphinx_dbi'}->next_user(@_) if $self->{'sphinx_dbi'};
2731 0   0       };
2732              
2733             =stem links
2734             http://en.wikipedia.org/wiki/New_York_State_Identification_and_Intelligence_System
2735             http://translit.ru/
2736             http://koi8.pp.ru/koi8-r_iso9945-2.txt
2737             http://en.wikipedia.org/wiki/Stemming
2738             http://linguist.nm.ru/stemka/stemka.html
2739             =cut
2740              
2741             $self->{'stem'} ||= sub {
2742 0     0     my $self = shift;
2743             #$self->log('dev', "stem in[$_[0]]( $self->{'codepage'}, $self->{'cp_in'} -> $self->{'cp_int'})");
2744             #return $_[0];
2745 0           local $_ = lc( scalar psmisc::cp_trans( $self->{'cp_in'}, $self->{'cp_int'}, $_[0] ) );
2746             #local $_ = lc( scalar psmisc::cp_trans( $self->{'codepage'}, $self->{'cp_int'}, $_[0] ) );
2747             #local $_ = lc($_[0] );
2748             #$self->log('dev', "stem bef[$_]");
2749 0 0         $self->{'stem_version'} = 4 if $self->{'stem_version'} <= 1;
2750 0 0         if ( $self->{'stem_version'} == 2 ) { #first
    0          
    0          
2751 0           s/(\d)(\D)/$1 $2/g;
2752 0           s/(\D)(\d)/$1 $2/g;
2753 0           tr/А-Я/а-я/;
2754 0           s/[ъь]//g;
2755 0           s/kn/n/g;
2756 0           tr/абвгдеёжзийклмнопрстуфхцчшщыэюя/abvgdeejsiiklmnoprstufhccssieua/;
2757 0           tr/ekouw/acaav/;
2758 0           s/'//g;
2759 0 0         s/\W/ /g if $_[1];
2760 0           s/_/ /g;
2761 0           s/(?:rd|nd)\b/d/g;
2762 0           s/ay\b/y/g;
2763 0           s/\B[aeisuo]\b//g;
2764 0           s/av/af/g;
2765 0           s/sch/s/g;
2766 0           s/ph/f/g;
2767 0           s/\s+/ /g;
2768 0           s/(\w)\1+/$1/g;
2769             } elsif ( $self->{'stem_version'} == 3 ) { #temporary
2770 0           s/(\d)(\D)/$1 $2/g;
2771 0           s/(\D)(\d)/$1 $2/g;
2772 0           tr/А-Я/а-я/;
2773 0           s/[ъь]//g;
2774 0           s/kn/n/g;
2775 0           tr/абвгдеёжзийклмнопрстуфхцчшщыэюя/abvgdeejsiiklmnoprstufhccssieua/;
2776 0           s/ks/x/g; #2
2777 0           tr/kw/cv/; #3
2778 0           s/'//g;
2779 0 0         s/\W/ /g if $_[1];
2780 0           s/_/ /g;
2781 0           s/(?:rd|nd)\b/d/g;
2782 0           s/ay\b/y/g;
2783 0           s/\B[aeisuo]\b//g;
2784 0           s/av/af/g;
2785 0           s/sch/s/g;
2786 0           s/ph/f/g;
2787 0           s/\s+/ /g;
2788 0           s/(?:(?!xxx)|(?=xxxx))(\w)\1+(?:(?
2789             } elsif ( $self->{'stem_version'} == 4 ) { #release candidate
2790 0           s/(\d)(\D)/$1 $2/g;
2791 0           s/(\D)(\d)/$1 $2/g;
2792 0           tr/А-Я/а-я/;
2793 0           s/kn/n/g;
2794 0           s/[ъь]//g;
2795 0           tr{абвгдеёжзийклмнопрстуфхцчшщыэюя}
2796             {abvgdeejziiklmnoprstufhccssieua}; #4 z
2797 0           s/ks/x/g; #2
2798 0           tr/kw/cv/; #3
2799 0           s/'//g;
2800 0 0         s/\W/ /g if $_[1];
2801 0           s/_/ /g;
2802 0           s/(?:rd|nd)\b/d/g;
2803 0           s/ay\b/y/g;
2804 0           s/\B[aeisuo]\b//g;
2805 0           s/av/af/g;
2806 0           s/sch/s/g;
2807 0           s/ph/f/g;
2808 0           s/\s+/ /g;
2809 0           s/(?:(?!xxx)|(?=xxxx))(\w)\1+(?:(?
2810             }
2811             #$self->log('dev', "stem aft[$_]");
2812             #$_ = scalar psmisc::cp_trans( $self->{'cp_int'}, $self->{'cp_in'},$_);
2813             #$_ = scalar psmisc::cp_trans( $self->{'cp_int'}, $self->{'codepage'}, $_ );
2814             #$self->log('dev', "stem out[$_]");
2815             #return scalar psmisc::cp_trans( $self->{'cp_int'}, $self->{'codepage'}, $_ );
2816 0           return scalar psmisc::cp_trans( $self->{'cp_int'}, $self->{'cp_in'}, $_ );
2817 0   0       };
2818             $self->{'stem_insert'} ||= sub {
2819 0     0     my $self = shift;
2820             #$config{'sql'}{'handler_insert'} = sub {
2821 0           my ( $table, $col ) = @_;
2822 0 0         return 1 unless ref $self->{'stem'} eq 'CODE';
2823             #$config{'stem'} and
2824             #$config{'stem_func'};
2825 0           $col->{'stem'} = join ' ',
2826 0 0         map { $self->stem( $col->{$_}, 1 ) } grep { $self->{'table'}{$table}{$_}{'stem'} and $col->{$_} } keys %$col;
  0            
2827 0           return undef;
2828 0   0       };
2829             $self->{'last_insert_id'} ||= sub {
2830 0     0     my $self = shift;
2831 0   0       my $table = shift || $self->{'current_table'};
2832 0 0 0       if ( $^O eq 'MSWin32' and $self->{'driver'} eq 'pgpp' ) {
2833 0 0         my ($field) =
2834 0           grep { $self->{'table'}{$table}{$_}{'type'} eq 'serial' or $self->{'table'}{$table}{$_}{'auto_increment'} }
2835 0           keys %{ $self->{'table'}{$table} };
2836             #$self->log('dev', 'use lid1', "${table}_${field}");
2837 0           return $self->line("SELECT currval('${table}_${field}_seq') as lastid")->{'lastid'};
2838             } else {
2839             #$self->log('use lid2');
2840 0           return $self->{dbh}->last_insert_id( undef, undef, $table, undef );
2841             }
2842 0   0       };
2843             $self->{'dump_cp'} ||= sub {
2844 0     0     $self->log( 'dev', map { "$_ = $self->{$_}; " } qw(codepage cp cp_in cp_out cp_int cp_set_names) );
  0            
2845 0   0       };
2846             $self->{'cp_client'} ||= sub {
2847 0     0     my $self = shift;
2848 0 0         $self->{'cp_in'} = $_[0] if $_[0];
2849 0 0 0       $self->{'cp_out'} = $_[1] || $self->{'cp_in'} if $_[1] or $_[0];
      0        
2850 0           return ( $self->{'cp_in'}, $self->{'cp_out'} );
2851 0   0       };
2852             $self->{'index_disable'} ||= sub {
2853 0     0     my $self = shift;
2854 0           my $tim = psmisc::timer();
2855 0           $self->log( 'info', 'Disabling indexes on', @_ );
2856 0 0 0       $self->log( 'err', 'ALTER TABLE ... DISABLE KEYS available in mysql >= 4' ), return
2857             if $self->{'driver'} eq 'mysql3'
2858             or $self->{'driver'} !~ /mysql/;
2859             $self-> #query_log
2860 0           do("ALTER TABLE $tq$config{'table_prefix'}$_$tq DISABLE KEYS") for @_;
2861 0           $self->log( 'time', "Disable index per", psmisc::human( 'time_period', $tim->() ), "sec" );
2862 0   0       };
2863             $self->{'index_enable'} ||= sub {
2864 0     0     my $self = shift;
2865 0           my $tim = psmisc::timer();
2866 0           $self->log( 'info', 'Enabling indexes on', @_ );
2867 0 0 0       $self->log( 'err', 'ALTER TABLE ... DISABLE KEYS available in mysql >= 4' ), return
2868             if $self->{'driver'} eq 'mysql3'
2869             or $self->{'driver'} !~ /mysql/;
2870             $self-> #query_log
2871 0           do("ALTER TABLE $tq$config{'table_prefix'}$_$tq ENABLE KEYS") for @_;
2872 0           $self->log( 'time', 'Enable index per ', psmisc::human( 'time_period', $tim->() ) );
2873 0   0       };
2874 0           for my $action (qw(optimize analyze check flush)) {
2875             $self->{$action} ||= sub {
2876 0     0     my $self = shift;
2877 0 0         @_ = sort keys %{ $self->{'table'} } unless @_;
  0            
2878 0 0         @_ = grep { $_ and $self->{'table'}{$_} } @_;
  0            
2879 0 0         $self->log( 'err', 'not defined action', $action, ), return unless $self->{ uc $action };
2880 0           $self->log( 'info', $action, @_ );
2881 0           my $tim = psmisc::timer();
2882 0 0         for ( $self->{'bulk_service'} ? \@_ : @_ ) {
2883 0           $self->query_log(
2884             $self->{ uc $action } . ' ' . join( ',', map( $self->tquote("$self->{'table_prefix'}$_"), psmisc::array $_ ) ) );
2885             }
2886 0           $self->log( 'time', $action, 'per ', psmisc::human( 'time_period', $tim->() ) );
2887 0   0       };
2888             }
2889              
2890             =no
2891             for my $action (qw(flush)) {
2892             $self->{$action} ||= sub {
2893             my $self = shift;
2894             @_ = sort keys %{ $self->{'table'} } unless @_;
2895             @_ = grep { $_ and ( m/\./ or $self->{'table'}{$_} ) } @_;
2896             $self->log( 'err', 'not defined action', $action, ), return unless $self->{ uc $action };
2897             $self->log( 'info', $action, @_ );
2898             my $tim = psmisc::timer();
2899             $self->do( $self->{ uc $action } . ' ' . join( ',', map( $self->tquote( $self->{'table_prefix'} . $_ ), @_ ) ) );
2900             $self->log( 'time', $action, 'per ', psmisc::human( 'time_period', $tim->() ) );
2901             };
2902             }
2903             =cut
2904              
2905             $self->{'retry_off'} ||= sub {
2906 0     0     my $self = shift;
2907 0 0         return if %{ $self->{'retry_save'} || {} };
  0 0          
2908 0           $self->{'retry_save'}{$_} = $self->{$_}, $self->{$_} = 0 for @{ $self->{'retry_vars'} };
  0            
2909 0   0       };
2910             $self->{'retry_on'} ||= sub {
2911 0     0     my $self = shift;
2912 0 0         return unless %{ $self->{'retry_save'} || {} };
  0 0          
2913 0           $self->{$_} = $self->{'retry_save'}{$_} for @{ $self->{'retry_vars'} };
  0            
2914 0           $self->{'retry_save'} = {};
2915 0   0       };
2916             $self->{'set_names'} ||= sub {
2917 0     0     my $self = shift;
2918 0   0       local $_ = $_[0] || $self->{'cp_set_names'};
2919 0 0 0       $self->do( $self->{'SET NAMES'} . " $vq$_$vq" ) if $_ and $self->{'SET NAMES'};
2920 0   0       };
2921             }
2922             1;