package tests::DlfQueryTest;

use strict;

use base qw/ tests::TestStoreFixture Lire::Test::TestCase /;

use Lire::DlfQuery;
use Lire::DlfSchema;
use Lire::ExtendedSchema;

sub new {
    my $self = shift()->SUPER::new( @_ );

    $self->init();

    $self;
}

sub set_up {
    my $self = $_[0];
    $self->SUPER::set_up();

    $self->set_up_test_schema();

    $Lire::DlfSchema::SCHEMA_CACHE{'test-extended2'}
      = new Lire::ExtendedSchema( 'id' => 'test-extended2',
                                  'base-schema' => 'test-derived',
                                  'module' => 'MyModule' );
    $self->{'query'} = new Lire::DlfQuery( 'test' );

    return;
}

sub tear_down {
    my $self = $_[0];
    $self->SUPER::tear_down();

    delete $Lire::DlfSchema::SCHEMA_CACHE{'test-extended2'};

    $self->tear_down_test_store()
      if $self->{'store'};
    $self->{'query'}->release();

    return;
}

sub test_new {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_isa( 'Lire::DlfQuery', $query );

    $self->assert_num_equals( 0, $query->{'_limit'} );
    $self->assert_null( $query->{'_sort_spec'},
                        "'_sort_spec' attribute should be undef" );
    $self->assert_null( $query->{'_order_by'},
                        "'_order_by' attribute should be undef" );
    $self->assert_deep_equals( [], $query->{'_fields'} );
    $self->assert_deep_equals( {}, $query->{'_field_refs'} );
    $self->assert_deep_equals( [], $query->{'_filter_params'});
    $self->assert_null( $query->{'_parent'},
                        "new query should have NULL parent" );
    $self->assert_deep_equals( [], $query->{'_nested_queries'} );
    $self->assert_null( $query->{'_filter_clause'},
                        "filter_clause attribute should be undef" );

    $self->assert_str_equals( 'test', $query->{'_stream_name'} );
}

sub test_new_bad_params {
    my $self = $_[0];

    $self->assert_dies( qr/missing 'stream_name' parameter/,
                        sub { new Lire::DlfQuery() } );
    $self->assert_dies( qr/there is no schema 'wawa'/,
                        sub { new Lire::DlfQuery( 'wawa' ) } );
}

sub test__schema_has_field {
    my $self = $_[0];

    $self->assert( $self->{'query'}->_schema_has_field( 'time_start' ) );
    $self->assert( ! $self->{'query'}->_schema_has_field( 'dirname' ) );
    $self->{'query'}{'_joined_streams'} = [ 'test-extended' ];
    $self->assert( $self->{'query'}->_schema_has_field( 'dirname' ) );
}

sub test__field_by_name {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->_add_field ('file', 'simple');
    $self->assert_deep_equals ( { 'field' => 'file', 'type' => 'simple', 'alias' => undef },
                                $query->_field_by_name('file') );
    $query->_add_field ('sum()', 'aggr', 'count');
    $self->assert_deep_equals ( { 'field' => 'sum()', 'type' => 'aggr', 'alias' => 'count' },
                                $query->_field_by_name('count') );
}

sub test__add_field {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->_add_field( 'time_start', 'simple' );
    $self->assert( exists $query->{'_field_refs'}{'time_start'} );
    $self->assert_deep_equals( [ { 'field' => 'time_start', 
                                   'alias' => undef,
                                   'type' => 'simple' } ],
                               $query->{'_fields'} );

    $query->_add_field( 'count()', 'aggr', 'nrecords' );
    $self->assert( exists $query->{'_field_refs'}{'nrecords'} );
    $self->assert_deep_equals( [ { 'field' => 'time_start', 
                                   'alias' => undef,
                                   'type' => 'simple' },
                                 { 'field' => 'count()', 'alias' => 'nrecords',
                                   'type' => 'aggr' } ],
                               $query->{'_fields'} );
}

sub test__add_field_bad_params {
    my $self = $_[0];

    my $query = $self->{'query'};

    $self->assert_dies( qr/missing 'field' parameter/,
                        sub { $query->_add_field() } );
    $self->assert_dies( qr/missing 'type' parameter/,
                        sub { $query->_add_field( 'a_field' ) } );

    $self->assert_dies( qr/field name should contain only alphanumeric characters: 'a field'/,
                        sub { $query->_add_field( 'a field', 'simple' ) } );
    $self->assert_dies( qr/field name should contain only alphanumeric characters: 'sum\(\)'/,
                      sub { $query->_add_field( 'sum()', 'aggr' ) } );

    $self->assert_dies( qr/type should be 'simple', 'group', 'aggr': 'zoinzoin'/,
                        sub { $query->_add_field( 'wawa', 'zoinzoin' ) } );

    $self->assert_dies( qr/no field 'wawa' in 'test' schema/,
                        sub { $query->_add_field( 'wawa', 'simple' ) } );

    $self->assert_dies(  qr/'nrecords' is already defined in the query/,
                         sub { $query->_add_field( 'sum()', 'aggr',
                                                   'nrecords' );
                               $query->_add_field( 'sum()', 'aggr',
                                                   'nrecords' ) } );
}

sub test__add_field_with_nested_query {
    my $self = $_[0];

    my $query = $self->{'query'};
    my $nested1 = $query->create_nested_query();
    my $nested2 = $query->create_nested_query();
    my $nested3 = $nested1->create_nested_query();

    $query->_add_field( 'lr_timegroup(time_start)', 'group', 'period' );
    $self->assert_dies( qr/'period' is already defined in the query/,
                        sub { $nested1->_add_field( 'lr_timegroup(time_start)', 'group', 'period' ) } );
    $self->assert_dies( qr/'period' is already defined in the query/,
                        sub { $nested2->_add_field( 'lr_timegroup(time_start)', 'group', 'period' ) } );
    $self->assert_dies( qr/'period' is already defined in the query/,
                        sub { $nested3->_add_field( 'lr_timegroup(time_start)', 'group', 'period' ) } );

    $nested1->_add_field( 'connection_id', 'simple' );
    $self->assert_dies( qr/'connection_id' is already defined in the query/,
                        sub { $query->_add_field( 'connection_id', 'simple' ) } );

    $nested2->_add_field( 'connection_id', 'simple' );

    $nested3->_add_field( 'file', 'simple' );
    $self->assert_dies( qr/'file' is already defined in the query/,
                        sub { $nested1->_add_field( 'file', 'simple' ) } );
}

sub test_add_field {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_field( 'time_start' );

    $self->assert_deep_equals( { 'field' => 'time_start',
                                 'alias' => undef,
                                 'type' => 'simple' },
                               $query->_field_by_name( 'time_start' ) );

    $query->add_field( 'file', 'file_size/1024' );
    $self->assert_deep_equals( { 'field' => 'file_size/1024',
                                 'alias' => 'file', 'type' => 'simple' },
                               $query->_field_by_name( 'file' ) );
}

sub test_add_field_after_add_group {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'connection_id' );
    $self->assert_dies( qr/calls to add_field\(\) and add_group_field\(\) can't be mixed on the same Lire::DlfQuery/,
                        sub { $query->add_field( 'file' ) } );
}

sub test_add_field_with_create_nested_query {
    my $self = $_[0];

    my $query = $self->{'query'};
    my $nested = $query->create_nested_query();
    $self->assert_dies( qr/can't add simple field to a query containing nested queries/,
                        sub { $query->add_field( 'file' ) } );
    $nested->add_field( 'file' );
    $self->assert( defined $nested->_field_by_name( "file" ),
                   "field 'file' wasn't added" );
}

sub test_no_subqueries_on_simple_queries {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_field( 'file' );
    $self->assert_dies( qr/can't create a nested query on query containing simple fields/,
                        sub { $query->create_nested_query() } );
}

sub test__fields_of_type {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_field( 'connection_id' );
    $self->assert_deep_equals( [ { 'field' => 'connection_id',
                                   'type' => 'simple',
                                   'alias' => undef } ],
                               $query->_fields_of_type('simple') );
    $self->assert_deep_equals( [],
                               $query->_fields_of_type('group+aggr') );
    $self->assert_deep_equals( [ ],
                               $query->_fields_of_type('group') );
    $self->assert_deep_equals( [ ],
                               $query->_fields_of_type('aggr') );
}

sub test__fields_of_type_group {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'connection_id' );
    $self->assert_deep_equals( [ { 'field' => 'connection_id',
                                   'type' => 'group',
                                   'alias' => undef } ],
                               $query->_fields_of_type('group') );
}

sub test__fields_of_type_group_aggr {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_aggr_field( 'nrecords', 'count(*)' );
    $query->add_group_field( 'connection_id' );
    $self->assert_deep_equals( [ { 'field' => 'count(*)',
                                   'type' => 'aggr',
                                   'alias' => 'nrecords' },
                                 { 'field' => 'connection_id',
                                   'type' => 'group',
                                   'alias' => undef }, ],
                               $query->_fields_of_type('group+aggr') );
}

sub test__fields_of_type_bad_params {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_dies( qr/type should be 'simple', 'group', 'aggr' or 'group\+aggr': 'nosuchtype/,
                        sub { $query->_fields_of_type('nosuchtype') } );
}

sub test_add_group_field {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_group_field( 'connection_id' );
    $self->assert_deep_equals( { 'field' => 'connection_id',
                                 'alias' => undef,
                                 'type' => 'group' },
                               $query->_field_by_name( 'connection_id' ) );

    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $self->assert_deep_equals( { 'field' => 'lr_timegroup(time_start)',
                                 'alias' => 'period', 'type' => 'group' },
                               $query->_field_by_name( 'period' ) );
}

sub test_group_fields {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_deep_equals( [], $query->group_fields() );
    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );

    my $nested = $query->create_nested_query();
    $nested->add_group_field( 'connection_id' );
    $nested->add_aggr_field( 'nrecords', 'count()' );

    $self->assert_deep_equals( ['period' ],
                               $query->group_fields() );
    $self->assert_deep_equals( ['period', 'connection_id' ],
                               $nested->group_fields() );
}

sub test_aggr_fields {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_deep_equals( [], $query->aggr_fields() );

    $query->add_group_field( 'connection_id' );
    $query->add_aggr_field( 'bytes', 'sum(file_size)' );

    my $nested = $query->create_nested_query();
    $nested->add_aggr_field( 'nrecords', 'count()' );

    $self->assert_deep_equals( [ 'bytes', 'nrecords' ],
                               $query->aggr_fields() );
    $self->assert_deep_equals( [ 'nrecords' ],
                               $nested->aggr_fields() );
}

sub test_fields {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_deep_equals( [], $query->fields() );

    $query->add_field( 'connection_id' );
    $query->add_field( 'k', 'file_size/1024' );
    $self->assert_deep_equals( [ 'connection_id', 'k' ],
                               $query->fields() );
}

sub test_field_def {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'connection_id' );

    my $nested1 = $self->{'query'}->create_nested_query();
    $nested1->add_aggr_field( 'nrecords', 'count()' );

    my $nested2 = $self->{'query'}->create_nested_query();
    $nested2->add_field( 'user' );
    $nested2->add_field( 'file' );

    $self->assert_dies( qr/no field 'time_start' defined in the query/,
                        sub { $query->field_def( 'time_start' ) } );
    $self->assert_str_equals( 'user', $query->field_def( 'user' ) );
    $self->assert_str_equals( 'file', $query->field_def( 'file' ) );
    $self->assert_str_equals( 'connection_id',
                              $query->field_def( 'connection_id' ) );
    $self->assert_str_equals( 'count()',
                              $query->field_def( 'nrecords' ) );

    $self->assert_dies( qr/no field 'file' defined in the query/,
                        sub { $nested1->field_def( 'file' ) } );
    $self->assert_str_equals( 'connection_id',
                              $nested1->field_def( 'connection_id' ) );
    $self->assert_str_equals( 'count()',
                              $nested1->field_def( 'nrecords' ) );

    $self->assert_dies( qr/no field 'nrecords' defined in the query/,
                        sub { $nested2->field_def( 'nrecords' ) } );
    $self->assert_str_equals( 'connection_id',
                              $nested2->field_def( 'connection_id' ) );
    $self->assert_str_equals( 'user', $nested2->field_def( 'user' ) );
    $self->assert_str_equals( 'file', $nested2->field_def( 'file' ) );

}

sub test__query_subtree_from_root {
    my $self = $_[0];

    my $query = $self->{'query'};
    my $nested1 = $query->create_nested_query();
    my $nested2 = $query->create_nested_query();
    my $nested3 = $nested1->create_nested_query();

    $self->assert_deep_equals( [ $query, $nested1, $nested3, $nested2 ],
                               $query->_query_subtree_from_root() );
    $self->assert_deep_equals( [ $query, $nested1, $nested3 ],
                               $nested1->_query_subtree_from_root() );
    $self->assert_deep_equals( [ $query, $nested2 ],
                               $nested2->_query_subtree_from_root() );
    $self->assert_deep_equals( [ $query, $nested1, $nested3 ],
                               $nested3->_query_subtree_from_root() );
}

sub test_add_group_after_add_field {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_field( 'file' );
    $self->assert_dies( qr/calls to add_field\(\) and add_group_field\(\) can't be mixed on the same Lire::DlfQuery/,
                        sub { $query->add_group_field( 'connection_id' ) } );
}

sub test_add_aggr_field {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_aggr_field( "requests", "count()" );
    $self->assert_str_equals( 'aggr',
                              $query->_field_by_name( 'requests' )->{'type'} );

    $self->assert_dies( qr/missing 'aggregate' parameter/,
                        sub { $query->add_aggr_field( "requests" ) } );
}

sub test_set_sort_spec {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'time_start' );
    $query->add_group_field( 'connection_id' );
    $query->add_group_field( 'transfer-complete' );

    my $nested = $query->create_nested_query();
    $nested->add_aggr_field( 'downloads', "count()" );
    $query->set_sort_spec( '-time_start' );
    $self->assert_str_equals( '-time_start', $query->{'_sort_spec'} );
    $self->assert_str_equals( 'time_start DESC', $query->{'_order_by'} );

    $query->set_sort_spec( undef );
    $self->assert_null( $query->{'_sort_spec'}, 
                        "'_sort_spec' attribute should have been cleared" );
    $self->assert_null( $query->{'_order_by'},
                        "'_order_by' attribute should have been cleared" );

    $query->set_sort_spec( '' );
    $self->assert_null( $query->{'_sort_spec'}, 
                        "'_sort_spec' attribute should still be cleared" );
    $self->assert_null( $query->{'_order_by'},
                        "'_order_by' attribute should still be cleared" );

    $query->set_sort_spec( 'connection_id -time_start' );
    $self->assert_str_equals( 'connection_id -time_start',
                              $query->{'_sort_spec'} );
    $self->assert_str_equals( 'connection_id, time_start DESC',
                              $query->{'_order_by'} );

    $query->set_sort_spec( 'transfer-complete' );
    $self->assert_str_equals( '"transfer-complete"', $query->{'_order_by'} );

    $query->set_sort_spec( '-transfer-complete' );
    $self->assert_str_equals( '"transfer-complete" DESC',
                              $query->{'_order_by'} );

    $query->set_sort_spec( '-downloads' );
    $self->assert_str_equals( 'downloads DESC', $query->{'_order_by'} );

    $self->assert_dies( qr/field 'bad_field' unavailable for sorting/,
                        sub { $query->set_sort_spec( 'bad_field' ); } );
}

sub test__is_valid_sort_field {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'connection_id' );

    my $nested1 = $query->create_nested_query();
    $nested1->add_aggr_field( 'downloads', "count()" );

    my $nested2 = $nested1->create_nested_query();
    $nested2->add_field( 'user' );

    $self->assert_num_equals( 0, $query->_is_valid_sort_field( 'bad_field' ) );
    $self->assert_num_equals( 0, $query->_is_valid_sort_field( 'user' ) );
    $self->assert_num_equals( 1,
                              $query->_is_valid_sort_field( 'connection_id'));
    $self->assert_num_equals( 1, $query->_is_valid_sort_field( 'downloads'));

    $self->assert_num_equals( 0,
                              $nested1->_is_valid_sort_field( 'bad_field' ) );
    $self->assert_num_equals( 0, $nested1->_is_valid_sort_field( 'user' ) );
    $self->assert_num_equals( 1,
                              $nested1->_is_valid_sort_field( 'connection_id'));
    $self->assert_num_equals( 1, $nested1->_is_valid_sort_field( 'downloads'));

    $self->assert_num_equals( 0, $nested2->_is_valid_sort_field( 'bad_field' ) );
    $self->assert_num_equals( 0, $nested2->_is_valid_sort_field( 'downloads'));
    $self->assert_num_equals( 1, $nested2->_is_valid_sort_field( 'user' ) );
    $self->assert_num_equals( 1,
                              $nested2->_is_valid_sort_field( 'connection_id'));
}

sub test_set_sort_spec_and_set_order_by_clause_interaction {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_field( 'time_start');
    $query->set_sort_spec( 'time_start' );
    $query->set_order_by_clause( 'connection_id' );
    $self->assert_null( $query->{'_sort_spec'},
                        "'_sort_spec' attribute should have been cleared" );
}

sub test_set_order_by_clause {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->set_order_by_clause( 'time_start DESC' );
    $self->assert_str_equals( 'time_start DESC', $query->{'_order_by'} );

    $query->set_order_by_clause( undef );
    $self->assert_null( $query->{'_order_by'},
                        "order_by attribute should have been cleared" );

    $query->set_order_by_clause( '' );
    $self->assert_null( $query->{'_order_by'},
                        "order_by attribute should still be cleared" );
}

sub test_set_limit {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->set_limit( 50 );
    $self->assert_num_equals( 50, $query->{'_limit'} );
    $query->set_limit( undef );
    $self->assert_num_equals( 0, $query->{'_limit'} );
    $query->set_limit( 20 );
    $query->set_limit( 0 );
    $self->assert_num_equals( 0, $query->{'_limit'} );
    $self->assert_dies( qr/'limit' parameter should be an integer: 'some text'/,
                        sub { $query->set_limit( 'some text' ) } );
}

sub test_set_filter_clause {
    my $self = $_[0];

    my $query = $self->{'query'};

    $self->assert_dies( qr/invalid number of parameters: filter clause contains 0 placeholders while 1 values provided/,
                        sub { $query->set_filter_clause( undef, '' ); } );
    $self->assert_dies( qr/invalid number of parameters: filter clause contains 1 placeholders while 0 values provided/,
                      sub { $query->set_filter_clause( 'time_start > ?' ); } );
    $self->assert_dies( qr/invalid number of parameters: filter clause contains 1 placeholders while 2 values provided/,
                        sub { $query->set_filter_clause( 'time_start > ?', 1, 2 ); } );


    $query->set_filter_clause( 'time_start = 0' );
    $self->assert_str_equals( 'time_start = 0', $query->{'_filter_clause'} );
    $self->assert_deep_equals( [], $query->{'_filter_params'});

    $query->set_filter_clause( 'time_start > ?', 0 );
    $self->assert_str_equals( 'time_start > ?', $query->{'_filter_clause'} );
    $self->assert_deep_equals( [ 0 ], $query->{'_filter_params'});

    $query->set_filter_clause( undef );
    $self->assert_null( $query->{'_filter_clause'},
                        "filter_clause attribute should have been cleared" );
    $self->assert_deep_equals( [], $query->{'_filter_params'});

    $query->set_filter_clause( '' );
    $self->assert_null( $query->{'_filter_clause'},
                        "filter_clause attribute should still be cleared" );
    $self->assert_deep_equals( [], $query->{'_filter_params'});
}

sub test_sql_params {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->set_filter_clause( 'time_start = 0' );
    $self->assert_deep_equals( [], $query->sql_params() );

    $query->set_filter_clause( 'time_start = ?', '12345678' );
    $self->assert_deep_equals( [ '12345678' ], $query->sql_params() );

    my $nested = $query->create_nested_query();
    $nested->set_filter_clause( 'user IN ?', "( 'wdankers', 'flacoste' )" );
    $self->assert_deep_equals( [ '12345678', "( 'wdankers', 'flacoste' )"],
                               $nested->sql_params() );

    my $nested2 = $nested->create_nested_query();
    $nested2->set_filter_clause( 'file LIKE ? ', "%.mpg" );
    $self->assert_deep_equals( [ '12345678', "( 'wdankers', 'flacoste' )",
                                 '%.mpg' ],
                               $nested2->sql_params() );
}

sub test_join_stream {
    my $self = $_[0];

    $self->assert_deep_equals( [], $self->{'query'}{'_joined_streams'} );

    $self->assert_dies( qr/there is no schema: 'bad_schema'/,
                        sub { $self->{'query'}->join_stream( 'bad_schema' ) } );
    $self->assert_dies( qr/cannot join 'test-derived' with 'test'/,
                        sub { $self->{'query'}->join_stream( 'test-derived') } );

    my $nested = $self->{'query'}->create_nested_query();
    $self->assert_dies( qr/can only join streams on root query/,
                        sub { $nested->join_stream( 'test-extended' ) } );

    $self->{'query'}->join_stream( 'test-extended' );
    $self->assert_deep_equals( [ 'test-extended' ],
                               $self->{'query'}{'_joined_streams'} );
    $self->assert_deep_equals( [ 'test', 'test-extended' ],
                               $self->{'query'}->joined_streams() );

    $self->assert_dies( qr/'test-extended' is already joined/,
                        sub { $self->{'query'}->join_stream( 'test-extended' )});

    my $query = new Lire::DlfQuery( 'test-extended' );
    $self->assert_dies( qr/'test-extended' is already joined/,
                        sub { $query->join_stream( 'test-extended' )});

}

sub test_execute {
    my $self = $_[0];

    $self->set_up_test_store();

    my $query = $self->{'query'};
    $query->add_field( 'user' );
    $query->add_field( 'file' );

    $self->assert_dies( qr/missing 'store' parameter/,
                        sub { $query->execute () } );

    my $result = $query->execute( $self->{'store'} );
    $self->assert_isa( 'Lire::DlfResult', $result );
}

sub test_execute_summary {
    my $self = $_[0];

    $self->set_up_test_store();

    my $query = $self->{'query'};
    $query->add_group_field( 'user' );
    $query->add_aggr_field( 'downloads', 'count()' );

    $self->assert_dies( qr/missing 'store' parameter/,
                        sub { $query->execute_summary () } );

    my $result = $query->execute_summary( $self->{'store'} );
    $self->assert_isa( 'Lire::DlfResult', $result );
    my $summary = $result->next_row();
    $self->assert( ! $result->next_row(),
                   "only one row should be returned by summary on top-level query" );
}

sub test_execute_no_stream {
    my $self = $_[0];

    $self->set_up_test_store();

    my $query = new Lire::DlfQuery( 'test' );
    $query->{'_joined_streams'} = [ 'test-extended' ];
    $self->assert_dies( qr/store doesn't contain a 'test-extended' stream/,
                        sub { $query->execute_summary( $self->{'store'} ) } );
    $self->assert_dies( qr/store doesn't contain a 'test-extended' stream/,
                        sub { $query->execute( $self->{'store'} ) } );
}

sub test__sql_select_clause_quoting {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_group_field( "connection_id" );
    $query->add_group_field( "transfer-complete" );
    $query->add_group_field( "total-requests", "count()" );
    $query->add_group_field( "period", "lr_timegroup(time_start)" );
    $self->assert_str_equals( <<ESQL, $query->_sql_select_clause() . "\n");
SELECT connection_id,
       "transfer-complete",
       count() AS "total-requests",
       lr_timegroup(time_start) AS period
ESQL
}

sub test__sql_from_clause {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_str_equals( "FROM dlf_test", $query->_sql_from_clause() );

    $query->{'_stream_name'} = "test-extended";
    $self->assert_str_equals( 'FROM dlf_test, "dlf_test-extended"',
                              $query->_sql_from_clause() );

    $query->{'_stream_name'} = 'test';
    $query->{'_joined_streams'} = [ "test-extended" ];
    $self->assert_str_equals( 'FROM dlf_test, "dlf_test-extended"',
                              $query->_sql_from_clause() );

    $query->{'_stream_name'} = 'test-derived';
    $query->{'_joined_streams'} = [ "test-extended" ];
    $self->assert_str_equals( 'FROM "dlf_test-derived", "dlf_test-derived_links", "dlf_test-extended"',
                              $query->_sql_from_clause() );

    $query->{'_joined_streams'} = [ "test" ];
    $self->assert_str_equals( 'FROM "dlf_test-derived", "dlf_test-derived_links", dlf_test',
                              $query->_sql_from_clause() );

    $query->{'_joined_streams'} = [];
    $self->assert_str_equals( 'FROM "dlf_test-derived"',
                              $query->_sql_from_clause() );

    $query->{'_joined_streams'} = [ 'test-extended2' ];
    $self->assert_str_equals( 'FROM "dlf_test-derived", "dlf_test-extended2"',
                              $query->_sql_from_clause() );
}

sub test__sql_group_by_clause_quoting {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_group_field( "connection_id" );
    $query->add_group_field( "transfer-complete" );
    $query->add_group_field( "total-requests", "count()" );
    $query->add_group_field( "period", "lr_timegroup(time_start)" );
    $self->assert_str_equals( q{GROUP BY connection_id, "transfer-complete", "total-requests", period},
                          $query->_sql_group_by_clause() );
}

sub test__as_sql {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $query->add_group_field( 'client_host' );
    $query->add_aggr_field( 'requests', 'count()' );
    $query->add_aggr_field( 'bytes', 'sum(file_size)' );
    $query->set_order_by_clause( "period, bytes DESC" );
    $query->set_filter_clause( "file = ?", "index.html" );
    $query->set_limit( 5 );

    $self->assert_str_equals ( <<SQLS, $query->_as_sql() );
SELECT lr_timegroup(time_start) AS period,
       client_host,
       count() AS requests,
       sum(file_size) AS bytes
FROM dlf_test
WHERE file = ?
GROUP BY period, client_host
ORDER BY period, bytes DESC
LIMIT 5
SQLS
}

sub test__sql_select_fields {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'file' );
    my $nquery1 = $query->create_nested_query();
    $nquery1->add_group_field( 'connection_id' );
    $nquery1->add_aggr_field( 'bytes', 'sum(file_size)' );

    my $nquery2 = $query->create_nested_query();
    $nquery2->add_aggr_field( 'requests', 'count(*)' );
    $nquery2->add_field( 'connection_id' );

    $self->assert_deep_equals( [ { 'field' => 'file',
                                   'type' => 'group',
                                   'alias' => undef },
                                 { 'field' => 'sum(file_size)',
                                   'type' => 'aggr',
                                   'alias' => 'bytes' },
                                 { 'field' => 'count(*)',
                                   'type' => 'aggr',
                                   'alias' => 'requests' }, ],
                               $query->_sql_select_fields() );
    $self->assert_deep_equals( [ { 'field' => 'sum(file_size)',
                                   'type' => 'aggr',
                                   'alias' => 'bytes' },
                                 { 'field' => 'count(*)',
                                   'type' => 'aggr',
                                   'alias' => 'requests' }, ],
                               $query->_sql_select_fields( 1 ) );

    $self->assert_deep_equals( [ { 'field' => 'connection_id',
                                   'type' => 'simple',
                                   'alias' => undef }, ],
                               $nquery2->_sql_select_fields() );
    $self->assert_deep_equals( [ { 'field' => 'count(*)',
                                   'type' => 'aggr',
                                   'alias' => 'requests' }, ],
                               $nquery2->_sql_select_fields(1) );
}

sub test__sql_where_clause {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_null( $query->_sql_where_clause() );

    $query->{'_filter_clause'} = 'time_stamp = ?';
    $self->assert_str_equals( 'WHERE time_stamp = ?',
                              $query->_sql_where_clause() );

    my $sub = $query->create_nested_query();
    $self->assert_str_equals( 'WHERE time_stamp = ?',
                              $sub->_sql_where_clause() );

    $sub->{'_filter_clause'} = 'connection_id LIKE ?';
    $self->assert_str_equals( 'WHERE time_stamp = ?',
                              $query->_sql_where_clause() );
    $self->assert_str_equals( 'WHERE time_stamp = ? AND connection_id LIKE ?',
                              $sub->_sql_where_clause() );

    $query->{'_stream_name'} = 'test-extended';
    $self->assert_str_equals( 'WHERE dlf_test.dlf_id = "dlf_test-extended".dlf_id AND time_stamp = ?',
                              $query->_sql_where_clause() );
}

sub test__sql_where_join_elements {
    my $self = $_[0];

    my $query = $self->{'query'};
    $self->assert_deep_equals( [], $query->_sql_where_join_elements() );

    $query->{'_stream_name'} = 'test-extended';
    $self->assert_deep_equals( [ 'dlf_test.dlf_id = "dlf_test-extended".dlf_id' ],
                               $query->_sql_where_join_elements() );

    $query->{'_stream_name'} = 'test';
    $query->{'_joined_streams'} = [ 'test-extended' ];
    $self->assert_deep_equals( [ 'dlf_test.dlf_id = "dlf_test-extended".dlf_id' ],
                               $query->_sql_where_join_elements() );

    $query->{'_stream_name'} = 'test-derived';
    $query->{'_joined_streams'} = [ 'test', 'test-extended', 'test-extended2' ];
    $self->assert_deep_equals( [ '"dlf_test-derived".dlf_id = "dlf_test-derived_links".src_id',
                                 '"dlf_test-derived_links".link_id = dlf_test.dlf_id',
                                 '"dlf_test-derived_links".link_id = "dlf_test-extended".dlf_id',
                                 '"dlf_test-derived".dlf_id = "dlf_test-extended2".dlf_id' ],
                               $query->_sql_where_join_elements() );
    $query->{'_joined_streams'} = [ 'test-extended2' ];
    $self->assert_deep_equals( [ '"dlf_test-derived".dlf_id = "dlf_test-extended2".dlf_id' ],
                               $query->_sql_where_join_elements() );
}

sub test__as_sql_with_nested_query {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $query->add_aggr_field( 'nrecords', 'count()' );
    $query->set_filter_clause( 'time_start > ?', "123456789" );
    $query->set_order_by_clause( "period" );

    my $nquery1 = $query->create_nested_query();
    $nquery1->add_group_field( 'connection_id' );
    $nquery1->add_aggr_field( 'bytes', 'sum(file_size)' );
    $nquery1->set_order_by_clause( "bytes DESC" );

    my $nquery2 = $query->create_nested_query();
    $nquery2->add_group_field( "file" );
    $nquery2->add_aggr_field( "downloads", "count()" );
    $nquery2->set_filter_clause( 'file = ?', "index.html" );

    $self->assert_str_equals( <<SQLS, $query->_as_sql() );
SELECT lr_timegroup(time_start) AS period,
       count() AS nrecords,
       sum(file_size) AS bytes,
       count() AS downloads
FROM dlf_test
WHERE time_start > ?
GROUP BY period
ORDER BY period
SQLS

    $self->assert_str_equals( <<SQLS, $nquery1->_as_sql() );
SELECT lr_timegroup(time_start) AS period,
       connection_id,
       sum(file_size) AS bytes
FROM dlf_test
WHERE time_start > ?
GROUP BY period, connection_id
ORDER BY period, bytes DESC
SQLS

    $self->assert_str_equals( <<SQLS, $nquery2->_as_sql() );
SELECT lr_timegroup(time_start) AS period,
       file,
       count() AS downloads
FROM dlf_test
WHERE time_start > ? AND file = ?
GROUP BY period, file
ORDER BY period
SQLS
}

sub test__as_sql_select_only {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_field( 'requests', 'count()' );
    $query->add_field( 'bytes', 'sum(file_size)' );

    $self->assert_str_equals ( <<SQLS, $query->_as_sql() );
SELECT count() AS requests,
       sum(file_size) AS bytes
FROM dlf_test
SQLS
}

sub test__as_summary_sql {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $query->add_group_field( 'client_host' );
    $query->add_aggr_field( 'requests', 'count()' );
    $query->add_aggr_field( 'bytes', 'sum(file_size)' );
    $query->set_order_by_clause( "period, bytes DESC" );
    $query->set_filter_clause( "file = ?", "index.html" );
    $query->set_limit( 5 );

    $self->assert_str_equals ( <<SQLS, $query->_as_summary_sql() );
SELECT count() AS requests,
       sum(file_size) AS bytes
FROM dlf_test
WHERE file = ?
SQLS
}

sub test__as_summary_sql_nested_query {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $query->add_aggr_field( 'nrecords', 'count()' );
    $query->set_filter_clause( 'time_start > ?', 123456789 );
    $query->set_order_by_clause( "period" );

    my $nquery1 = $query->create_nested_query();
    $nquery1->add_group_field( 'connection_id' );
    $nquery1->add_aggr_field( 'bytes', 'sum(file_size)' );
    $nquery1->set_order_by_clause( "bytes DESC" );

    my $nquery2 = $query->create_nested_query();
    $nquery2->add_group_field( "file" );
    $nquery2->add_aggr_field( "downloads", "count()" );
    $nquery2->set_filter_clause( 'file = ?', "index.html" );

    $self->assert_str_equals( <<SQLS, $query->_as_summary_sql() );
SELECT count() AS nrecords,
       sum(file_size) AS bytes,
       count() AS downloads
FROM dlf_test
WHERE time_start > ?
SQLS

    $self->assert_str_equals( <<SQLS, $nquery1->_as_summary_sql() );
SELECT lr_timegroup(time_start) AS period,
       sum(file_size) AS bytes
FROM dlf_test
WHERE time_start > ?
GROUP BY period
ORDER BY period
SQLS

    $self->assert_str_equals( <<SQLS, $nquery2->_as_summary_sql() );
SELECT lr_timegroup(time_start) AS period,
       count() AS downloads
FROM dlf_test
WHERE time_start > ? AND file = ?
GROUP BY period
ORDER BY period
SQLS
}

sub test__as_summary_sql_no_where {
    my $self = $_[0];

    my $query = $self->{'query'};

    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $query->add_group_field( 'client_host' );
    $query->add_aggr_field( 'requests', 'count()' );
    $query->add_aggr_field( 'bytes', 'sum(file_size)' );
    $query->set_order_by_clause( "period, bytes DESC" );
    $query->set_limit( 5 );

    $self->assert_str_equals ( <<SQLS, $query->_as_summary_sql() );
SELECT count() AS requests,
       sum(file_size) AS bytes
FROM dlf_test
SQLS
}

sub test__as_sql_simple_field_nested_query {
    my $self = $_[0];

    my $query = $self->{'query'};
    $query->add_group_field( 'period', 'lr_timegroup(time_start)' );
    $query->set_order_by_clause( 'period' );
    my $nquery = $query->create_nested_query();
    $nquery->add_field( 'time_start' );
    $nquery->add_field( 'user' );
    $nquery->add_field( 'file' );
    $nquery->add_field( 'time_taken' );
    $self->assert_str_equals ( <<SQLS, $nquery->_as_sql() );
SELECT lr_timegroup(time_start) AS period,
       time_start,
       user,
       file,
       time_taken
FROM dlf_test
ORDER BY period
SQLS
}

sub test_create_nested_query {
    my $self = $_[0];

    my $query = $self->{'query'};

    $self->assert_null( $query->{'_parent'},
                        "new query should have NULL parent" );
    $self->assert_deep_equals( [], $query->{'_nested_queries'} );

    my $nested = $self->{'query'}->create_nested_query();
    $self->assert_isa( 'Lire::DlfQuery', $nested );

    $self->assert_str_equals( $query->{'_stream_name'},
                              $nested->{'_stream_name'} );
    $self->assert_str_equals( $query, $nested->{'_parent'} );
    $self->assert_deep_equals( [ $nested ], $query->{'_nested_queries'} );
}

sub test_release {
    my $self = $_[0];

    my $query = $self->{'query'};
    my $nested = $query->create_nested_query();
    $query->release();
    $self->assert_null( $nested->{'_parent'},
                        "_parent attribute of nested queries should be undef" );
}

sub test__root_query {
    my $self = $_[0];

    $self->assert_str_equals( $self->{'query'},
                              $self->{'query'}->_root_query() );

    my $nested = $self->{'query'}->create_nested_query();
    $self->assert_str_equals( $self->{'query'}, $nested->_root_query() );

    my $nested2 = $nested->create_nested_query();
    $self->assert_str_equals( $self->{'query'}, $nested2->_root_query() );
}

sub test__sql_order_by_clause {
    my $self = $_[0];

    my $query = $self->{'query'};
    my $clause = $query->_sql_order_by_clause();
    $self->assert_null( $clause, '_sql_order_by_clause() should be undef' );

    $query->{'_order_by'} = 'connection_id, user';
    $self->assert_str_equals( 'ORDER BY connection_id, user',
                          $query->_sql_order_by_clause() );

    my $squery = $query->create_nested_query();
    $self->assert_str_equals( 'ORDER BY connection_id, user',
                          $squery->_sql_order_by_clause() );
    $squery->{'_order_by'} = 'file DESC';
    $self->assert_str_equals( 'ORDER BY connection_id, user, file DESC',
                          $squery->_sql_order_by_clause() );
    $query->{'_order_by'} = undef;
    $self->assert_str_equals( 'ORDER BY file DESC',
                          $squery->_sql_order_by_clause() );
}

1;


syntax highlighted by Code2HTML, v. 0.9.1