Oracle にも OFFSET FETCH が入ってたので Doma の対応コード書いてみた。

仕事で Oracle12c を触る機会があって気付いたのですが、Oracle12c から OFFSET FETCH が入ってたようです。
第37回 新しいSQLについて
構文が SQL Server と似た感じですね。
※正確な構文はこちらから SELECT

SQL Server との違いで ORACLE の方が便利そうな箇所は、

  1. ORDER BY が無くてもOK (普通 ORDER BY 書くけどね)
  2. OFFSET は省略可 (先頭からN件とる場合)
  3. FETCH で指定する方法が行数と割合(%)で指定可 (全体の5%とか)
  4. fetch で with ties を指定すると、最後のデータと同じものは指定行数を超えても含まれる。

SQL Server の方が便利そうな箇所は、

  1. FETCH を省略可 (先頭N件をスキップして残り全部とる場合)

使うかどうかは置いといて機能的には負けてますね。。

で、Welcome to Doma — Doma 2.0 ドキュメント も使ってたので、12C用の PagingTransformer 書いてみました。
雰囲気動きそうな感じです。percent や with ties には対応していませんが、ちょっと使う分にはこれで良いかなー。

/*
 * Copyright 2004-2010 the Seasar Foundation and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.seasar.doma.internal.jdbc.dialect;

import static org.seasar.doma.internal.Constants.ROWNUMBER_COLUMN_NAME;

import org.seasar.doma.internal.jdbc.sql.node.FragmentNode;
import org.seasar.doma.internal.jdbc.sql.node.FromClauseNode;
import org.seasar.doma.internal.jdbc.sql.node.SelectClauseNode;
import org.seasar.doma.internal.jdbc.sql.node.SelectStatementNode;
import org.seasar.doma.internal.jdbc.sql.node.WhereClauseNode;
import org.seasar.doma.jdbc.SqlNode;

/**
 * @author taedium
 * 
 */
public class Oracle12PagingTransformer extends OraclePagingTransformer {

    public Oracle12PagingTransformer(long offset, long limit) {
        super(offset, limit);
    }

    @Override
    public SqlNode visitSelectStatementNode(SelectStatementNode node, Void p) {
        if (processed) {
            return node;
        }
        processed = true;

        OrderByClauseNode originalOrderBy = node.getOrderByClauseNode();
        OrderByClauseNode orderBy = node.getOrderByClauseNode();
        if (originalOrderBy != null) {
            orderBy = new OrderByClauseNode(originalOrderBy.getWordNode());
            for (SqlNode child : originalOrderBy.getChildren()) {
                orderBy.appendNode(child);
            }
        } else {
            orderBy = new OrderByClauseNode("");
        }

        if (this.offset > 0) {
            orderBy.appendNode(new FragmentNode(" offset "));
            orderBy.appendNode(new FragmentNode(String.valueOf(this.offset)));
            orderBy.appendNode(new FragmentNode(" rows"));
        }

        if (this.limit > 0) {
            orderBy.appendNode(new FragmentNode(" fetch first "));
            orderBy.appendNode(new FragmentNode(String.valueOf(this.limit)));
            orderBy.appendNode(new FragmentNode(" rows only"));
        }

        SelectStatementNode result = new SelectStatementNode();
        result.setSelectClauseNode(node.getSelectClauseNode());
        result.setFromClauseNode(node.getFromClauseNode());
        result.setWhereClauseNode(node.getWhereClauseNode());
        result.setGroupByClauseNode(node.getGroupByClauseNode());
        result.setHavingClauseNode(node.getHavingClauseNode());
        result.setOrderByClauseNode(orderBy);
        result.setForUpdateClauseNode(node.getForUpdateClauseNode());
        result.setOptionClauseNode(node.getOptionClauseNode());
        return result;
    }
}