#!/usr/bin/perl BEGIN { die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; }; use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); use Test::More tests => 26; use TableParser; use TableNibbler; use Quoter; use PerconaTest; my $q = new Quoter(); my $tp = new TableParser(Quoter => $q); my $n = new TableNibbler( TableParser => $tp, Quoter => $q, ); my $t; $t = $tp->parse( load_file('t/lib/samples/sakila.film.sql') ); is_deeply( $n->generate_asc_stmt ( tbl_struct => $t, cols => $t->{cols}, index => 'PRIMARY', ), { cols => [qw(film_id title description release_year language_id original_language_id rental_duration rental_rate length replacement_cost rating special_features last_update)], index => 'PRIMARY', where => '((`film_id` >= ?))', slice => [0], scols => [qw(film_id)], boundaries => { '>=' => '((`film_id` >= ?))', '>' => '((`film_id` > ?))', '<=' => '((`film_id` <= ?))', '<' => '((`film_id` < ?))', }, }, 'asc stmt on sakila.film', ); is_deeply( $n->generate_del_stmt ( tbl_struct => $t, ), { cols => [qw(film_id)], index => 'PRIMARY', where => '(`film_id` = ?)', slice => [0], scols => [qw(film_id)], }, 'del stmt on sakila.film', ); is_deeply( $n->generate_asc_stmt ( tbl_struct => $t, index => 'PRIMARY', ), { cols => [qw(film_id title description release_year language_id original_language_id rental_duration rental_rate length replacement_cost rating special_features last_update)], index => 'PRIMARY', where => '((`film_id` >= ?))', slice => [0], scols => [qw(film_id)], boundaries => { '>=' => '((`film_id` >= ?))', '>' => '((`film_id` > ?))', '<=' => '((`film_id` <= ?))', '<' => '((`film_id` < ?))', }, }, 'defaults to all columns', ); throws_ok( sub { $n->generate_asc_stmt ( tbl_struct => $t, cols => $t->{cols}, index => 'title', ) }, qr/Index 'title' does not exist in table/, 'Error on nonexistent index', ); is_deeply( $n->generate_asc_stmt ( tbl_struct => $t, cols => $t->{cols}, index => 'idx_title', ), { cols => [qw(film_id title description release_year language_id original_language_id rental_duration rental_rate length replacement_cost rating special_features last_update)], index => 'idx_title', where => '((`title` >= ?))', slice => [1], scols => [qw(title)], boundaries => { '>=' => '((`title` >= ?))', '>' => '((`title` > ?))', '<=' => '((`title` <= ?))', '<' => '((`title` < ?))', }, }, 'asc stmt on sakila.film with different index', ); is_deeply( $n->generate_del_stmt ( tbl_struct => $t, index => 'idx_title', cols => [qw(film_id)], ), { cols => [qw(film_id title description release_year language_id original_language_id rental_duration rental_rate length replacement_cost rating special_features last_update)], index => 'idx_title', where => '(`film_id` = ? AND `title` = ? AND ((? IS NULL AND `description` IS NULL) OR (`description` = ?)) AND ((? IS NULL AND `release_year` IS NULL) OR (`release_year` = ?)) AND `language_id` = ? AND ((? IS NULL AND `original_language_id` IS NULL) OR (`original_language_id` = ?)) AND `rental_duration` = ? AND `rental_rate` = ? AND ((? IS NULL AND `length` IS NULL) OR (`length` = ?)) AND `replacement_cost` = ? AND ((? IS NULL AND `rating` IS NULL) OR (`rating` = ?)) AND ((? IS NULL AND `special_features` IS NULL) OR (`special_features` = ?)) AND `last_update` = ?)', slice => [ 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10, 11, 11, 12 ], scols => [qw( film_id title description description release_year release_year language_id original_language_id original_language_id rental_duration rental_rate length length replacement_cost rating rating special_features special_features last_update)], }, 'del stmt on sakila.film with different index and extra column', ); # TableParser::find_best_index() is case-insensitive, returning the # correct case even if the wrong case is given. But generate_asc_stmt() # no longer calls find_best_index() so this test is a moot point. is_deeply( $n->generate_asc_stmt ( tbl_struct => $t, cols => $t->{cols}, index => 'idx_title', ), { cols => [qw(film_id title description release_year language_id original_language_id rental_duration rental_rate length replacement_cost rating special_features last_update)], index => 'idx_title', where => '((`title` >= ?))', slice => [1], scols => [qw(title)], boundaries => { '>=' => '((`title` >= ?))', '>' => '((`title` > ?))', '<=' => '((`title` <= ?))', '<' => '((`title` < ?))', }, }, 'Index returned in correct lettercase', ); is_deeply( $n->generate_asc_stmt ( tbl_struct => $t, cols => [qw(title)], index => 'PRIMARY', ), { cols => [qw(title film_id)], index => 'PRIMARY', where => '((`film_id` >= ?))', slice => [1], scols => [qw(film_id)], boundaries => { '>=' => '((`film_id` >= ?))', '>' => '((`film_id` > ?))', '<=' => '((`film_id` <= ?))', '<' => '((`film_id` < ?))', }, }, 'Required columns added to SELECT list', ); # ########################################################################## # Switch to the rental table # ########################################################################## $t = $tp->parse( load_file('t/lib/samples/sakila.rental.sql') ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?)' . ' OR (`rental_date` = ? AND `inventory_id` = ? AND `customer_id` >= ?))', slice => [1, 1, 2, 1, 2, 3], scols => [qw(rental_date rental_date inventory_id rental_date inventory_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` >= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` > ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` <= ?))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` < ?))', }, }, 'Alternate index on sakila.rental', ); is_deeply( $n->generate_del_stmt ( tbl_struct => $t, index => 'rental_date', ), { cols => [qw(rental_date inventory_id customer_id)], index => 'rental_date', where => '(`rental_date` = ? AND `inventory_id` = ? AND `customer_id` = ?)', slice => [0, 1, 2], scols => [qw(rental_date inventory_id customer_id)], }, 'Alternate index on sakila.rental delete statement', ); # Check that I can select from one table and insert into another OK my $f = $tp->parse( load_file('t/lib/samples/sakila.film.sql') ); is_deeply( $n->generate_ins_stmt( ins_tbl => $f, sel_cols => $t->{cols}, ), { cols => [qw(last_update)], slice => [6], }, 'Generated an INSERT statement from film into rental', ); my $sel_tbl = $tp->parse( load_file('t/lib/samples/issue_131_sel.sql') ); my $ins_tbl = $tp->parse( load_file('t/lib/samples/issue_131_ins.sql') ); is_deeply( $n->generate_ins_stmt( ins_tbl => $ins_tbl, sel_cols => $sel_tbl->{cols}, ), { cols => [qw(id name)], slice => [0, 2], }, 'INSERT stmt with different col order and a missing ins col' ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', asc_first => 1, ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` >= ?))', slice => [1], scols => [qw(rental_date)], boundaries => { '>=' => '((`rental_date` >= ?))', '>' => '((`rental_date` > ?))', '<=' => '((`rental_date` <= ?))', '<' => '((`rental_date` < ?))', }, }, 'Alternate index with asc_first on sakila.rental', ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', n_index_cols => 2, ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` >= ?))', slice => [qw(1 1 2)], scols => [qw(rental_date rental_date inventory_id)], boundaries => { '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND `inventory_id` < ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND `inventory_id` <= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?))', '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` >= ?))' }, }, 'Use only N left-most columns of the index', ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', n_index_cols => 5, ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?)' . ' OR (`rental_date` = ? AND `inventory_id` = ? AND `customer_id` >= ?))', slice => [1, 1, 2, 1, 2, 3], scols => [qw(rental_date rental_date inventory_id rental_date inventory_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` >= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` > ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` <= ?))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` < ?))', }, }, "Don't crash if N > number of index columns" ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', asc_only => 1, ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?)' . ' OR (`rental_date` = ? AND `inventory_id` = ? AND `customer_id` > ?))', slice => [1, 1, 2, 1, 2, 3], scols => [qw(rental_date rental_date inventory_id rental_date inventory_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` >= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` > ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` <= ?))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` < ?))', }, }, 'Alternate index on sakila.rental with strict ascending', ); # ########################################################################## # Switch to the rental table with customer_id nullable # ########################################################################## $t = $tp->parse( load_file('t/lib/samples/sakila.rental.null.sql') ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?)' . ' OR (`rental_date` = ? AND `inventory_id` = ? AND ' . '(? IS NULL OR `customer_id` >= ?)))', slice => [1, 1, 2, 1, 2, 3, 3], scols => [qw(rental_date rental_date inventory_id rental_date inventory_id customer_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND (? IS NULL OR `customer_id` >= ?)))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND ((? IS NULL AND `customer_id` IS NOT NULL) ' . 'OR (`customer_id` > ?))))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND (? IS NULL OR `customer_id` <= ?)))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND ((? IS NOT NULL AND `customer_id` IS NULL) ' . 'OR (`customer_id` < ?))))', }, }, 'Alternate index on sakila.rental with nullable customer_id', ); is_deeply( $n->generate_del_stmt ( tbl_struct => $t, index => 'rental_date', ), { cols => [qw(rental_date inventory_id customer_id)], index => 'rental_date', where => '(`rental_date` = ? AND `inventory_id` = ? AND ' . '((? IS NULL AND `customer_id` IS NULL) OR (`customer_id` = ?)))', slice => [0, 1, 2, 2], scols => [qw(rental_date inventory_id customer_id customer_id)], }, 'Alternate index on sakila.rental delete statement with nullable customer_id', ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', asc_only => 1, ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?)' . ' OR (`rental_date` = ? AND `inventory_id` = ? AND ' . '((? IS NULL AND `customer_id` IS NOT NULL) OR (`customer_id` > ?))))', slice => [1, 1, 2, 1, 2, 3, 3], scols => [qw(rental_date rental_date inventory_id rental_date inventory_id customer_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND (? IS NULL OR `customer_id` >= ?)))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND ((? IS NULL AND `customer_id` IS NOT NULL) ' . 'OR (`customer_id` > ?))))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND (? IS NULL OR `customer_id` <= ?)))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND ((? IS NOT NULL AND `customer_id` IS NULL) ' . 'OR (`customer_id` < ?))))', }, }, 'Alternate index on sakila.rental with nullable customer_id and strict ascending', ); # ########################################################################## # Switch to the rental table with inventory_id nullable # ########################################################################## $t = $tp->parse( load_file('t/lib/samples/sakila.rental.null2.sql') ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR ' . '(`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NOT NULL) OR (`inventory_id` > ?)))' . ' OR (`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NULL) ' . 'OR (`inventory_id` = ?)) AND `customer_id` >= ?))', slice => [1, 1, 2, 2, 1, 2, 2, 3], scols => [qw(rental_date rental_date inventory_id inventory_id rental_date inventory_id inventory_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '((? IS NULL AND `inventory_id` IS NOT NULL) OR (`inventory_id` ' . '> ?))) OR (`rental_date` = ? AND ((? IS NULL AND `inventory_id` ' . 'IS NULL) OR (`inventory_id` = ?)) AND `customer_id` >= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ((? IS NULL ' . 'AND `inventory_id` IS NOT NULL) OR (`inventory_id` > ?))) OR ' . '(`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NULL) ' . 'OR (`inventory_id` = ?)) AND `customer_id` > ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ((? IS NOT ' . 'NULL AND `inventory_id` IS NULL) OR (`inventory_id` < ?))) OR ' . '(`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NULL) ' . 'OR (`inventory_id` = ?)) AND `customer_id` <= ?))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ((? IS NOT ' . 'NULL AND `inventory_id` IS NULL) OR (`inventory_id` < ?))) ' . 'OR (`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS ' . 'NULL) OR (`inventory_id` = ?)) AND `customer_id` < ?))', }, }, 'Alternate index on sakila.rental with nullable inventory_id', ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, cols => $t->{cols}, index => 'rental_date', asc_only => 1, ), { cols => [qw(rental_id rental_date inventory_id customer_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR ' . '(`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NOT NULL) OR (`inventory_id` > ?)))' . ' OR (`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NULL) ' . 'OR (`inventory_id` = ?)) AND `customer_id` > ?))', slice => [1, 1, 2, 2, 1, 2, 2, 3], scols => [qw(rental_date rental_date inventory_id inventory_id rental_date inventory_id inventory_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '((? IS NULL AND `inventory_id` IS NOT NULL) OR (`inventory_id` ' . '> ?))) OR (`rental_date` = ? AND ((? IS NULL AND `inventory_id` ' . 'IS NULL) OR (`inventory_id` = ?)) AND `customer_id` >= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ((? IS NULL ' . 'AND `inventory_id` IS NOT NULL) OR (`inventory_id` > ?))) OR ' . '(`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NULL) ' . 'OR (`inventory_id` = ?)) AND `customer_id` > ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ((? IS NOT ' . 'NULL AND `inventory_id` IS NULL) OR (`inventory_id` < ?))) OR ' . '(`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS NULL) ' . 'OR (`inventory_id` = ?)) AND `customer_id` <= ?))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ((? IS NOT ' . 'NULL AND `inventory_id` IS NULL) OR (`inventory_id` < ?))) ' . 'OR (`rental_date` = ? AND ((? IS NULL AND `inventory_id` IS ' . 'NULL) OR (`inventory_id` = ?)) AND `customer_id` < ?))', }, }, 'Alternate index on sakila.rental with nullable inventory_id and strict ascending', ); # ########################################################################## # Switch to the rental table with cols in a different order. # ########################################################################## $t = $tp->parse( load_file('t/lib/samples/sakila.rental.remix.sql') ); is_deeply( $n->generate_asc_stmt( tbl_struct => $t, index => 'rental_date', ), { cols => [qw(rental_id rental_date customer_id inventory_id return_date staff_id last_update)], index => 'rental_date', where => '((`rental_date` > ?) OR (`rental_date` = ? AND `inventory_id` > ?)' . ' OR (`rental_date` = ? AND `inventory_id` = ? AND `customer_id` >= ?))', slice => [1, 1, 3, 1, 3, 2], scols => [qw(rental_date rental_date inventory_id rental_date inventory_id customer_id)], boundaries => { '>=' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` >= ?))', '>' => '((`rental_date` > ?) OR (`rental_date` = ? AND ' . '`inventory_id` > ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` > ?))', '<=' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` <= ?))', '<' => '((`rental_date` < ?) OR (`rental_date` = ? AND ' . '`inventory_id` < ?) OR (`rental_date` = ? AND `inventory_id` ' . '= ? AND `customer_id` < ?))', }, }, 'Out-of-order index on sakila.rental', ); # ########################################################################## # Switch to table without any indexes # ########################################################################## $t = $tp->parse( load_file('t/lib/samples/t1.sql') ); # This test is no longer needed because TableSyncNibble shouldn't # ask TableNibbler to asc an indexless table. # throws_ok( # sub { # $n->generate_asc_stmt ( # tbl_struct => $t, # ) # }, # qr/Cannot find an ascendable index in table/, # 'Error when no good index', # ); is_deeply( $n->generate_cmp_where( cols => [qw(a b c d)], slice => [0, 3], is_nullable => {}, type => '>=', ), { scols => [qw(a a d)], slice => [0, 0, 3], where => '((`a` > ?) OR (`a` = ? AND `d` >= ?))', }, 'WHERE for >=', ); is_deeply( $n->generate_cmp_where( cols => [qw(a b c d)], slice => [0, 3], is_nullable => {}, type => '>', ), { scols => [qw(a a d)], slice => [0, 0, 3], where => '((`a` > ?) OR (`a` = ? AND `d` > ?))', }, 'WHERE for >', ); is_deeply( $n->generate_cmp_where( cols => [qw(a b c d)], slice => [0, 3], is_nullable => {}, type => '<=', ), { scols => [qw(a a d)], slice => [0, 0, 3], where => '((`a` < ?) OR (`a` = ? AND `d` <= ?))', }, 'WHERE for <=', ); is_deeply( $n->generate_cmp_where( cols => [qw(a b c d)], slice => [0, 3], is_nullable => {}, type => '<', ), { scols => [qw(a a d)], slice => [0, 0, 3], where => '((`a` < ?) OR (`a` = ? AND `d` < ?))', }, 'WHERE for <', ); # ############################################################################# # Done. # ############################################################################# exit;