diff --git a/CHANGELOG.md b/CHANGELOG.md index f6043e6..6e62a78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `pdo/close-cursor` - wrap `PDOStatement::closeCursor`; frees the cursor so the statement can be re-executed ([#9]). - `pdo/fetch-object` - wrap `PDOStatement::fetchObject`; returns the next row as an object (`stdClass` or a named class), or `nil` when exhausted ([#11]). +### Changed + +- `pdo/get-attribute` / `pdo/set-attribute` now dispatch on the handle: pass a connection (as before) or a statement to reach `PDOStatement::getAttribute` / `setAttribute` ([#12]). + ## [0.1.0] - 2026-05-13 ### Changed diff --git a/README.md b/README.md index 0d9c9e4..da52e28 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ All functions live in the `phel.pdo` namespace. | `last-insert-id` | `(last-insert-id conn)` | ID of the last inserted row. | | `begin` / `commit` / `rollback` | `(begin conn)` … | Transaction control. | | `in-transaction` | `(in-transaction conn)` | `true` if a transaction is active. | -| `get-attribute` / `set-attribute` | `(get-attribute conn attr)` / `(set-attribute conn attr value)` | PDO attribute access. | +| `get-attribute` / `set-attribute` | `(get-attribute handle attr)` / `(set-attribute handle attr value)` | PDO attribute access; `handle` is a connection or a statement. | | `get-available-drivers` | `(get-available-drivers conn)` | Vector of installed PDO drivers. | | `error-code` | `(error-code conn)` | SQLSTATE of the last operation. | | `error-info` | `(error-info conn)` | `[sqlstate driver-code driver-message]`. | diff --git a/src/pdo.phel b/src/pdo.phel index 9f6c8f9..06a63a7 100644 --- a/src/pdo.phel +++ b/src/pdo.phel @@ -2,15 +2,22 @@ (defstruct connection [pdo]) +(declare statement?) + +(defn- attr-target + "The PDO handle behind a connection or statement struct." + [handle] + (if (statement? handle) (handle :stmt) (handle :pdo))) + (defn ^bool set-attribute - "Set an attribute" - [conn attribute value] - (php/-> (conn :pdo) (setAttribute attribute value))) + "Set an attribute on a connection or statement" + [handle attribute value] + (php/-> (attr-target handle) (setAttribute attribute value))) (defn get-attribute - "Retrieve a database connection attribute" - [conn attribute] - (php/-> (conn :pdo) (getAttribute attribute))) + "Retrieve an attribute from a connection or statement" + [handle attribute] + (php/-> (attr-target handle) (getAttribute attribute))) (defn connect "Connect database and return connection object. diff --git a/src/pdo/statement.phel b/src/pdo/statement.phel index 29603a7..7bf2121 100644 --- a/src/pdo/statement.phel +++ b/src/pdo/statement.phel @@ -79,9 +79,7 @@ ;; PDOStatement::bindColumn — Bind a column to a PHP variable ;; PDOStatement::errorCode — Fetch the SQLSTATE associated with the last operation on the statement handle ;; PDOStatement::errorInfo — Fetch extended error information associated with the last operation on the statement handle -;; PDOStatement::getAttribute — Retrieve a statement attribute ;; PDOStatement::getColumnMeta — Returns metadata for a column in a result set ;; PDOStatement::getIterator — Gets result set iterator ;; PDOStatement::nextRowset — Advances to the next rowset in a multi-rowset statement handle -;; PDOStatement::setAttribute — Set a statement attribute ;; PDOStatement::setFetchMode — Set the default fetch mode for this statement diff --git a/tests/pdo_test.phel b/tests/pdo_test.phel index 8a02528..97ba516 100644 --- a/tests/pdo_test.phel +++ b/tests/pdo_test.phel @@ -183,6 +183,28 @@ stmt (pdo/execute stmt {:id 1})] (is (= "phel" (pdo/fetch-column stmt))))) +(deftest set-attribute-dispatches-to-statement + (seed-t1 *conn*) + ;; SQLite rejects statement attributes; depending on the driver build this is + ;; either a false return or a PDOException - both prove the call reached the statement. + (let [stmt (pdo/prepare *conn* "select * from t1") + outcome (try + (pdo/set-attribute stmt \PDO/ATTR_CURSOR \PDO/CURSOR_FWDONLY) + (catch \PDOException _e :unsupported))] + (is (or (= false outcome) (= :unsupported outcome))))) + +(deftest get-attribute-dispatches-to-statement + (seed-t1 *conn*) + (testing "connection branch still returns a value" + (is (= \PDO/ERRMODE_EXCEPTION (pdo/get-attribute *conn* \PDO/ATTR_ERRMODE)))) + (testing "statement branch routes to the PDOStatement (SQLite reports unsupported)" + (let [stmt (pdo/prepare *conn* "select * from t1") + threw (try + (pdo/get-attribute stmt \PDO/ATTR_CURSOR) + false + (catch \PDOException _e true))] + (is (= true threw))))) + (deftest debug-dump-params-emits-sql-header (seed-t1 *conn*) (let [stmt (pdo/prepare *conn* "select * from t1 where name = :name")