diff --git a/src/PgSqlCommandResult.php b/src/PgSqlCommandResult.php index 5b23c08..fe4ce60 100644 --- a/src/PgSqlCommandResult.php +++ b/src/PgSqlCommandResult.php @@ -34,6 +34,17 @@ final class PgSqlCommandResult implements CommandResult } /** + * @deprecated This is not meant to be used to get the last insertion ID. Use `INSERT ... RETURNING column_name` + * to get the last auto-increment ID. + * + * $sql = "INSERT INTO person (lastname, firstname) VALUES (?, ?) RETURNING id;" + * $statement = yield $pool->prepare($sql); + * $result = yield $statement->execute(['Doe', 'John']); + * if (!yield $result->advance()) { + * throw new \RuntimeException("Insertion failed"); + * } + * $id = $result->getCurrent()['id']; + * * @return string */ public function getLastOid(): string diff --git a/src/PgSqlHandle.php b/src/PgSqlHandle.php index de135d3..2dc6939 100644 --- a/src/PgSqlHandle.php +++ b/src/PgSqlHandle.php @@ -241,13 +241,14 @@ final class PgSqlHandle implements Handle /** * @param resource $result PostgreSQL result resource. + * @param string $sql Query SQL. * * @return \Amp\Sql\CommandResult|ResultSet * * @throws FailureException * @throws QueryError */ - private function createResult($result) + private function createResult($result, string $sql) { switch (\pg_result_status($result, \PGSQL_STATUS_LONG)) { case \PGSQL_EMPTY_QUERY: @@ -265,7 +266,7 @@ final class PgSqlHandle implements Handle foreach (self::DIAGNOSTIC_CODES as $fieldCode => $desciption) { $diagnostics[$desciption] = \pg_result_error_field($result, $fieldCode); } - throw new QueryExecutionError(\pg_result_error($result), $diagnostics); + throw new QueryExecutionError(\pg_result_error($result), $diagnostics, null, $sql); case \PGSQL_BAD_RESPONSE: throw new FailureException(\pg_result_error($result)); @@ -286,7 +287,8 @@ final class PgSqlHandle implements Handle public function statementExecute(string $name, array $params): Promise { return call(function () use ($name, $params) { - return $this->createResult(yield from $this->send("pg_send_execute", $name, $params)); + \assert(isset($this->statements[$name]), "Named statement not found when executing"); + return $this->createResult(yield from $this->send("pg_send_execute", $name, $params), $this->statements[$name]->sql); }); } @@ -326,7 +328,7 @@ final class PgSqlHandle implements Handle } return call(function () use ($sql) { - return $this->createResult(yield from $this->send("pg_send_query", $sql)); + return $this->createResult(yield from $this->send("pg_send_query", $sql), $sql); }); } @@ -343,7 +345,7 @@ final class PgSqlHandle implements Handle $params = Internal\replaceNamedParams($params, $names); return call(function () use ($sql, $params) { - return $this->createResult(yield from $this->send("pg_send_query_params", $sql, $params)); + return $this->createResult(yield from $this->send("pg_send_query_params", $sql, $params), $sql); }); } @@ -378,12 +380,15 @@ final class PgSqlHandle implements Handle use Struct; public $refCount = 1; public $promise; + public $sql; }; + $storage->sql = $sql; + $this->statements[$name] = $storage; try { - yield ($storage->promise = call(function () use ($name, $modifiedSql) { + yield ($storage->promise = call(function () use ($name, $modifiedSql, $sql) { $result = yield from $this->send("pg_send_prepare", $name, $modifiedSql); switch (\pg_result_status($result, \PGSQL_STATUS_LONG)) { @@ -396,7 +401,7 @@ final class PgSqlHandle implements Handle foreach (self::DIAGNOSTIC_CODES as $fieldCode => $description) { $diagnostics[$description] = \pg_result_error_field($result, $fieldCode); } - throw new QueryExecutionError(\pg_result_error($result), $diagnostics); + throw new QueryExecutionError(\pg_result_error($result), $diagnostics, null, $sql); case \PGSQL_BAD_RESPONSE: throw new FailureException(\pg_result_error($result)); diff --git a/src/PqHandle.php b/src/PqHandle.php index 9f774d6..9de6f98 100644 --- a/src/PqHandle.php +++ b/src/PqHandle.php @@ -163,6 +163,7 @@ final class PqHandle implements Handle } /** + * @param string|null Query SQL or null if not related. * @param callable $method Method to execute. * @param mixed ...$args Arguments to pass to function. * @@ -172,7 +173,7 @@ final class PqHandle implements Handle * * @throws FailureException */ - private function send(callable $method, ...$args): \Generator + private function send(?string $sql, callable $method, ...$args): \Generator { while ($this->busy) { try { @@ -232,7 +233,7 @@ final class PqHandle implements Handle case pq\Result::NONFATAL_ERROR: case pq\Result::FATAL_ERROR: - throw new QueryExecutionError($result->errorMessage, $result->diag); + throw new QueryExecutionError($result->errorMessage, $result->diag, null, $sql ?? ''); case pq\Result::BAD_RESPONSE: throw new FailureException($result->errorMessage); @@ -306,7 +307,7 @@ final class PqHandle implements Handle \assert($storage->statement instanceof pq\Statement, "Statement storage in invalid state"); - return new Coroutine($this->send([$storage->statement, "execAsync"], $params)); + return new Coroutine($this->send($storage->sql, [$storage->statement, "execAsync"], $params)); } /** @@ -334,7 +335,7 @@ final class PqHandle implements Handle \assert($storage->statement instanceof pq\Statement, "Statement storage in invalid state"); - return new Coroutine($this->send([$storage->statement, "deallocateAsync"])); + return new Coroutine($this->send(null, [$storage->statement, "deallocateAsync"])); } /** @@ -346,7 +347,7 @@ final class PqHandle implements Handle throw new \Error("The connection to the database has been closed"); } - return new Coroutine($this->send([$this->handle, "execAsync"], $sql)); + return new Coroutine($this->send($sql, [$this->handle, "execAsync"], $sql)); } /** @@ -361,7 +362,7 @@ final class PqHandle implements Handle $sql = Internal\parseNamedParams($sql, $names); $params = Internal\replaceNamedParams($params, $names); - return new Coroutine($this->send([$this->handle, "execParamsAsync"], $sql, $params)); + return new Coroutine($this->send($sql, [$this->handle, "execParamsAsync"], $sql, $params)); } /** @@ -396,13 +397,16 @@ final class PqHandle implements Handle public $refCount = 1; public $promise; public $statement; + public $sql; }; + $storage->sql = $sql; + $this->statements[$name] = $storage; try { $storage->statement = yield ( - $storage->promise = new Coroutine($this->send([$this->handle, "prepareAsync"], $name, $modifiedSql)) + $storage->promise = new Coroutine($this->send($sql, [$this->handle, "prepareAsync"], $name, $modifiedSql)) ); } catch (\Throwable $exception) { unset($this->statements[$name]); @@ -420,7 +424,7 @@ final class PqHandle implements Handle */ public function notify(string $channel, string $payload = ""): Promise { - return new Coroutine($this->send([$this->handle, "notifyAsync"], $channel, $payload)); + return new Coroutine($this->send(null, [$this->handle, "notifyAsync"], $channel, $payload)); } /** @@ -437,6 +441,7 @@ final class PqHandle implements Handle try { yield from $this->send( + null, [$this->handle, "listenAsync"], $channel, static function (string $channel, string $message, int $pid) use ($emitter) { @@ -474,7 +479,7 @@ final class PqHandle implements Handle if (!$this->handle) { $promise = new Success; // Connection already closed. } else { - $promise = new Coroutine($this->send([$this->handle, "unlistenAsync"], $channel)); + $promise = new Coroutine($this->send(null, [$this->handle, "unlistenAsync"], $channel)); } $promise->onResolve([$emitter, "complete"]); diff --git a/src/QueryExecutionError.php b/src/QueryExecutionError.php index ab7e8ad..9627844 100644 --- a/src/QueryExecutionError.php +++ b/src/QueryExecutionError.php @@ -9,9 +9,9 @@ class QueryExecutionError extends QueryError /** @var mixed[] */ private $diagnostics; - public function __construct(string $message, array $diagnostics, \Throwable $previous = null) + public function __construct(string $message, array $diagnostics, \Throwable $previous = null, string $query = '') { - parent::__construct($message, 0, $previous); + parent::__construct($message, $query, $previous); $this->diagnostics = $diagnostics; }