use strict;
use warnings;
use lib 't/lib';
use Fey::Test;
use Test::More;
use Fey::SQL;
my $s = Fey::Test->mock_test_schema_with_fks();
my $dbh = Fey::Test->mock_dbh();
{
my $q = Fey::SQL->new_select();
eval { $q->from() };
like(
$@, qr/from\(\) called with invalid parameters \(\)/,
'from() without any parameters is an error'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User') );
is( $q->from_clause($dbh), q{FROM "User"},
'from_clause() for one table' );
}
{
my $q = Fey::SQL->new_select();
eval { $q->from('foo') };
like(
$@, qr/from\(\) called with invalid parameters \(foo\)/,
'from() called with one non-table argument'
);
}
{
my $q = Fey::SQL->new_select();
my $alias = $s->table('User')->alias( alias_name => 'UserA' );
$q->from($alias);
is(
$q->from_clause($dbh), q{FROM "User" AS "UserA"},
'from_clause() for one table alias'
);
}
{
my $q = Fey::SQL->new_select();
my $user_t = $s->table('User');
my $alias = $user_t->alias( alias_name => 'UserA' );
my $fk = Fey::FK->new(
source_columns => $user_t->column('user_id'),
target_columns => $alias->column('user_id'),
);
$q->from( $user_t, $alias, $fk );
is(
$q->from_clause($dbh),
q{FROM "User" JOIN "User" AS "UserA" ON ("User"."user_id" = "UserA"."user_id")},
'from_clause() with self-join to alias using fake FK'
);
}
{
my $q = Fey::SQL->new_select();
eval { $q->from( $s->table('User'), $s->table('Group') ) };
like(
$@, qr/do not share a foreign key/,
'Cannot join two tables without a foreign key'
);
}
{
my $q = Fey::SQL->new_select();
eval { $q->from( $s->table('User'), 'foo' ) };
like(
$@,
qr/\Qthe first two arguments to from() were not valid (not tables or something else joinable)/,
'from() called with two args, one not a table'
);
eval { $q->from( 'foo', $s->table('User') ) };
like(
$@,
qr/\Qthe first two arguments to from() were not valid (not tables or something else joinable)/,
'from() called with two args, one not a table'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), $s->table('UserGroup') );
my $sql
= q{FROM "User" JOIN "UserGroup" ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables, fk not provided'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), $s->table('UserGroup') );
$q->from( $s->table('UserGroup'), $s->table('Group') );
my $sql
= q{FROM "UserGroup" JOIN "Group" ON ("UserGroup"."group_id" = "Group"."group_id")};
$sql .= q{ JOIN "User" ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two joins'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User') );
$q->from( $s->table('User'), $s->table('UserGroup') );
my $sql
= q{FROM "User" JOIN "UserGroup" ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for table alone first, then table in join'
);
}
{
my $frag = Fey::SQL::Fragment::Join->new(
table1 => $s->table('User'),
table2 => $s->table('UserGroup'),
);
is(
$frag->sql_with_alias(
'Fey::FakeDBI', {
$s->table('User')->id() => 1,
$s->table('UserGroup')->id() => 1,
},
),
q{},
'join fragment ignores tables already seen'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), $s->table('UserGroup') );
$q->from( $s->table('Group'), $s->table('UserGroup') );
my $sql
= q{FROM "Group" JOIN "UserGroup" ON ("UserGroup"."group_id" = "Group"."group_id")};
$sql .= q{ JOIN "User" ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two joins, seen table comes second in second clause'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User') );
$q->from( $s->table('UserGroup') );
$q->from( $s->table('Group') );
my $sql = q{FROM "Group", "User", "UserGroup"};
is(
$q->from_clause($dbh), $sql,
'from_clause() for three tables with no joins'
);
}
{
my $q = Fey::SQL->new_select();
my @t = ( $s->table('User'), $s->table('UserGroup') );
my ($fk) = $s->foreign_keys_between_tables(@t);
$q->from( @t, $fk );
my $sql
= q{FROM "User" JOIN "UserGroup" ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables with fk provided'
);
}
{
my $q = Fey::SQL->new_select();
my $fk = Fey::FK->new(
source_columns => $s->table('User')->column('user_id'),
target_columns => $s->table('UserGroup')->column('group_id'),
);
$s->add_foreign_key($fk);
eval { $q->from( $s->table('User'), $s->table('UserGroup') ) };
like(
$@, qr/more than one foreign key/,
'Cannot auto-join two tables with >1 foreign key'
);
$s->remove_foreign_key($fk);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), 'left', $s->table('UserGroup') );
my $sql = q{FROM "User" LEFT OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables with left outer join'
);
}
{
my $q = Fey::SQL->new_select();
my $user_alias = $s->table('User')->alias( alias_name => 'U' );
my $user_group_alias
= $s->table('UserGroup')->alias( alias_name => 'UG' );
$q->from( $user_alias, 'left', $user_group_alias );
my $sql = q{FROM "User" AS "U" LEFT OUTER JOIN "UserGroup" AS "UG"};
$sql .= q{ ON ("UG"."user_id" = "U"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two table aliases with left outer join'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), $s->table('UserGroup') );
$q->from( $s->table('UserGroup'), 'left', $s->table('Group') );
my $sql = q{FROM "UserGroup" LEFT OUTER JOIN "Group" ON};
$sql .= q{ ("UserGroup"."group_id" = "Group"."group_id")};
$sql .= q{ JOIN "User" ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for regular join + left outer join'
);
}
{
my $q = Fey::SQL->new_select();
my @t = ( $s->table('User'), $s->table('UserGroup') );
my ($fk) = $s->foreign_keys_between_tables(@t);
$q->from( $t[0], 'left', $t[1], $fk );
my $sql = q{FROM "User" LEFT OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables with left outer join with explicit fk'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), 'right', $s->table('UserGroup') );
my $sql = q{FROM "User" RIGHT OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables with right outer join'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), 'full', $s->table('UserGroup') );
my $sql = q{FROM "User" FULL OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables with full outer join'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User'), 'full', $s->table('UserGroup') );
my $sql = q{FROM "User" FULL OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for two tables with full outer join'
);
}
{
my $q = Fey::SQL->new_select();
my $q2 = Fey::SQL->new_where( auto_placeholders => 0 );
$q2->where( $s->table('User')->column('user_id'), '=', 2 );
$q->from( $s->table('User'), 'left', $s->table('UserGroup'), $q2 );
my $sql = q{FROM "User" LEFT OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id"};
$sql .= q{ AND "User"."user_id" = 2)};
is(
$q->from_clause($dbh), $sql,
'from_clause() for outer join with where clause'
);
}
{
my $q = Fey::SQL->new_select();
my $q2 = Fey::SQL->new_where( auto_placeholders => 0 );
$q2->where( $s->table('User')->column('user_id'), '=', 2 );
my @t = ( $s->table('User'), $s->table('UserGroup') );
my ($fk) = $s->foreign_keys_between_tables(@t);
$q->from( $t[0], 'left', $t[1], $fk, $q2 );
my $sql = q{FROM "User" LEFT OUTER JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id"};
$sql .= q{ AND "User"."user_id" = 2)};
is(
$q->from_clause($dbh), $sql,
'from_clause() for outer join with where clause() and explicit fk'
);
}
{
my $q = Fey::SQL->new_select();
my $alias = $s->table('User')->alias( alias_name => 'UserA' );
$q->from( $s->table('User'), $s->table('UserGroup') );
$q->from( $alias, $s->table('UserGroup') );
my $sql = q{FROM "User" JOIN "UserGroup"};
$sql .= q{ ON ("UserGroup"."user_id" = "User"."user_id")};
$sql .= q{ JOIN "User" AS "UserA"};
$sql .= q{ ON ("UserGroup"."user_id" = "UserA"."user_id")};
is(
$q->from_clause($dbh), $sql,
'from_clause() for one table alias'
);
}
{
my $q = Fey::SQL->new_select();
eval { $q->from( $s->table('User')->column('user_id') ) };
like(
$@, qr/\Qfrom() called with invalid parameters/,
'passing just a column to from()'
);
}
{
my $q = Fey::SQL->new_select();
eval { $q->from( $s->table('User'), 'foobar', $s->table('UserGroup') ) };
like(
$@, qr/invalid outer join type/,
'invalid outer join type causes an error'
);
}
{
my $q = Fey::SQL->new_select();
eval { $q->from( 'not a table', 'left', $s->table('UserGroup') ) };
like(
$@, qr/from\(\) was called with invalid arguments/,
'invalid outer join type causes an error'
);
}
{
my $q = Fey::SQL->new_select();
eval { $q->from( $s->table('UserGroup'), 'left', 'not a table' ) };
like(
$@, qr/from\(\) was called with invalid arguments/,
'invalid outer join type causes an error'
);
}
{
my $q = Fey::SQL->new_select();
eval {
$q->from( $s->table('User'), 'full', $s->table('UserGroup'),
'invalid' );
};
like(
$@, qr/\Qfrom() called with invalid parameters/,
'passing invalid parameter to from() with outer join'
);
}
{
my $q = Fey::SQL->new_select();
my $subselect = Fey::SQL->new_select();
$subselect->select( $s->table('User')->column('user_id') )
->from( $s->table('User') );
$q->from($subselect);
my $sql = q{FROM ( SELECT "User"."user_id" FROM "User" ) AS "SUBSELECT0"};
is(
$q->from_clause($dbh), $sql,
'from_clause() for subselect'
);
is(
$subselect->alias_name, 'SUBSELECT0',
'subselect alias_name is set after use in from()'
);
}
{
my $q = Fey::SQL->new_select();
my $table = Fey::Table->new( name => 'NewTable' );
eval { $q->from($table) };
like(
$@, qr/\Qfrom() called with invalid parameters/,
'cannot pass a table without a schema to from()'
);
}
{
my $q = Fey::SQL->new_select();
my $table = Fey::Table->new( name => 'NewTable' );
eval { $q->from( $table, $s->table('User') ) };
like(
$@,
qr/\Qthe first two arguments to from() were not valid (not tables or something else joinable)/,
'cannot pass a table without a schema to from() as part of a join'
);
}
{
my $q = Fey::SQL->new_select();
my $table = Fey::Table->new( name => 'NewTable' );
my $non_table = bless {}, 'Thingy';
eval { $q->from( $table, $non_table ) };
like(
$@,
qr/\Qthe first two arguments to from() were not valid (not tables or something else joinable)/,
'cannot pass a table without a schema to from()'
);
}
{
my $q = Fey::SQL->new_select->from( $s->table('User') );
# The bug this exercised was that two aliases were created, but
# since they had the same name, we only ended up with one join
# fragment. Then the column from the second table alias ended up
# going out of scope.
for ( 0 .. 1 ) {
my $table = $s->table('UserGroup')->alias('UserGroup1');
$q->from( $s->table('User'), $table );
$q->where( $table->column('group_id'), '=', 1 );
}
$q->select(1);
my $sql = q{SELECT 1 FROM "User" JOIN "UserGroup" AS "UserGroup1" ON};
$sql .= q{ ("UserGroup1"."user_id" = "User"."user_id")};
$sql
.= q{ WHERE "UserGroup1"."group_id" = ? AND "UserGroup1"."group_id" = ?};
is( $q->sql($dbh), $sql, 'alias shows up in join once and where twice' );
}
{
my $q = Fey::SQL->new_select();
$q->from( $s->table('User') );
$q->from( $s->table('User'), 'left', $s->table('UserGroup') );
my $sql = q{FROM "User" LEFT OUTER JOIN "UserGroup" ON};
$sql .= q{ ("UserGroup"."user_id" = "User"."user_id")};
is( $q->from_clause($dbh), $sql,
'table only shows up once in from, not twice' );
}
{
my $t1 = $s->table('User');
my $t2 = $s->table('UserGroup');
my $where = Fey::SQL->new_where( auto_placeholders => 0 );
$where->where( $t1->column('user_id'), '=', 2 );
my ($fk) = $s->foreign_keys_between_tables( $t1, $t2 );
my $sql = q{FROM "User" JOIN "UserGroup" ON};
$sql .= q{ ("UserGroup"."user_id" = "User"."user_id"};
$sql .= q{ AND "User"."user_id" = 2)};
{
my $q = Fey::SQL->new_select();
$q->from( $t1, $t2, $where );
is(
$q->from_clause($dbh), $sql,
'from_clause() for inner join with where clause'
);
}
{
my $q = Fey::SQL->new_select();
$q->from( $t1, $t2, $fk, $where );
is(
$q->from_clause($dbh), $sql,
'from_clause() for inner join with explicit fk and where clause'
);
}
}
done_testing();