diff --git a/CHANGELOG.md b/CHANGELOG.md index bb9fdeba..1ee8a642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ ## [Unreleased] +## [v8.2.1] - 2020-12-07 + +- Fix query builder bulk insert. [#612] +- Fix [#558]. + +## [v8.2.0] - 2020-12-06 + +- Improve pagination performance. [#611] +- Fixes [#563] + +## [v8.1.3] - 2020-12-06 + +- Fix Model::create() with guarded property. [#609] +- Fix [#596] + ## [v8.1.2] - 2020-12-06 - Fix database presence verifier. [#607] @@ -115,7 +130,10 @@ - Fix [#406], [#404]. - Added more options to Sequence Create Method [#355], credits to [@nikklass]. -[Unreleased]: https://github.com/yajra/laravel-oci8/compare/v8.1.2...8.x +[Unreleased]: https://github.com/yajra/laravel-oci8/compare/v8.2.1...8.x +[v8.2.1]: https://github.com/yajra/laravel-oci8/compare/v8.2.0...v8.2.1 +[v8.2.0]: https://github.com/yajra/laravel-oci8/compare/v8.1.3...v8.2.0 +[v8.1.3]: https://github.com/yajra/laravel-oci8/compare/v8.1.2...v8.1.3 [v8.1.2]: https://github.com/yajra/laravel-oci8/compare/v8.1.1...v8.1.2 [v8.1.1]: https://github.com/yajra/laravel-oci8/compare/v8.1.0...v8.1.1 [v8.1.0]: https://github.com/yajra/laravel-oci8/compare/v8.0.0...v8.1.0 @@ -169,7 +187,13 @@ [#598]: https://github.com/yajra/laravel-oci8/pull/598 [#599]: https://github.com/yajra/laravel-oci8/pull/599 [#607]: https://github.com/yajra/laravel-oci8/pull/607 +[#609]: https://github.com/yajra/laravel-oci8/pull/609 +[#611]: https://github.com/yajra/laravel-oci8/pull/611 +[#612]: https://github.com/yajra/laravel-oci8/pull/612 +[#558]: https://github.com/yajra/laravel-oci8/issue/558 +[#563]: https://github.com/yajra/laravel-oci8/issue/563 +[#596]: https://github.com/yajra/laravel-oci8/issue/596 [#602]: https://github.com/yajra/laravel-oci8/issue/602 [#601]: https://github.com/yajra/laravel-oci8/issue/601 [#590]: https://github.com/yajra/laravel-oci8/issue/590 diff --git a/src/Oci8/Oci8Connection.php b/src/Oci8/Oci8Connection.php index 46830738..e807b43d 100644 --- a/src/Oci8/Oci8Connection.php +++ b/src/Oci8/Oci8Connection.php @@ -487,4 +487,18 @@ public function useCaseSensitiveSession() { return $this->setSessionVars(['NLS_COMP' => 'BINARY', 'NLS_SORT' => 'BINARY']); } + + /** + * Bind values to their parameters in the given statement. + * + * @param \Yajra\Pdo\Oci8\Statement $statement + * @param array $bindings + * @return void + */ + public function bindValues($statement, $bindings) + { + foreach ($bindings as $key => $value) { + $statement->bindValue(is_string($key) ? $key : $key + 1, $value); + } + } } diff --git a/src/Oci8/Query/Grammars/OracleGrammar.php b/src/Oci8/Query/Grammars/OracleGrammar.php index 291b68b3..580a54fb 100644 --- a/src/Oci8/Query/Grammars/OracleGrammar.php +++ b/src/Oci8/Query/Grammars/OracleGrammar.php @@ -161,6 +161,13 @@ protected function compileTableExpression($sql, $constraint, $query) return "select * from ({$sql}) where rownum {$constraint}"; } + if (! is_null($query->limit && ! is_null($query->offset))) { + $start = $query->offset + 1; + $finish = $query->offset + $query->limit; + + return "select t2.* from ( select rownum AS \"rn\", t1.* from ({$sql}) t1 where rownum <= {$finish}) t2 where t2.\"rn\" >= {$start}"; + } + return "select t2.* from ( select rownum AS \"rn\", t1.* from ({$sql}) t1 ) t2 where t2.\"rn\" {$constraint}"; } diff --git a/src/Oci8/Query/Processors/OracleProcessor.php b/src/Oci8/Query/Processors/OracleProcessor.php index 771fb95e..1cde34ec 100644 --- a/src/Oci8/Query/Processors/OracleProcessor.php +++ b/src/Oci8/Query/Processors/OracleProcessor.php @@ -169,7 +169,7 @@ public function processColumnListing($results) $mapping = function ($r) { $r = (object) $r; - return $r->column_name; + return strtolower($r->column_name); }; return array_map($mapping, $results); diff --git a/tests/Database/Oci8QueryBuilderTest.php b/tests/Database/Oci8QueryBuilderTest.php index 4df48419..2771b335 100644 --- a/tests/Database/Oci8QueryBuilderTest.php +++ b/tests/Database/Oci8QueryBuilderTest.php @@ -267,8 +267,10 @@ public function testSubSelectWhereIns() $builder->select('*')->from('users')->whereIn('id', function ($q) { $q->select('id')->from('users')->where('age', '>', 25)->take(3); }); - $this->assertEquals('select * from "USERS" where "ID" in (select t2.* from ( select rownum AS "rn", t1.* from (select "ID" from "USERS" where "AGE" > ?) t1 ) t2 where t2."rn" between 1 and 3)', - $builder->toSql()); + $this->assertEquals( + 'select * from "USERS" where "ID" in (select t2.* from ( select rownum AS "rn", t1.* from (select "ID" from "USERS" where "AGE" > ?) t1 where rownum <= 3) t2 where t2."rn" >= 1)', + $builder->toSql() + ); $this->assertEquals([25], $builder->getBindings()); $builder = $this->getBuilder(); @@ -279,8 +281,10 @@ public function testSubSelectWhereIns() $builder->select('*')->from('users')->whereNotIn('id', function ($q) { $q->select('id')->from('users')->where('age', '>', 25)->take(3); }); - $this->assertEquals('select * from "USERS" where "ID" not in (select t2.* from ( select rownum AS "rn", t1.* from (select "ID" from "USERS" where "AGE" > ?) t1 ) t2 where t2."rn" between 1 and 3)', - $builder->toSql()); + $this->assertEquals( + 'select * from "USERS" where "ID" not in (select t2.* from ( select rownum AS "rn", t1.* from (select "ID" from "USERS" where "AGE" > ?) t1 where rownum <= 3) t2 where t2."rn" >= 1)', + $builder->toSql() + ); $this->assertEquals([25], $builder->getBindings()); } @@ -361,8 +365,10 @@ public function testOffset() $connection = $builder->getConnection(); $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->offset(10); - $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" >= 11', - $builder->toSql()); + $this->assertEquals( + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 10) t2 where t2."rn" >= 11', + $builder->toSql() + ); } public function testLimitsAndOffsets() @@ -371,36 +377,46 @@ public function testLimitsAndOffsets() $connection = $builder->getConnection(); $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->offset(5)->limit(10); - $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 6 and 15', - $builder->toSql()); + $this->assertEquals( + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 15) t2 where t2."rn" >= 6', + $builder->toSql() + ); $builder = $this->getBuilder(); $connection = $builder->getConnection(); $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->skip(5)->take(10); - $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 6 and 15', - $builder->toSql()); + $this->assertEquals( + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 15) t2 where t2."rn" >= 6', + $builder->toSql() + ); $builder = $this->getBuilder(); $connection = $builder->getConnection(); $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->skip(-5)->take(10); - $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 1 and 10', - $builder->toSql()); + $this->assertEquals( + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 10) t2 where t2."rn" >= 1', + $builder->toSql() + ); $builder = $this->getBuilder(); $connection = $builder->getConnection(); $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->forPage(2, 15); - $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 16 and 30', - $builder->toSql()); + $this->assertEquals( + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 30) t2 where t2."rn" >= 16', + $builder->toSql() + ); $builder = $this->getBuilder(); $connection = $builder->getConnection(); $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->forPage(-2, 15); - $this->assertEquals('select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 1 and 15', - $builder->toSql()); + $this->assertEquals( + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 15) t2 where t2."rn" >= 1', + $builder->toSql() + ); } public function testLimitAndOffsetToPaginateOne() @@ -410,7 +426,7 @@ public function testLimitAndOffsetToPaginateOne() $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->offset(0)->limit(1); $this->assertEquals( - 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 1 and 1', + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 1) t2 where t2."rn" >= 1', $builder->toSql() ); @@ -419,7 +435,7 @@ public function testLimitAndOffsetToPaginateOne() $connection->shouldReceive('getConfig')->andReturn(''); $builder->select('*')->from('users')->offset(1)->limit(1); $this->assertEquals( - 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 ) t2 where t2."rn" between 2 and 2', + 'select t2.* from ( select rownum AS "rn", t1.* from (select * from "USERS") t1 where rownum <= 2) t2 where t2."rn" >= 2', $builder->toSql() ); } diff --git a/tests/Functional/ModelTest.php b/tests/Functional/ModelTest.php new file mode 100644 index 00000000..1e2e9a8a --- /dev/null +++ b/tests/Functional/ModelTest.php @@ -0,0 +1,65 @@ + 'John', + 'email' => 'john@example.com', + ]; + + $model = new User; + $model->fill($attributes); + $model->save(); + + $this->assertDatabaseHas('users', $attributes); + } + + /** @test */ + public function it_can_insert_record_using_a_model() + { + User::query()->insert($attributes = [ + 'name' => 'John', + 'email' => 'john@example.com', + ]); + + $this->assertDatabaseHas('users', $attributes); + } + + /** @test */ + public function it_can_create_guarded_model_by_setting_the_property() + { + $count = UserWithGuardedProperty::count(); + + $user = new UserWithGuardedProperty; + $user->name = 'Test'; + $user->email = 'test@example.com'; + $user->save(); + + $this->assertDatabaseCount('users', $count + 1); + } + + /** @test */ + public function it_can_create_guarded_model_using_create_method() + { + $count = UserWithGuardedProperty::count(); + + UserWithGuardedProperty::create([ + 'name' => 'Test', + 'email' => 'test@example.com', + ]); + + $this->assertDatabaseCount('users', $count + 1); + } +} diff --git a/tests/Functional/QueryBuilderTest.php b/tests/Functional/QueryBuilderTest.php new file mode 100644 index 00000000..f0d74a33 --- /dev/null +++ b/tests/Functional/QueryBuilderTest.php @@ -0,0 +1,37 @@ + 'Foo', 'job_id' => null]; + + $this->getConnection()->table('jobs')->insert($data); + + $this->assertDatabaseCount('jobs', 1); + } + + /** @test */ + public function it_can_perform_bulk_inserts() + { + $data = [ + ['name' => 'Foo', 'job_id' => null], + ['name' => 'Bar', 'job_id' => 1], + ['name' => 'Test', 'job_id' => 2], + ['name' => null, 'job_id' => 4], + ['name' => null, 'job_id' => null], + ]; + + $this->getConnection()->table('jobs')->insert($data); + + $this->assertDatabaseCount('jobs', 5); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 5de0f04c..aedd7930 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -22,20 +22,30 @@ protected function migrateDatabase() { /** @var \Illuminate\Database\Schema\Builder $schemaBuilder */ $schemaBuilder = $this->app['db']->connection()->getSchemaBuilder(); - if (! $schemaBuilder->hasTable('users')) { - $schemaBuilder->create('users', function (Blueprint $table) { - $table->increments('id'); - $table->string('name'); - $table->string('email'); - $table->timestamps(); - }); + if ($schemaBuilder->hasTable('users')) { + $schemaBuilder->drop('users'); } + + $schemaBuilder->create('users', function (Blueprint $table) { + $table->increments('id'); + $table->string('name'); + $table->string('email'); + $table->timestamps(); + }); + + if ($schemaBuilder->hasTable('jobs')) { + $schemaBuilder->drop('jobs'); + } + + $schemaBuilder->create('jobs', function (Blueprint $table) { + $table->increments('id'); + $table->string('name')->nullable(); + $table->integer('job_id')->nullable(); + }); } protected function seedDatabase() { - User::query()->truncate(); - collect(range(1, 20))->each(function ($i) { /** @var User $user */ User::query()->create([ diff --git a/tests/User.php b/tests/User.php index f16841bd..1890db3c 100644 --- a/tests/User.php +++ b/tests/User.php @@ -4,6 +4,11 @@ use Illuminate\Database\Eloquent\Model; +/** + * @property int id + * @property string name + * @property string email + */ class User extends Model { protected $guarded = []; diff --git a/tests/UserWithGuardedProperty.php b/tests/UserWithGuardedProperty.php new file mode 100644 index 00000000..149c02d7 --- /dev/null +++ b/tests/UserWithGuardedProperty.php @@ -0,0 +1,10 @@ +