Skip to content

Commit a60569a

Browse files
committed
Reuse connection statement with dissimilar parameters
1 parent 93f5a9c commit a60569a

File tree

3 files changed

+78
-58
lines changed

3 files changed

+78
-58
lines changed

src/PgSqlHandle.php

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -364,61 +364,65 @@ public function prepare(string $sql): Promise
364364
throw new \Error("The connection to the database has been closed");
365365
}
366366

367-
$modifiedSql = Internal\parseNamedParams($sql, $names);
367+
return call(function () use ($sql) {
368+
$modifiedSql = Internal\parseNamedParams($sql, $names);
368369

369-
$name = Handle::STATEMENT_NAME_PREFIX . \sha1($sql);
370+
$name = Handle::STATEMENT_NAME_PREFIX . \sha1($modifiedSql);
370371

371-
if (isset($this->statements[$name])) {
372-
$storage = $this->statements[$name];
372+
if (isset($this->statements[$name])) {
373+
$storage = $this->statements[$name];
373374

374-
if ($storage->promise instanceof Promise) {
375-
return $storage->promise;
376-
}
375+
if ($storage->promise instanceof Promise) {
376+
// Do not return promised prepared statement object, as the $names array may differ.
377+
yield $storage->promise;
378+
}
377379

378-
++$storage->refCount; // Only increase refCount when returning a new object.
380+
++$storage->refCount;
379381

380-
return new Success(new PgSqlStatement($this, $name, $sql, $names));
381-
}
382+
return new PgSqlStatement($this, $name, $sql, $names);
383+
}
382384

383-
$storage = new class {
384-
use Struct;
385-
public $refCount = 1;
386-
public $promise;
387-
};
385+
$storage = new class {
386+
use Struct;
387+
public $refCount = 1;
388+
public $promise;
389+
};
388390

389-
$this->statements[$name] = $storage;
391+
$this->statements[$name] = $storage;
390392

391-
return $storage->promise = call(function () use ($storage, $name, $names, $sql, $modifiedSql) {
392393
try {
393-
/** @var resource $result PostgreSQL result resource. */
394-
$result = yield from $this->send("pg_send_prepare", $name, $modifiedSql);
394+
yield ($storage->promise = call(function () use ($name, $modifiedSql) {
395+
$result = yield from $this->send("pg_send_prepare", $name, $modifiedSql);
396+
397+
switch (\pg_result_status($result, \PGSQL_STATUS_LONG)) {
398+
case \PGSQL_COMMAND_OK:
399+
return $name; // Statement created successfully.
400+
401+
case \PGSQL_NONFATAL_ERROR:
402+
case \PGSQL_FATAL_ERROR:
403+
$diagnostics = [];
404+
foreach (self::DIAGNOSTIC_CODES as $fieldCode => $description) {
405+
$diagnostics[$description] = \pg_result_error_field($result, $fieldCode);
406+
}
407+
throw new QueryExecutionError(\pg_result_error($result), $diagnostics);
408+
409+
case \PGSQL_BAD_RESPONSE:
410+
throw new FailureException(\pg_result_error($result));
411+
412+
default:
413+
// @codeCoverageIgnoreStart
414+
throw new FailureException("Unknown result status");
415+
// @codeCoverageIgnoreEnd
416+
}
417+
}));
395418
} catch (\Throwable $exception) {
396419
unset($this->statements[$name]);
397420
throw $exception;
398421
} finally {
399422
$storage->promise = null;
400423
}
401424

402-
switch (\pg_result_status($result, \PGSQL_STATUS_LONG)) {
403-
case \PGSQL_COMMAND_OK:
404-
return new PgSqlStatement($this, $name, $sql, $names);
405-
406-
case \PGSQL_NONFATAL_ERROR:
407-
case \PGSQL_FATAL_ERROR:
408-
$diagnostics = [];
409-
foreach (self::DIAGNOSTIC_CODES as $fieldCode => $description) {
410-
$diagnostics[$description] = \pg_result_error_field($result, $fieldCode);
411-
}
412-
throw new QueryExecutionError(\pg_result_error($result), $diagnostics);
413-
414-
case \PGSQL_BAD_RESPONSE:
415-
throw new FailureException(\pg_result_error($result));
416-
417-
default:
418-
// @codeCoverageIgnoreStart
419-
throw new FailureException("Unknown result status");
420-
// @codeCoverageIgnoreEnd
421-
}
425+
return new PgSqlStatement($this, $name, $sql, $names);
422426
});
423427
}
424428

src/PqHandle.php

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -385,34 +385,37 @@ public function prepare(string $sql): Promise
385385
throw new \Error("The connection to the database has been closed");
386386
}
387387

388-
$modifiedSql = Internal\parseNamedParams($sql, $names);
388+
return call(function () use ($sql) {
389+
$modifiedSql = Internal\parseNamedParams($sql, $names);
389390

390-
$name = Handle::STATEMENT_NAME_PREFIX . \sha1($sql);
391+
$name = Handle::STATEMENT_NAME_PREFIX . \sha1($modifiedSql);
391392

392-
if (isset($this->statements[$name])) {
393-
$storage = $this->statements[$name];
393+
if (isset($this->statements[$name])) {
394+
$storage = $this->statements[$name];
394395

395-
if ($storage->promise instanceof Promise) {
396-
return $storage->promise;
397-
}
396+
if ($storage->promise instanceof Promise) {
397+
// Do not return promised prepared statement object, as the $names array may differ.
398+
yield $storage->promise;
399+
}
398400

399-
++$storage->refCount; // Only increase refCount when returning a new object.
401+
++$storage->refCount;
400402

401-
return new Success(new PqStatement($this, $name, $sql, $names));
402-
}
403+
return new PqStatement($this, $name, $sql, $names);
404+
}
403405

404-
$storage = new class {
405-
use Struct;
406-
public $refCount = 1;
407-
public $promise;
408-
public $statement;
409-
};
406+
$storage = new class {
407+
use Struct;
408+
public $refCount = 1;
409+
public $promise;
410+
public $statement;
411+
};
410412

411-
$this->statements[$name] = $storage;
413+
$this->statements[$name] = $storage;
412414

413-
return $storage->promise = call(function () use ($storage, $names, $name, $sql, $modifiedSql) {
414415
try {
415-
$storage->statement = yield from $this->send([$this->handle, "prepareAsync"], $name, $modifiedSql);
416+
$storage->statement = yield (
417+
$storage->promise = new Coroutine($this->send([$this->handle, "prepareAsync"], $name, $modifiedSql))
418+
);
416419
} catch (\Throwable $exception) {
417420
unset($this->statements[$name]);
418421
throw $exception;

test/AbstractLinkTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,19 @@ public function testPrepareSimilarQueryReturnsDifferentStatements()
361361
$this->assertInstanceOf(Statement::class, $statement2);
362362

363363
$this->assertNotSame($statement1, $statement2);
364+
365+
$data = $this->getData()[0];
366+
367+
$results = yield [$statement1->execute([$data[0]]), $statement2->execute(['domain' => $data[0]])];
368+
369+
foreach ($results as $result) {
370+
/** @var \Amp\Postgres\ResultSet $result */
371+
while (yield $result->advance()) {
372+
$row = $result->getCurrent();
373+
$this->assertSame($data[0], $row['domain']);
374+
$this->assertSame($data[1], $row['tld']);
375+
}
376+
}
364377
});
365378
}
366379

0 commit comments

Comments
 (0)