Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Changelog

All notable changes to this package are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

First standalone release. SqlMap moves out of the PRADO framework (`framework/Data/SqlMap`) into this package. Namespaces and class names are unchanged. The baseline is the framework copy in PRADO `master` at commit `2c7e90da` (2026-10-21), including every SqlMap fix made there through pradosoft/prado#1330. The entries below are differences from that baseline. See the [migration guide](docs/migration.md) for upgrade steps.

### Added

- `composer.json` declares `extra.prado` for PRADO 4.4: `bootstrap` (`TSqlMapConfig`), `error-messages` (`src/Data/SqlMap/DataMapper/messages.txt`), and `class-map` (`config/classes.json`). The legacy top-level `extra.bootstrap` remains for PRADO 4.3.
- `config/classes.json` maps the 55 SqlMap short class names (`TSqlMapConfig`, `TSqlMapFifoCache`, ...) to their FQNs. It replaces the SqlMap entries of the framework's `classes.php`.
- Message text for five codes that were thrown without a definition: `sqlmap_can_not_extend_select_key`, `sqlmap_configfile_invalid`, `sqlmap_must_enable_custom_paging`, `sqlmap_property_must_be_list`, `sqlmap_use_set_to_store_cache`.
- `TSqlMapException` accepts the `TException` argument forms: an optional integer error code first, and a trailing `Throwable` as the previous exception.

### Changed

- `TSqlMapException::getErrorCode()` returns the message code (for example `sqlmap_contains_no_statement`). It previously returned the translated message text.

### Fixed

- Result mapping falls back to a case-insensitive column match when the exact column name is absent. `pdo_oci` returns upper-case names and `pdo_pgsql` returns lower-case names. This applies to `<result column>` lookups, implicit `resultClass` mapping, `groupBy` keys, and lazy-load (`select`) keys.
- IBM DB2: the `PRADO_ROWNUM` column added by the paging subquery is removed from each row before mapping.
- IBM DB2: a null parameter without a `dbType` binds as `PDO::PARAM_STR`. `pdo_ibm` rejects `PDO::PARAM_NULL`.
- Firebird: a boolean parameter binds as `0` or `1`. `pdo_firebird` binds a PHP boolean only to a `BOOLEAN` column.
- `TCachingStatement` builds cache keys from the SQL text. It previously serialized the prepared command, which threw "Serialization of 'PDOStatement' is not allowed" for any cached statement with bound parameters.
- `TCachingStatement::executeInsert()` delegates to the wrapped statement. It previously called itself until the stack overflowed.
- `TParameterProperty::__sleep()` and `TResultProperty::__sleep()` name private properties by their namespaced class, so properties at their default values are left out of the serialized form. `TResultProperty` also misspelled `_propertyName`.
- `TResultProperty::getPropertyValueType()` no longer calls a method on `null` when no reflection class is found for the type.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Within a PRADO Application:
composer require pradosoft/prado-sqlmap
```

Applications upgrading from the SqlMap bundled with the PRADO framework: see the [migration guide](docs/migration.md). Release notes are in the [changelog](CHANGELOG.md).

## Supported Databases

Any database with a PDO driver. The test suite covers:
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ SqlMap is a data-mapper layer that maps SQL statements defined in XML files to P

| Section | Description |
|---|---|
| [Migration](migration.md) | Moving from the framework-bundled SqlMap to this package |
| [Prado Integration](integration.md) | Registering `TSqlMapConfig` as a PRADO application module, connection configuration, and caching the parsed manager |
| [Quick Start](quickstart.md) | Minimal working example: config file, mapping file, and PHP calls |
| [Configuration](configuration/README.md) | `sqlMapConfig` root file reference — properties, type aliases, type handlers, transaction manager, data source, and `sqlMaps` includes |
Expand Down
68 changes: 68 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[prado-sqlmap](../README.md) -> [SqlMap Docs](README.md) -> Migration

# Migration guide

SqlMap shipped inside the PRADO framework from PRADO 3.1. PRADO 4.4 extracts it into this package. The class names and namespaces are unchanged, so most applications need only add the dependency and clear the application cache.

## Install the package

```sh
composer require pradosoft/prado-sqlmap
```

PRADO reads the package's `extra.prado` section and registers its class map and error messages system-wide. Existing module definitions keep working without changes.

## What is unchanged

- **Namespaces**: every class keeps its `Prado\Data\SqlMap\...` namespace, for example `Prado\Data\SqlMap\TSqlMapConfig` and `Prado\Data\SqlMap\DataMapper\TSqlMapFifoCache`.
- **Short names** still resolve. `config/classes.json` maps them to their FQNs, so `<module class="TSqlMapConfig">` and `Prado::createComponent('TSqlMapFifoCache')` work as before.
- **Module configuration**: `TSqlMapConfig` properties (`ConfigFile`, `ConnectionID`, `EnableCache`) and the inline `<database>` element are unchanged.
- **XML formats**: `sqlMapConfig` and mapping files use the same elements and attributes.
- **API**: `TSqlMapGateway` methods and their arguments are unchanged.
- **Error codes**: existing `sqlmap_*` codes keep their text.

## What changed

### `TSqlMapException::getErrorCode()` returns the code

`getErrorCode()` returns the message code, matching other PRADO exceptions. It previously returned the translated message.

```php
try {
$gateway->queryForObject('missing');
} catch (TSqlMapUndefinedException $e) {
$e->getErrorCode(); // before: "Unable to find SQLMap statement 'missing'."
// after: "sqlmap_contains_no_statement"
$e->getMessage(); // unchanged: "Unable to find SQLMap statement 'missing'."
}
```

Code that compares `getErrorCode()` with message text must compare `getMessage()` instead.

### Column names match regardless of case

When a row has no column with the exact mapped name, a case-insensitive match is used. Oracle (`pdo_oci`) and PostgreSQL (`pdo_pgsql`) results now fill properties that previously stayed empty. Queries that alias columns to work around the folding continue to work.

### Driver-specific parameter binding

| Driver | Value | Before | After |
|---|---|---|---|
| Firebird | PHP `bool` | bound as boolean; rejected by integer columns | bound as `0` or `1` |
| IBM DB2 | `null` without `dbType` | bound as `PDO::PARAM_NULL`; rejected by `pdo_ibm` | bound as `PDO::PARAM_STR` (SQL `NULL`) |

### Cached statements

Statements with a `cacheModel` build their cache key from the SQL text. Cached statements with bound parameters previously failed with "Serialization of 'PDOStatement' is not allowed" and now cache as configured. Keys for statements without parameters differ from before, so earlier entries are not reused.

### Serialized managers

With `EnableCache="true"`, `TSqlMapConfig` stores the parsed `TSqlMapManager` in the application cache. The serialized form of several classes differs from the framework copy. Clear the application cache once after upgrading so the manager is rebuilt.

## Verifying the move

After installing the package:

1. Confirm `class="TSqlMapConfig"` module definitions still resolve.
2. Clear the application cache, or delete the cache files under `protected/runtime`.
3. Replace any comparison of `TSqlMapException::getErrorCode()` with message text.
4. Run the application's own tests against its mapped statements.
2 changes: 1 addition & 1 deletion src/Data/SqlMap/Configuration/TSqlMapStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public function initialize($manager)

/**
* @param TSqlMapTypeHandlerRegistry $registry type handler registry
* @return \ArrayAccess new instance of list class.
* @return array|\ArrayAccess new instance of list class.
*/
public function createInstanceOfListClass($registry)
{
Expand Down
7 changes: 3 additions & 4 deletions src/Data/SqlMap/DataMapper/TSqlMapCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,15 @@ public function add($id, $value, $expire = 0, $dependency = null)
}

/**
* Deletes a value with the specified key from cache and returns the removed object.
* Deletes a value with the specified key from cache
* @param string $key the key of the value to be deleted
* @return mixed the object that was cached under $key, or null if not found
* @return bool if no error happens during deletion
*/
public function delete($key)
{
$object = $this->get($key);
$this->_cache->remove($key);
$this->_keyList->remove($key);
return $object;
return true;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Data/SqlMap/Statements/IMappedStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function getManager();
* @param int $skip The number of rows to skip over.
* @param int $max The maximum number of rows to return.
* @param null|mixed $delegate
* @return \Prado\Collections\TMap A map of object containing the rows keyed by <tt>$keyProperty</tt>.
* @return array A map of object containing the rows keyed by <tt>$keyProperty</tt>.
*/
public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty = null, $skip = -1, $max = -1, $delegate = null);

Expand All @@ -66,7 +66,7 @@ public function executeUpdate($connection, $parameter);
* @param int $skip The number of rows to skip over.
* @param int $max The maximum number of rows to return.
* @param null|mixed $delegate
* @return \Prado\Collections\TList A TList of result objects.
* @return array|\ArrayAccess A TList of result objects.
*/
public function executeQueryForList($connection, $parameter, $result = null, $skip = -1, $max = -1, $delegate = null);

Expand Down
4 changes: 1 addition & 3 deletions src/Data/SqlMap/Statements/TMappedStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,7 @@ public function runQueryForMap($connection, $parameter, $command, $keyProperty,
*/
protected function raiseRowDelegate($handler, $param)
{
if ($handler instanceof \Closure) {
$handler($this, $param);
} elseif (is_string($handler) || is_object($handler) && is_callable($handler)) {
if (is_string($handler) || is_object($handler) && is_callable($handler)) {
call_user_func($handler, $this, $param);
} elseif (is_array($handler) && is_callable($handler, true)) {
// an array: 0 - object, 1 - method name/path
Expand Down
2 changes: 1 addition & 1 deletion src/Data/SqlMap/TSqlMapConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected function cacheSqlMapManager($manager)

/**
* Loads SqlMap manager from cache.
* @return TSqlMapManager SqlMap manager intance if load was successful, null otherwise.
* @return ?TSqlMapManager SqlMap manager intance if load was successful, null otherwise.
*/
protected function loadCachedSqlMapManager()
{
Expand Down
8 changes: 4 additions & 4 deletions src/Data/SqlMap/TSqlMapGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function queryForObject($statementName, $parameter = null, $result = null
* pass in null if want to return a list instead.
* @param int $skip The number of rows to skip over.
* @param int $max The maximum number of rows to return.
* @return TList A List of result objects.
* @return array|\ArrayAccess A List of result objects.
*/
public function queryForList($statementName, $parameter = null, $result = null, $skip = -1, $max = -1)
{
Expand All @@ -110,7 +110,7 @@ public function queryForList($statementName, $parameter = null, $result = null,
* pass in null if want to return a list instead.
* @param int $skip The number of rows to skip over.
* @param int $max The maximum number of rows to return.
* @return TList A List of result objects.
* @return array|\ArrayAccess A List of result objects.
*/
public function queryWithRowDelegate($statementName, $delegate, $parameter = null, $result = null, $skip = -1, $max = -1)
{
Expand Down Expand Up @@ -168,7 +168,7 @@ public function queryForPagedListWithRowDelegate($statementName, $delegate, $par
* @param null|string $valueProperty The property of the result object to be used as the value.
* @param int $skip The number of rows to skip over.
* @param int $max The maximum number of rows to return.
* @return TMap Array object containing the rows keyed by keyProperty.
* @return array Array object containing the rows keyed by keyProperty.
*/
public function queryForMap($statementName, $parameter = null, $keyProperty = null, $valueProperty = null, $skip = -1, $max = -1)
{
Expand All @@ -189,7 +189,7 @@ public function queryForMap($statementName, $parameter = null, $keyProperty = nu
* @param null|string $valueProperty The property of the result object to be used as the value.
* @param int $skip The number of rows to skip over.
* @param int $max The maximum number of rows to return.
* @return TMap Array object containing the rows keyed by keyProperty.
* @return array Array object containing the rows keyed by keyProperty.
*/
public function queryForMapWithRowDelegate($statementName, $delegate, $parameter = null, $keyProperty = null, $valueProperty = null, $skip = -1, $max = -1)
{
Expand Down
12 changes: 4 additions & 8 deletions tests/unit/Data/SqlMap/SqlMapCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ public function testFifoDelete()
$fifo->set('k1', $object1);
$fifo->set('k2', $object2);

// delete returns the removed object
$removed = $fifo->delete('k1');
$this->assertSame($object1, $removed);
$this->assertTrue($fifo->delete('k1'));

// key is gone
$this->assertNull($fifo->get('k1'));
Expand All @@ -96,20 +94,18 @@ public function testLruDelete()
$lru->set('k1', $object1);
$lru->set('k2', $object2);

$removed = $lru->delete('k1');
$this->assertSame($object1, $removed);
$this->assertTrue($lru->delete('k1'));
$this->assertNull($lru->get('k1'));
$this->assertSame($object2, $lru->get('k2'));
}

public function testDeleteExistingReturnsObjectAndRemovesIt()
public function testDeleteExistingReturnsTrueAndRemovesIt()
{
$fifo = new TSqlMapFifoCache();
$obj = new TComponent();
$fifo->set('k', $obj);

$removed = $fifo->delete('k');
$this->assertSame($obj, $removed);
$this->assertTrue($fifo->delete('k'));
$this->assertNull($fifo->get('k'));
}

Expand Down
Loading