Skip to content

Commit

Permalink
Merge pull request #63 from efcor/master
Browse files Browse the repository at this point in the history
fix bug where Model::create() does not work with query logging
  • Loading branch information
jfelder authored Aug 30, 2023
2 parents c805c0e + af7919d commit 10c4c75
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 66 deletions.
33 changes: 32 additions & 1 deletion src/Jfelder/OracleDB/OracleConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,41 @@ public function bindValues($statement, $bindings)
$statement->bindValue(
$key,
$value,
is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
match (true) {
is_int($value) => PDO::PARAM_INT,
is_bool($value) => PDO::PARAM_BOOL,
is_null($value) => PDO::PARAM_NULL,
is_resource($value) => PDO::PARAM_LOB,
default => PDO::PARAM_STR
},
);
}
}

/**
* Run an "insert get ID" statement against an oracle database.
*
* @param string $query
* @param array $bindings
* @return int
*/
public function oracleInsertGetId($query, $bindings = [])
{
return $this->run($query, $bindings, function ($query, $bindings) {
$last_insert_id = 0;

$statement = $this->getPdo()->prepare($query);

$this->bindValues($statement, $this->prepareBindings($bindings));

// bind final param to a var to capture the id obtained by the query's "returning id into" clause
$statement->bindParam(count($bindings), $last_insert_id, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, 8);

$this->recordsHaveBeenModified();

$statement->execute();

return (int) $last_insert_id;
});
}
}
53 changes: 2 additions & 51 deletions src/Jfelder/OracleDB/Query/Processors/OracleProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Processors\Processor as Processor;
use Jfelder\OracleDB\OCI_PDO\OCI;

class OracleProcessor extends Processor
{
Expand All @@ -14,38 +13,12 @@ class OracleProcessor extends Processor
* @param \Illuminate\Database\Query\Builder $query
* @param string $sql
* @param array $values
* @param string $sequence
* @param string $sequence no effect; only for method signature compatibility
* @return int
*/
public function processInsertGetId(Builder $query, $sql, $values, $sequence = null)
{
$counter = 0;
$last_insert_id = 0;

// Get PDO object
$pdo = $query->getConnection()->getPdo();

// get PDO statment object
$stmt = $pdo->prepare($sql);

// PDO driver params are 1-based so ++ has to be before bindValue
// OCI driver params are 0-based so no ++ before bindValue
if (get_class($pdo) != OCI::class) {
$counter++;
}

// bind each parameter from the values array to their location in the
foreach ($values as $k => $v) {
$stmt->bindValue($counter++, $v, $this->bindType($v));
}

// bind output param for the returning clause
$stmt->bindParam($counter, $last_insert_id, \PDO::PARAM_INT | \PDO::PARAM_INPUT_OUTPUT, 8);

// execute statement
$stmt->execute();

return (int) $last_insert_id;
return $query->getConnection()->oracleInsertGetId($sql, $values);
}

/**
Expand All @@ -64,26 +37,4 @@ public function processColumnListing($results)

return array_map($mapping, $results);
}

/*
* Determine parameter type passed in
*
* @param mixed $param
* @return \PDO::PARAM_* type
*/

private function bindType($param)
{
if (is_int($param)) {
$param = \PDO::PARAM_INT;
} elseif (is_bool($param)) {
$param = \PDO::PARAM_BOOL;
} elseif (is_null($param)) {
$param = \PDO::PARAM_NULL;
} else {
$param = \PDO::PARAM_STR;
}

return $param;
}
}
79 changes: 79 additions & 0 deletions tests/OracleDBConnectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Jfelder\OracleDB\Tests;

use Jfelder\OracleDB\OCI_PDO\OCI;
use Jfelder\OracleDB\OCI_PDO\OCIStatement;
use Jfelder\OracleDB\OracleConnection;
use Mockery as m;
use PDO;
use PHPUnit\Framework\TestCase;

class OracleDBConnectionTest extends TestCase
{
protected function setUp(): void
{
if (! extension_loaded('oci8')) {
$this->markTestSkipped('The oci8 extension is not available.');
}
}

protected function tearDown(): void
{
m::close();
}

public function testOracleInsertGetIdProperlyCallsPDO()
{
$pdo = $this->getMockBuilder(OracleDBConnectionTestMockPDO::class)->onlyMethods(['prepare'])->getMock();
$statement = $this->getMockBuilder(OracleDBConnectionTestMockOCIStatement::class)->onlyMethods(['execute', 'bindValue', 'bindParam'])->getMock();
$statement->expects($this->once())->method('bindValue')->with(0, 'bar', 2);
$statement->expects($this->once())->method('bindParam')->with(1, 0, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, 8);
$statement->expects($this->once())->method('execute');
$pdo->expects($this->once())->method('prepare')->with($this->equalTo('foo'))->willReturn($statement);
$mock = $this->getMockConnection(['prepareBindings'], $pdo);
$mock->expects($this->once())->method('prepareBindings')->with($this->equalTo(['bar']))->willReturn(['bar']);
$results = $mock->oracleInsertGetId('foo', ['bar']);
$this->assertSame(0, $results);
$log = $mock->getQueryLog();
$this->assertSame('foo', $log[0]['query']);
$this->assertEquals(['bar'], $log[0]['bindings']);
$this->assertIsNumeric($log[0]['time']);
}

protected function getMockConnection($methods = [], $pdo = null)
{
$pdo = $pdo ?: new OracleDBConnectionTestMockPDO;
$defaults = ['getDefaultQueryGrammar', 'getDefaultPostProcessor', 'getDefaultSchemaGrammar'];
$connection = $this->getMockBuilder(OracleConnection::class)->onlyMethods(array_merge($defaults, $methods))->setConstructorArgs([$pdo])->getMock();
$connection->enableQueryLog();

return $connection;
}
}

class OracleDBConnectionTestMockPDO extends OCI
{
public function __construct()
{
//
}

public function __destruct()
{
//
}
}

class OracleDBConnectionTestMockOCIStatement extends OCIStatement
{
public function __construct()
{
//
}

public function __destruct()
{
//
}
}
17 changes: 3 additions & 14 deletions tests/OracleDBOCIProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace Jfelder\OracleDB\Tests;

use ProcessorTestOCIStub;
use ProcessorTestOCIStatementStub;
use Jfelder\OracleDB\OracleConnection;
use Jfelder\OracleDB\Query\OracleBuilder;
use Jfelder\OracleDB\Query\Processors\OracleProcessor;
Expand All @@ -28,24 +26,15 @@ public function tearDown(): void

public function testInsertGetIdProcessing()
{
$stmt = m::mock(new ProcessorTestOCIStatementStub());
$stmt->shouldReceive('bindValue')->times(4)->withAnyArgs();
$stmt->shouldReceive('bindParam')->once()->with(5, 0, \PDO::PARAM_INT | \PDO::PARAM_INPUT_OUTPUT, 8);
$stmt->shouldReceive('execute')->once()->withNoArgs();

$pdo = m::mock(new ProcessorTestOCIStub());
$pdo->shouldReceive('prepare')->once()->with('sql')->andReturn($stmt);

$connection = m::mock(OracleConnection::class);
$connection->shouldReceive('getPdo')->once()->andReturn($pdo);
$connection->shouldReceive('oracleInsertGetId')->once()->with('sql', [1, 'foo', true, null])->andReturn(1234);

$builder = m::mock(OracleBuilder::class);
$builder->shouldReceive('getConnection')->once()->andReturn($connection);

$processor = new OracleProcessor;

$result = $processor->processInsertGetId($builder, 'sql', [1, 'foo', true, null], 'id');
$this->assertSame(0, $result);
$result = $processor->processInsertGetId($builder, 'sql', [1, 'foo', true, null]);
$this->assertSame(1234, $result);
}

public function testProcessColumnListing()
Expand Down

0 comments on commit 10c4c75

Please sign in to comment.