ZendFramework1 future の Zend_Db_Adapter_Db2#limitメソッド

[ZendFramework1 future](https://github.com/Shardj/zf1-future) は **PHP8.1** コンパチで動作する、ありがたいライブラリ。（※8.1 も 2024/11/25 で [セキュリティサポート](https://qiita.com/bezeklik/items/72d1ff8393f66673e2bc) が来てしまいますが...）
過去の資産を使い、未だに多くの **IBMi** ユーザーが PHP 開発で、**ZF1** を使用しており、PHPのバージョンアップでは、非常に助かっている。
しかし、旧ZF1時代から、`Zend_Db_Adapter_Db2#limitメソッド` に問題があり（※DB2 LUW 試していないのでわかりません）我々が `IBMi` で使用する際には、一工夫が必要。

## 参考リンク
- [repo - Shardj/zf1-future](https://github.com/Shardj/zf1-future)
- [issue - Zend_Db_Adapter_Db2 limit does not work #391](https://github.com/Shardj/zf1-future/issues/391)
- [PR - Improving limit method using OVER() #392](https://github.com/Shardj/zf1-future/pull/392)
- [PR - Improving limit method using LIMIT,OFFSET #393](https://github.com/Shardj/zf1-future/pull/393)


## 問題の箇所
以下の箇所の `OVER()` だが、SQLに `ORDER BY` がない場合は正常に動作するが、`ORDER BY` が存在する場合は `OVER(ORDER BY ...)` にしなければならない部分が [抜け落ちている](https://github.com/Shardj/zf1-future/blob/master/library/Zend/Db/Adapter/Db2.php#L694)。

- [OLAP の指定](https://www.ibm.com/docs/ja/i/7.5?topic=expressions-olap-specifications)
**「OLAP-specification OVER( --- window-order-clause --- ) 」**

```php:/Zend/Db/Adapter/Db2.php
        /**
         * DB2 does not implement the LIMIT clause as some RDBMS do.
         * We have to simulate it with subqueries and ROWNUM.
         * Unfortunately because we use the column wildcard "*",
         * this puts an extra column into the query result set.
         */
        return "SELECT z2.*
            FROM (
                SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.*
                FROM (
                    " . $sql . "
                ) z1
            ) z2
            WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
```

上記問題を zf1-future の issue に上げた。 [issue - Zend_Db_Adapter_Db2 limit does not work #391](https://github.com/Shardj/zf1-future/issues/391)

## 対応
対応としては、`OVER(ORDER BY ...)` と、過去には存在しなかったが、今ではDB2でも実装されている `LIMIT` の何れかを使用する実装が考えられる。
`OVER(ORDER BY ...)` は、過去の DB2 バージョンのユーザーも使えるが、冗長で煩雑に見え、`LIMIT` の方は、他のDB製品とも近く、記述としてはシンプルになる。

- [OVER() 実装](https://github.com/Shardj/zf1-future/pull/392/commits/f53b6638346ed32c70f5b06d2e804cea80631760)
- [LIMIT 実装](https://github.com/Shardj/zf1-future/pull/393/commits/87c1ae22f928c76c23fee072f428a464f3eed26c)

```php:/Zend/Db/Adapter/Db2.php 実装 OVER()
       // Add this piece and place $order in OVER() clause
        $pieces = preg_split("/order by/i", $sql);
        $order = "";
        if(array_key_exists(1, $pieces)) {
            $order = "ORDER BY " . $pieces[1];
        }

        $limit_sql = "SELECT z2.*
              FROM (
                 SELECT ROW_NUMBER() OVER($order) AS \"ZEND_DB_ROWNUM\", z1.*
                    FROM (
                       " . $sql . "
                    ) z1
               ) z2
               WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count);

        return $limit_sql;
    }

```

```php:/Zend/Db/Adapter/Db2.php 実装 LIMIT
        $sql .= " LIMIT $count";
        if ($offset > 0) {
            $sql .= " OFFSET $offset";
        }

        return $sql;
    }

```

~~本体にいつ取り込まれるかは？未定なので、もし `limitメソッド` で困っている人は参考にまでどうぞ。~~

## **2024.01.26** 追記
PR案 [ Shardj/zf1-future - Improving limit method using OVER() #392 ](https://github.com/Shardj/zf1-future/pull/392) に関しては、一旦マージして貰えました。これで少なくても IBM i で limit,OFFSET の改善にはなりました。Thanks!

こっち[ Shardj/zf1-future -  Improving limit method using LIMIT,OFFSET #393 ](https://github.com/Shardj/zf1-future/pull/393) は現時点で **discussion** 中です。