@@ -78,19 +78,33 @@ class Adapter implements AdapterInterface, Profiler\ProfilerAwareInterface, Sche
7878 public function getQueryResultSetPrototype(): ResultSet\ResultSetInterface;
7979 public function getCurrentSchema(): string|false;
8080
81+ /** @deprecated Use prepareQuery() and executeQuery() instead. */
8182 public function query(
8283 string $sql,
8384 ParameterContainer|array|string $parametersOrQueryMode = self::QUERY_MODE_PREPARE,
8485 ?ResultSet\ResultSetInterface $resultPrototype = null
8586 ): Driver\StatementInterface|ResultSet\ResultSet|Driver\ResultInterface;
8687
88+ public function prepareQuery(
89+ string $sql,
90+ ParameterContainer|array $parameters = []
91+ ): Driver\StatementInterface;
92+
93+ public function executeQuery(
94+ string|Driver\StatementInterface $sql
95+ ): Driver\ResultInterface;
96+
8797 public function createStatement(
8898 ?string $initialSql = null,
8999 ParameterContainer|array|null $initialParameters = null
90100 ): Driver\StatementInterface;
91101}
92102```
93103
104+ > ** Note:** ` prepareQuery() ` and ` executeQuery() ` are currently declared on
105+ > the ` Adapter ` class only; ` AdapterInterface ` still declares ` query() ` alone
106+ > to avoid breaking existing implementors during the 0.x series.
107+
94108### Constructor Parameters
95109
96110- ** ` $driver ` ** : A ` DriverInterface ` implementation from a driver package
@@ -104,51 +118,85 @@ class Adapter implements AdapterInterface, Profiler\ProfilerAwareInterface, Sche
104118
105119## Query Preparation
106120
107- By default, ` PhpDb\Adapter\Adapter::query() ` prefers that you use
108- "preparation" as a means for processing SQL statements. This generally means
109- that you will supply a SQL statement containing placeholders for the values, and
110- separately provide substitutions for those placeholders:
121+ ` PhpDb\Adapter\Adapter::prepareQuery() ` prepares a SQL statement, optionally
122+ binding parameters, and always returns the prepared ` Statement ` without
123+ executing it:
111124
112- ``` php title="Query with Prepared Statement"
113- $adapter->query ('SELECT * FROM `artist` WHERE `id` = ?', [5]);
125+ ``` php title="Preparing a Statement"
126+ $statement = $ adapter->prepareQuery ('SELECT * FROM `artist` WHERE `id` = ?', [5]);
114127```
115128
116129The above example will go through the following steps:
117130
1181311 . Create a new ` Statement ` object
1191322 . Prepare the array ` [5] ` into a ` ParameterContainer ` if necessary
1201333 . Inject the ` ParameterContainer ` into the ` Statement ` object
121- 4 . Execute the ` Statement ` object, producing a ` Result ` object
122- 5 . Check the ` Result ` object to check if the supplied SQL was a result set
123- producing statement. If the query produced a result set, clone the
124- ` ResultSet ` prototype, inject the ` Result ` as its datasource, and return
125- the new ` ResultSet ` instance. Otherwise, return the ` Result ` .
134+ 4 . Prepare the ` Statement ` object and return it
135+
136+ To actually run the statement, pass it to ` executeQuery() ` :
137+
138+ ``` php title="Executing a Prepared Statement"
139+ $result = $adapter->executeQuery($statement);
140+ ```
141+
142+ ` executeQuery() ` always returns the raw ` Driver\ResultInterface ` — it never
143+ wraps it. If you want the result wrapped in a ` ResultSet ` , check
144+ ` isQueryResult() ` and call ` getQueryResult() ` yourself:
145+
146+ ``` php title="Wrapping a Query Result"
147+ if ($result->isQueryResult()) {
148+ $resultSet = $result->getQueryResult();
149+ }
150+ ```
151+
152+ ` getQueryResult() ` clones the ` ResultSet ` prototype you pass it (or a
153+ default prototype if you pass none), injects the ` Result ` as its data
154+ source, and returns the new ` ResultSet ` instance. See
155+ [ ` getQueryResult() ` ] ( #using-the-driver-object ) below for details.
126156
127157## Query Execution
128158
129- In some cases, you have to execute statements directly without preparation. One
159+ In some cases, you have to execute SQL directly without preparation. One
130160possible reason for doing so would be to execute a DDL statement, as most
131161extensions and RDBMS systems are incapable of preparing such statements.
132162
133- To execute a query without the preparation step, pass a flag as
134- the second argument indicating execution is required :
163+ Pass the raw SQL string to ` executeQuery() ` to execute it without a
164+ preparation step :
135165
136166``` php title="Executing DDL Statement Without Preparation"
167+ $adapter->executeQuery(
168+ 'ALTER TABLE ADD INDEX(`foo_index`) ON (`foo_column`)'
169+ );
170+ ```
171+
172+ ## The Deprecated query() Method
173+
174+ ` Adapter::query() ` predates the ` prepareQuery() ` /` executeQuery() ` split and
175+ combines both concerns behind a single method and a stringly-typed second
176+ argument. It is deprecated in favour of the methods above but remains
177+ available, proxying to them internally, for backwards compatibility:
178+
179+ ``` php title="Query with Prepared Statement (deprecated)"
180+ $adapter->query('SELECT * FROM `artist` WHERE `id` = ?', [5]);
181+ ```
182+
183+ ``` php title="Executing DDL Statement Without Preparation (deprecated)"
137184$adapter->query(
138185 'ALTER TABLE ADD INDEX(`foo_index`) ON (`foo_column`)',
139186 Adapter::QUERY_MODE_EXECUTE
140187);
141188```
142189
143- The primary difference to notice is that you must provide the
144- ` Adapter::QUERY_MODE_EXECUTE ` (execute) flag as the second parameter.
190+ The primary difference to notice in the second example is that you must
191+ provide the ` Adapter::QUERY_MODE_EXECUTE ` (execute) flag as the second
192+ parameter.
145193
146194## Creating Statements
147195
148- While ` query ()` is highly useful for one-off and quick querying of a database
149- via the ` Adapter ` , it generally makes more sense to create a statement and
150- interact with it directly, so that you have greater control over the
151- prepare-then-execute workflow:
196+ While ` prepareQuery ()` and ` executeQuery() ` are highly useful for one-off and
197+ quick querying of a database via the ` Adapter ` , it generally makes more sense
198+ to create a statement and interact with it directly, so that you have
199+ greater control over the prepare-then-execute workflow:
152200
153201``` php title="Creating and Executing a Statement"
154202$statement = $adapter->createStatement($sql, $optionalParameters);
@@ -229,13 +277,22 @@ interface ResultInterface extends Countable, Iterator
229277{
230278 public function buffer(): void;
231279 public function isQueryResult(): bool;
280+ public function getQueryResult(?ResultSetInterface $resultPrototype = null): ResultSetInterface;
232281 public function getAffectedRows(): int;
233282 public function getGeneratedValue(): mixed;
234283 public function getResource(): mixed;
235284 public function getFieldCount(): int;
236285}
237286```
238287
288+ ` getQueryResult() ` clones the given ` $resultPrototype ` (or a default
289+ ` ResultSet ` prototype if none is given), initializes the clone from the
290+ result, and returns it. It throws ` Exception\RuntimeException ` if
291+ ` isQueryResult() ` is false. ` Adapter::query() ` (the deprecated BC method)
292+ delegates to this method rather than duplicating the clone-and-initialize
293+ logic itself; call it yourself when you want a ` ResultSet ` from
294+ ` executeQuery() ` 's raw ` Driver\ResultInterface ` .
295+
239296## Using The Platform Object
240297
241298The ` Platform ` object provides an API to assist in crafting queries in a way
@@ -442,7 +499,7 @@ $sql = 'UPDATE ' . $qi('artist')
442499 . ' SET ' . $qi('name') . ' = ' . $fp('name')
443500 . ' WHERE ' . $qi('id') . ' = ' . $fp('id');
444501
445- $statement = $adapter->query ($sql);
502+ $statement = $adapter->prepareQuery ($sql);
446503
447504$parameters = [
448505 'name' => 'Updated Artist',
@@ -452,7 +509,7 @@ $parameters = [
452509$statement->execute($parameters);
453510
454511// DATA UPDATED, NOW CHECK
455- $statement = $adapter->query (
512+ $statement = $adapter->prepareQuery (
456513 'SELECT * FROM '
457514 . $qi('artist')
458515 . ' WHERE id = ' . $fp('id')
0 commit comments