diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 9ec882ed3231..b4da92901801 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -228,11 +228,7 @@ public function select($columns = ['*']) $columns = is_array($columns) ? $columns : func_get_args(); foreach ($columns as $as => $column) { - if (is_string($as) && ( - $column instanceof self || - $column instanceof EloquentBuilder || - $column instanceof Closure - )) { + if (is_string($as) && $this->isQueryable($column)) { $this->selectSub($column, $as); } else { $this->columns[] = $column; @@ -335,6 +331,8 @@ protected function createSub($query) * * @param mixed $query * @return array + * + * @throws \InvalidArgumentException */ protected function parseSub($query) { @@ -343,10 +341,25 @@ protected function parseSub($query) } elseif (is_string($query)) { return [$query, []]; } else { - throw new InvalidArgumentException; + throw new InvalidArgumentException( + 'The subquery must be an instance of Closure or Builder, or a string.' + ); } } + /** + * Determine if the value is a query builder instance or a closure. + * + * @param mixed $value + * @return bool + */ + protected function isQueryable($value) + { + return $value instanceof self || + $value instanceof EloquentBuilder || + $value instanceof Closure; + } + /** * Add a new select column to the query. * @@ -358,11 +371,7 @@ public function addSelect($column) $columns = is_array($column) ? $column : func_get_args(); foreach ($columns as $as => $column) { - if (is_string($as) && ( - $column instanceof self || - $column instanceof EloquentBuilder || - $column instanceof Closure - )) { + if (is_string($as) && $this->isQueryable($column)) { if (is_null($this->columns)) { $this->select($this->from.'.*'); } @@ -403,9 +412,7 @@ public function distinct() */ public function from($table, $as = null) { - if ($table instanceof self || - $table instanceof EloquentBuilder || - $table instanceof Closure) { + if ($this->isQueryable($table)) { return $this->fromSub($table, $as); } @@ -890,9 +897,7 @@ public function whereIn($column, $values, $boolean = 'and', $not = false) // If the value is a query builder instance we will assume the developer wants to // look for any values that exists within this given query. So we will add the // query accordingly so that this query is properly executed when it is run. - if ($values instanceof self || - $values instanceof EloquentBuilder || - $values instanceof Closure) { + if ($this->isQueryable($values)) { [$query, $bindings] = $this->createSub($values); $values = [new Expression($query)]; @@ -1806,9 +1811,7 @@ public function orHavingRaw($sql, array $bindings = []) */ public function orderBy($column, $direction = 'asc') { - if ($column instanceof self || - $column instanceof EloquentBuilder || - $column instanceof Closure) { + if ($this->isQueryable($column)) { [$query, $bindings] = $this->createSub($column); $column = new Expression('('.$query.')'); diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 97308d83388d..8fec745f6268 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -1658,6 +1658,10 @@ public function testJoinSub() $expected .= 'inner join (select * from "contacts" where "name" = ?) as "sub2" on "users"."id" = "sub2"."user_id"'; $this->assertEquals($expected, $builder->toSql()); $this->assertEquals(['foo', 1, 'bar'], $builder->getRawBindings()['join']); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->from('users')->joinSub(['foo'], 'sub', 'users.id', '=', 'sub.id'); } public function testJoinSubWithPrefix() @@ -1673,6 +1677,10 @@ public function testLeftJoinSub() $builder = $this->getBuilder(); $builder->from('users')->leftJoinSub($this->getBuilder()->from('contacts'), 'sub', 'users.id', '=', 'sub.id'); $this->assertSame('select * from "users" left join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->from('users')->leftJoinSub(['foo'], 'sub', 'users.id', '=', 'sub.id'); } public function testRightJoinSub() @@ -1680,6 +1688,10 @@ public function testRightJoinSub() $builder = $this->getBuilder(); $builder->from('users')->rightJoinSub($this->getBuilder()->from('contacts'), 'sub', 'users.id', '=', 'sub.id'); $this->assertSame('select * from "users" right join (select * from "contacts") as "sub" on "users"."id" = "sub"."id"', $builder->toSql()); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->from('users')->rightJoinSub(['foo'], 'sub', 'users.id', '=', 'sub.id'); } public function testRawExpressionsInSelect() @@ -1917,6 +1929,13 @@ function (Builder $query) { $this->assertEquals(1, $result); } + public function testInsertUsingInvalidSubquery() + { + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->from('table1')->insertUsing(['foo'], ['bar']); + } + public function testInsertOrIgnoreMethod() { $this->expectException(RuntimeException::class); @@ -2869,6 +2888,10 @@ public function testSubSelect() $builder->selectSub($subBuilder, 'sub'); $this->assertEquals($expectedSql, $builder->toSql()); $this->assertEquals($expectedBindings, $builder->getBindings()); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getPostgresBuilder(); + $builder->selectSub(['foo'], 'sub'); } public function testSqlServerWhereDate() @@ -3421,6 +3444,10 @@ public function testFromSub() }, 'sessions')->where('bar', '<', '10'); $this->assertSame('select * from (select max(last_seen_at) as last_seen_at from "user_sessions" where "foo" = ?) as "sessions" where "bar" < ?', $builder->toSql()); $this->assertEquals(['1', '10'], $builder->getBindings()); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->fromSub(['invalid'], 'sessions')->where('bar', '<', '10'); } public function testFromSubWithPrefix() @@ -3441,6 +3468,10 @@ public function testFromSubWithoutBindings() $query->select(new Raw('max(last_seen_at) as last_seen_at'))->from('user_sessions'); }, 'sessions'); $this->assertSame('select * from (select max(last_seen_at) as last_seen_at from "user_sessions") as "sessions"', $builder->toSql()); + + $this->expectException(InvalidArgumentException::class); + $builder = $this->getBuilder(); + $builder->fromSub(['invalid'], 'sessions'); } public function testFromRaw()