/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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.opengion.hayabusa.taglib;

import static org.opengion.fukurou.util.StringUtil.nval;

import java.util.Locale;
import java.util.Set;											// 6.4.3.4 (2016/03/11)
import java.util.ArrayList;										// 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
import java.util.List;											// 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )

import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.db.Transaction;
import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ArraySet;						// 6.4.3.4 (2016/03/11)
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;		// 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
import org.opengion.hayabusa.db.DBColumn;
import org.opengion.hayabusa.db.DBEditConfig;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.db.Query;
import org.opengion.hayabusa.db.QueryFactory;
import org.opengion.hayabusa.resource.GUIInfo;

import static org.opengion.fukurou.system.HybsConst.BR;			// 6.1.0.0 (2014/12/26) refactoring

/**
 * データベースの検索を行うタグです。
 *
 * このタグの内容に、SQL文を記述します。 whereタグ、 andタグ を使うと引数に応じて
 * 実行されるSQL文が異なります(使用例参照)。
 * また、PL/SQLのSPで検索を行うときもこのタグを使います。
 * SystemData の USE_SQL_INJECTION_CHECK が true か、quotCheck 属性が true の場合は、
 * ＳＱＬインジェクション対策用のシングルクォートチェックを行います。リクエスト引数に
 * シングルクォート(')が含まれると、エラーになります。
 * 同様にUSE_XSS_CHECKがtrueか、xssCheck属性がtrueの場合は、
 * クロスサイトススクリプティング(XSS)対策のためless/greater than signのチェックを行います。
 *
 * 実行後にリクエストパラメータに以下の値がセットされます。
 *   DB.COUNT     : 検索結果の件数
 *   DB.ERR_CODE  : 検索結果のｴﾗｰｺｰﾄﾞ
 *   DB.IS_UPDATE : 更新(=true)/検索(false)
 *
 * ※ このタグは、Transaction タグの対象です。
 *
 * @og.formSample
 * ●形式：
 *       ・&lt;og:query command="NEW" &gt;
 *              SELECT文
 *         &lt;/og:query&gt;
 *       ・&lt;og:query command="NEW" names="･･･" queryType="JDBCErrMsg" &gt;
 *              { call PL/SQL(?,?,?,? ) }
 *         &lt;/og:query&gt;
 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
 *
 * ●Tag定義：
 *   &lt;og:query
 *       queryType          【TAG】Query を発行する為のクラスID(JDBC,JDBCCallable,JDBCErrMsg,JDBCUpdate,JDBCPrepared)を指定します{@og.doc03Link queryType 初期値:JDBC})
 *       command            【TAG】コマンド (NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY)
 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/application]を指定します(初期値:session)
 *       maxRowCount        【TAG】(通常は使いません)データの最大読み込み件数を指定します (初期値:DB_MAX_ROW_COUNT[=1000])(0:[無制限])
 *       skipRowCount       【TAG】(通常は使いません)データの読み始めの初期値を指定します
 *       displayMsg         【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します (初期値:VIEW_DISPLAY_MSG[=])
 *       overflowMsg        【TAG】検索データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します (初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました])
 *       notfoundMsg        【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])
 *       names              【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します
 *       stopZero           【TAG】検索結果が０件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])
 *       tableId            【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
 *       dbid               【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します
 *       trace              【TAG】引数の SQL 文を EXPLAIN PLAN を[true:行う/それ以外:行わない]を指定します(初期値:false)
 *       checkNames         【TAG】リクエスト変数の正規化を行うカラムをCSV形式で複数指定します
 *       modifyType         【TAG】DB検索時の モディファイタイプを指定します[A:追加/C:更新/D:削除]
 *       stopError          【TAG】PLSQL/SQL処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)
 *       dispError			【TAG】エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true)
 *       quotCheck          【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します(初期値:USE_SQL_INJECTION_CHECK)
 *       xssCheck           【TAG】リクエスト情報の HTMLTag開始/終了文字(&gt;&lt;) 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_XSS_CHECK[=true])
 *       mainTrans          【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:true)
 *       useBeforeHtmlTag   【TAG】処理時間(queryTime)などの情報出力[true:有効/false:無効]を指定します(初期値:true)
 *       useTimeView        【TAG】処理時間を表示する TimeView を表示するかどうかを指定します
 *										(初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:query&gt;
 *
 * ●使用例
 *     &lt;og:query command="NEW"&gt;
 *             select PN,YOBI,NMEN,HINM from XX01 where PN = '{&#064;PN}' order by PN
 *     &lt;/og:query&gt;
 *
 *          ・検索条件が入力された時({&#064;PN}がNOT NULLのとき)
 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where PN = 'AAA' order by PN
 *          ・検索条件が入力されなかった時({&#064;PN}がNULLのとき)
 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where PN = '' order by PN
 *
 *     &lt;og:query command="NEW"&gt;
 *             select PN,YOBI,NMEN,HINM from XX01
 *         &lt;og:where&gt;
 *             &lt;og:and value="PN = '{&#064;PN}%'" /&gt;
 *             &lt;og:and value="YOBI like '{&#064;YOBI}%'" /&gt;
 *         &lt;/og:where&gt;
 *             order by PN
 *     &lt;/og:query&gt;
 *
 *          ・検索条件が入力された時({&#064;PN}がNOT NULLのとき)
 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 PN = 'AAA%' and YOBI like 'BBB%' order by PN
 *          ・検索条件が入力されなかった時({&#064;PN}がNULLのとき) WHERE句がなくなる。
 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 order by PN
 *
 *        注意:WhereTagを使った場合、下のようにはなりません。
 *            select PN,YOBI,NMEN,HINM from XX01 PN = '' and YOBI like '%' order by PN
 *
 *     &lt;og:query command="NEW"&gt;
 *             select PN,YOBI,NMEN,HINM from XX01 where PN="11111"
 *         &lt;og:where startKey="and"&gt;
 *             &lt;og:and value="YOBI like '{&#064;PN}%'" /&gt;
 *         &lt;/og:where&gt;
 *             order by PN
 *     &lt;/og:query&gt;
 *
 *          ・検索条件が入力された時({&#064;YOBI}がNOT NULLのとき)
 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 PN = '11111' and YOBI like 'BBB%' order by PN
 *          ・検索条件が入力されなかった時({&#064;YOBI}がNULLのとき) WHERE句がなくなる。
 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 PN = '11111' order by PN
 *
 *     &lt;og:query
 *          command    = "NEW"
 *          names      = "SYSTEM_ID,LANG,CLM,NAME_JA,LABEL_NAME,KBSAKU,USER.ID"
 *          checkNames = "CLM,NAME_JA"
 *          queryType  = "JDBCErrMsg"
 *          displayMsg = ""&gt;
 *              {call TYPE3B01.TYPE3B01(?,?,?,?)}
 *     &lt;/og:query&gt;
 *
 *          ・queryType に JDBCErrMsg を指定して、PL/SQL をコールできます。
 *            引数は、names 属性をキーにリクエスト変数から読み込みます。
 *          ・checkNames にカラム名を指定すると、columns.valueSet による
 *            リクエスト変数の正規化を行います。
 *
 * @og.group ＤＢ検索
 * @og.group ＤＢ登録
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class QueryTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "8.0.2.0 (2021/11/30)" ;
	private static final long serialVersionUID = 802020211130L ;

	/** command 引数に渡す事の出来る コマンド  新規 {@value} */
	public static final String CMD_NEW	 = "NEW" ;
	/** command 引数に渡す事の出来る コマンド  再検索 {@value} */
	public static final String CMD_RENEW = "RENEW" ;
	// 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
	private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_NEW , CMD_RENEW );
	// 6.4.1.1 (2016/01/16) QueryTag.errMsgId  → QueryTag.ERR_MSG_ID  refactoring
	/** エラーメッセージID {@value} */
	protected static final String ERR_MSG_ID	 = HybsSystem.ERR_MSG_KEY;

	// 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
	/** ﾃｰﾌﾞﾙﾓﾃﾞﾙ */		protected transient DBTableModel	table		;
	/** ｴﾗｰﾒｯｾｰｼﾞ */		protected transient ErrorMessage	errMessage	;
	/** ﾃｰﾌﾞﾙID */			protected String	tableId		= HybsSystem.TBL_MDL_KEY;
	/** ｺﾏﾝﾄﾞ */			protected String	command		= CMD_NEW;
	/** ｽｷｯﾌﾟ行数 */		protected int		skipRowCount;
	/** 最大行数 */			protected int		maxRowCount	= -1;
	/** 検索文 */			protected String	sql			;
	/** 実行件数 */			protected int		executeCount = -1;			// 検索/実行件数
	/** 名前列 */			protected String	names		;
	/** ｱｳﾄﾒｯｾｰｼﾞ */		protected boolean	outMessage	= true;
	/** ｴﾗｰｺｰﾄﾞ */			protected int		errCode	 	= ErrorMessage.OK;
	/** ﾃﾞｨｽﾌﾟﾚｲﾒｯｾｰｼﾞ */	protected String	displayMsg	= HybsSystem.sys( "VIEW_DISPLAY_MSG" );

	private   boolean	updateFlag	;				// 6.3.6.1 (2015/08/28) 検索か更新か

	private   String	dbid		;
	private   String	queryType	;
	private   boolean	trace 		;				// 4.0.0 (2005/01/31) 廃止
	private   boolean	stopZero	;
	private   String	modifyType	;				// 3.8.5.1 (2006/05/08) modifyType 属性を追加します。
	private   String	overflowMsg	= "MSG0007";	// 検索結果が、制限行数を超えましたので、残りはカットされました。
	private   String	notfoundMsg	= "MSG0077";	// 対象データはありませんでした。
	private   boolean	isMainTrans	= true;			// 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し

	/** 3.5.4.7 (2004/02/06) 実行時間測定用のDIV要素を出力します。 */
	protected long		dyStart		;
	/** ﾀｲﾑﾊﾞｰを使用するかどうか */
	protected boolean	useTimeView	= HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );		// 6.3.6.0 (2015/08/16)
	/** 4.3.3.0 (2008/09/22) PLSQL/SQL実行エラーの際に、処理を中止するかどうか。 */
	protected boolean	stopError	= true;
	/** 5.9.26.1 (2017/11/10) 実行エラーの際に、エラーを画面に出力するかどうか。 */
	protected boolean dispError		= true;

	private StringBuilder debugMsg	;				// 3.5.6.0 (2004/06/18)
	private String		checkNames	;				// 3.8.0.5 (2005/08/20) リクエスト変数の正規化を行います。
	private String		traceMsg	;				// 3.8.5.3 (2006/08/07) トレース時のメッセージ文字列を保存しておきます。
	/** ｸｵｰﾄﾁｪｯｸ */
	protected boolean	quotCheck	= HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );	// 4.0.0 (2005/08/31)
	/** XSSﾁｪｯｸ */
	protected boolean	xssCheck	= HybsSystem.sysBool( "USE_XSS_CHECK" );			// 5.0.0.2 (2009/09/15)

	private boolean		useBeforeHtmlTag = true ;	// 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定
	/** SLABELを利用するかどうか */
	protected boolean	useSLabel	;				// 7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)

	// 5.10.2.1 (2018/08/18) where-andタグのplaceHolder情報を保持する
	private transient List<String> listPlaceValue	;

	private String	updQuery		;			// 7.2.9.1 (2020/10/23)
	private String	insQuery		;			// 7.2.9.1 (2020/10/23)
//	private int		sqlCnt			;			// 7.2.9.1 (2020/10/23) 0の場合はBODYから、1の場合はそのまま、それ以上の場合は、マージ処理。
	private String	selQuery		;			// 7.4.1.0 (2021/04/23) sqlType="MERGE" 時に、insertOnly="true" が指定された時

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public QueryTag() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @og.rev 3.5.4.7 (2004/02/06) 実行時間測定用に、開始時刻を取得します。
	 * @og.rev 3.5.6.5 (2004/08/09) 暫定的に、DBTableModelを先行削除します。
	 * @og.rev 3.6.0.0 (2004/09/24) DBTableModel の先行削除は、scope="session" の場合のみ。
	 * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
	 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
	 * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
	 * @og.rev 6.4.8.1 (2016/07/02) xssCheckを、doStartTag に移動
	 * @og.rev 7.2.5.0 (2020/06/01) NEXT,PREV等でstopZeroが効かない対応
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doStartTag() {
		// 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
		if( useTag() ) {
			useXssCheck( xssCheck );			// 6.4.8.1 (2016/07/02)

			dyStart = System.currentTimeMillis();
			// 7.2.5.0 (2020/06/01) NEXT,PREV等でstopZeroが効かない対応
//			if( ! check( command, COMMAND_SET ) ) { return SKIP_BODY ; }
			if( ! check( command, COMMAND_SET ) ) {
				table = (DBTableModel)getObject( tableId );
				if( table == null )	{ executeCount = 0; }
				else				{ executeCount = table.getRowCount(); }

				return SKIP_BODY ;
			}

			useMainTrans( isMainTrans );			// 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
			startQueryTransaction( tableId );		// 3.6.0.8 (2004/11/19)

			// 3.5.6.5 (2004/08/09) 削除するのは、セッションのオブジェクトでよい。
			// 3.6.0.0 (2004/09/24) 削除するのは、scope="session" の場合のみ。
			if( "session".equals( getScope() ) ) {
				removeSessionAttribute( tableId );
				removeSessionAttribute( HybsSystem.VIEWFORM_KEY );
			}

			return EVAL_BODY_BUFFERED ;	// Body を評価する。( extends BodyTagSupport 時)
		}
		return SKIP_BODY ;				// Body を評価しない
	}

	/**
	 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) ボディの内容を取得する処理を、CommonTagSupport で行う。
	 * @og.rev 3.6.0.8 (2004/11/19) エラー発生時に確実にリリースされるように try finally 追加
	 * @og.rev 3.8.5.3 (2006/08/07) USER.LASTSQL へのSQL文の保存は、実行前に行っておきます。
	 * @og.rev 3.8.6.3 (2006/11/30) SQL 文の前後のスペースを取り除きます。
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更
	 * @og.rev 4.0.0.0 (2005/08/31) useQuotCheck() によるＳＱＬインジェクション対策
	 * @og.rev 4.3.4.0 (2008/12/01) GE20(ユーザー定数)へ登録するかのフラグへの対応
	 * @og.rev 5.0.0.2 (2009/09/15) XSS対応
	 * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
	 * @og.rev 5.1.9.0 (2010/08/01) TransactionTag 対応。上位に TransactionTag があれば、そこからConnection をもらう。
	 * @og.rev 5.3.6.0 (2011/06/01) 集計、合計などのEdit機能に対応します。
	 * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
	 * @og.rev 5.3.7.0 (2011/07/01) PL/SQLかつscope="request"で正しく出力するためqueryType,namesも保存する。
	 * @og.rev 5.3.8.0 (2011/08/01) Transaction発生箇所でclose()
	 * @og.rev 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更。
	 * @og.rev 5.6.5.3 (2013/06/28) LASTQUERYTYPE ⇒ LASTSQLTYPE 変更。
	 * @og.rev 6.3.1.1 (2015/07/10) BodyString,BodyRawStringは、CommonTagSupport で、trim() します。
	 * @og.rev 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
	 * @og.rev 6.3.6.1 (2015/08/28) QueryFactory.close( Query ) 廃止。Queryはキャッシュしません。
	 * @og.rev 6.4.0.2 (2015/12/11) Transaction の commit は、errCode で判定します。
	 * @og.rev 6.4.8.1 (2016/07/02) xssCheckを、doStartTag に移動
	 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 * @og.rev 8.0.2.0 (2021/11/30) 検索実行前に、SQL文字をdebugPrint出来るように修正
	 *
	 * @return	後続処理の指示(SKIP_BODY)
	 */
	@Override
	public int doAfterBody() {

		// 4.0.0 (2005/08/31) useQuotCheck() によるＳＱＬインジェクション対策
		useQuotCheck( quotCheck );
		// 5.0.0.2 (2009/09/15) XSS対策

		// 7.2.9.3 (2020/11/06) JDBCTableUpdate と JDBCTableMerge の相互運用
		// 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
		final boolean useMerge = insQuery != null && ( updQuery != null || selQuery != null );
//		if( "JDBCTableUpdate".equals( queryType ) && updQuery != null && insQuery != null ) {
		if( "JDBCTableUpdate".equals( queryType ) && useMerge ) {
			queryType = "JDBCTableMerge";
		}
//		else if( "JDBCTableMerge".equals( queryType ) && ( updQuery == null || insQuery == null ) ) {
		else if( "JDBCTableMerge".equals( queryType ) && !useMerge ) {
			queryType = "JDBCTableUpdate";
		}

		if( sql == null ) {							// 7.2.9.1 (2020/10/23) 0の場合はBODYから、1の場合はそのまま、それ以上の場合は、マージ処理。
			sql = getBodyString();					// 6.3.1.1 (2015/07/10)
		}
		debugPrint( sql );							// 8.0.2.0 (2021/11/30)

		// 3.2.1.0 (2003/05/28) 最終ＳＱＬ文を、UserInfo に、キャッシュしておく。
		// 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
		if( isMainTrans ) {
			setUserInfo( "LASTSQL", sql, false );
			// 5.3.7.0 (2011/07/01) PL/SQLかつscope="request"で正しく出力するためqueryType,namesも保存する。
			setUserInfo( "LASTSQLTYPE", queryType, false );				// 5.6.5.3 (2013/06/28)
			setUserInfo( "LASTNAMES", names, false );
		}

		// 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
		try( Transaction tran = getTransaction() ) {
			if( maxRowCount < 0 ) {
				maxRowCount	= sysInt( "DB_MAX_ROW_COUNT" );				// 4:個人設定可
			}

			if( trace ) {
				traceMsg = traceQuery( sql,tran );		// 5.1.9.0 (2010/08/01) TransactionTag 対応
				// 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更
				return SKIP_BODY ;
			}

			final Query query = QueryFactory.newInstance( queryType );
			query.setConnection( tran.getConnection( dbid ) );			// 6.3.6.1 (2015/08/28)

			query.setSkipRowCount( skipRowCount );
			query.setMaxRowCount( maxRowCount );
			query.setResourceManager( getResource() );					// 4.0.0 (2005/01/31)
			query.setStatement( sql );
//			query.setMergeStatement( updQuery,insQuery );				// 7.2.9.1 (2020/10/23) 今は、queryType="JDBCTableMerge" の時のみ使用している。
			query.setMergeStatement( updQuery,insQuery,selQuery );		// 7.4.1.0 (2021/04/23) 今は、queryType="JDBCTableMerge" の時のみ使用している。

			// 5.3.6.0 (2011/06/01) 集計、合計などのEdit機能に対応します。
			if( isMainTrans ) {
				final String guikey = getGUIInfoAttri( "KEY" );
				final String editName = getRequestValue( "editName" );
				final DBEditConfig config = getUser().getEditConfig( guikey, editName );
				if( config != null ) {
					query.setEditConfig( config );
				}
			}

			execute( query );

			executeCount = query.getExecuteCount();
			if( errCode < ErrorMessage.NG && executeCount >= 0 ) {		// 異常以外の場合
				table = query.getDBTableModel();
				// 3.8.5.1 (2006/05/08) modifyType 属性を追加します。
				if( modifyType != null ) {
					for( int row=0; row<executeCount; row++ ) {
						table.setModifyType( row,modifyType );
					}
				}
			}

			// 6.4.0.2 (2015/12/11) errCode のみで判定します。(PL/SQL の場合は、executeCountがセットされない)
			if( errCode >= ErrorMessage.NG ) {
				tran.rollback();
			}

			tran.commit();
		}
		return SKIP_BODY ;
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 2.0.0.8 (2002/10/09) command="NEW" のときのみ、displayMsg を表示させます。
	 * @og.rev 2.1.1.4 (2002/11/25) デバッグ時に最終SQLをユーザー情報をセットするように変更。
	 * @og.rev 2.1.2.1 (2002/11/27) ErrorMessage をクリアしないように変更。
	 * @og.rev 3.1.1.0 (2003/03/28) JspWriter オブジェクトの使用箇所を、jspPrint() を使用するように変更。
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.2.1.0 (2003/05/28) 最終ＳＱＬ文を、UserInfo に、キャッシュしておく。
	 * @og.rev 3.3.3.3 (2003/08/06) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
	 * @og.rev 3.3.3.3 (2003/08/06) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。
	 * @og.rev 3.5.4.7 (2004/02/06) 実行時間測定用のDIV要素を出力しておきます。
	 * @og.rev 3.5.4.9 (2004/02/25) 警告時に停止していましたが、継続処理させます。
	 * @og.rev 3.5.5.0 (2004/03/12) ErrorMessage オブジェクトを、query が成功した時にもクリアするように変更
	 * @og.rev 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
	 * @og.rev 3.5.5.8 (2004/05/20) ErrorMessage オブジェクトを、コマンドが NEW の場合のみ、クリア
	 * @og.rev 3.5.6.0 (2004/06/18) debugMsg 属性を出力するように修正します。
	 * @og.rev 3.6.0.8 (2004/11/19) DBTableModel をセーブする時に、トランザクションチェックを行います。
	 * @og.rev 3.8.5.3 (2006/08/07) USER.LASTSQL へのSQL文の保存は、実行前に行っておきます。
	 * @og.rev 4.0.0.0 (2006/11/14) notfoundMsg 属性を追加。displayMsg は、VIEW_USE_DISPLAY_MSG で制御
	 * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel )
	 * @og.rev 4.3.3.0 (2008/09/22) 属性 stopError の設定により、JSP処理を中止するかどうかを制御します。
	 * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
	 * @og.rev 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定します。
	 * @og.rev 5.5.0.3 (2012/03/13) オーバーフローメッセージが存在しないときは、何もしない。(改行も入れない)
	 * @og.rev 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更。
	 * @og.rev 5.6.3.0 (2013/04/01) エラー時メッセージ変更
	 * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
	 * @og.rev 5.9.16.1 (2017/11/10) dispErrorの動作追加
	 * @og.rev 6.9.9.0 (2018/08/20) ｢ERR0041:検索処理中に割り込みの検索要求がありました｣エラーを、標準のErrorMessageに追加するようにします。
	 * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)
		// 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
		if( !useTag() ) { return EVAL_PAGE ; }

		if( trace ) {
			jspPrint( traceMsg );
			return SKIP_PAGE;		// 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更。
		}

		String label  = "";				// 4.0.0 (2005/11/30) 検索しなかった場合。
		if( check( command, COMMAND_SET ) ) {
			final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

			// 実行件数の表示 command="NEW" のときのみ、displayMsg を表示させます。
			// 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。
			if( CMD_NEW.equals( command ) ) {
				if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
					buf.append( executeCount )
						.append( getResource().getLabel( displayMsg ) )
						.append( BR );
				}
				else if( executeCount == 0 && notfoundMsg != null && notfoundMsg.length() > 0 ) {
					buf.append( getResource().getLabel( notfoundMsg ) )
						.append( BR );
				}
			}

			// 3.3.3.3 (2003/08/06) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
			setRequestAttribute( "DB.COUNT"   , String.valueOf( executeCount ) );
			// 3.3.3.3 (2003/08/06) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。
			setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
			// 6.3.6.1 (2015/08/28) 検索か更新か判定するフラグ(updateFlag)を用意します。
			setRequestAttribute( "DB.IS_UPDATE", String.valueOf( updateFlag ) );

			// オーバーフロー時のメッセージを表示
			// 5.5.0.3 (2012/03/09) オーバーフローメッセージが存在しないときは、何もしない。(改行も入れない)
			if( table != null && table.isOverflow() && overflowMsg != null && overflowMsg.length() > 0  ) {
				buf.append( getResource().getLabel( overflowMsg ) )
					.append( BR );
			}

			// 6.9.9.0 (2018/08/20) ｢ERR0041:検索処理中に割り込みの検索要求がありました｣エラーを、標準のErrorMessageに追加するようにします。
			if( table != null && ! commitTableObject( tableId, table ) ) {
				if( errMessage == null ) { errMessage = new ErrorMessage( "QueryTag Query Error!" ); }
				// ERR0041:検索処理中に割り込みの検索要求がありました。処理されません。
				errMessage.addMessage( 0,ErrorMessage.NG,"ERR0041" );
				errCode = ErrorMessage.NG;
			}

			// 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
//			final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
			final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource(),useSLabel );		// 7.0.7.0 (2019/12/13)
			if( err != null && err.length() > 0 ) {
				buf.append( err );
				setSessionAttribute( ERR_MSG_ID,errMessage );
			}
			else if( CMD_NEW.equals( command ) ) {		// 3.5.5.8 (2004/05/20)
				removeSessionAttribute( ERR_MSG_ID );
			}
			label = buf.toString();
			// 5.9.26.1 (2017/11/10) エラーメッセージをリクエスト変数で持つようにしておく
			setRequestAttribute( "DB.ERR_MSG", label );

//			// 6.9.9.0 (2018/08/20) ｢ERR0041:検索処理中に割り込みの検索要求がありました｣エラーを、標準のErrorMessageに追加するようにします。
//			if( table != null && ! commitTableObject( tableId, table ) ) {
//				// 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
//				// jspPrint( "QueryTag Query処理が割り込まれました。DBTableModel は登録しません。" );
//
//				// 5.6.4.0 (2013/04/01) リソースから出力するように対応
//				final ErrorMessage errMsgObj = new ErrorMessage( "QueryTag Query Error!" );
//				errMsgObj.addMessage( 0,ErrorMessage.NG,"ERR0041" );
//				jspPrint( TaglibUtil.makeHTMLErrorTable( errMsgObj,getResource() ) );
//
//				return SKIP_PAGE ;
//			}
		}

		// 5.9.26.1 (2017/11/10) dispErrorで表示をコントロール
		if( dispError ) {
			jspPrint( label );
		}

		// 3.5.4.9 (2004/02/25) 警告時に停止していましたが、継続処理させます。
		final int rtnCode ;
		if( errCode >= ErrorMessage.NG )  {	// 異常
			rtnCode = stopError ? SKIP_PAGE : EVAL_PAGE ;
		}
		else {
			// 件数０件かつ stopZero = true
			rtnCode = executeCount == 0 && stopZero ? SKIP_PAGE : EVAL_PAGE ;
		}

		// 3.5.4.7 (2004/02/06)
		final long dyTime = System.currentTimeMillis()-dyStart;

		// 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録)
		final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY );
		if( guiInfo != null ) { guiInfo.addReadCount( executeCount,dyTime,sql ); }

		// 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定します。
		if( useTimeView && useBeforeHtmlTag ) {		// 6.3.6.0 (2015/08/16)
			jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );	// 3.5.6.3 (2004/07/12)
		}

		return rtnCode ;
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.5.4.7 (2004/02/06) 実行時間測定用に、dyStart を追加します。
	 * @og.rev 3.5.6.0 (2004/06/18) debugMsg 属性を追加します。
	 * @og.rev 3.8.0.5 (2005/08/20) checkNames 属性を追加します。
	 * @og.rev 3.8.5.1 (2006/05/08) modifyType 属性を追加します。
	 * @og.rev 3.8.5.1 (2006/05/08) traceMsg 属性(トレース時のメッセージ文字列)を追加します。
	 * @og.rev 4.0.0.0 (2005/08/31) quotCheck 属性の追加
	 * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
	 * @og.rev 4.3.3.0 (2008/09/22) stopError 属性の追加
	 * @og.rev 5.0.0.2 (2009/09/15) XSS対応
	 * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
	 * @og.rev 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定します。
	 * @og.rev 6.3.6.1 (2015/08/28) 検索か更新か判定するフラグ(updateFlag)を用意します。
	 * @og.rev 5.9.26.1 (2017/11/10) dispError追加
	 * @og.rev 5.10.2.1 (2018/08/18) プレースホルダ対応
	 * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
	 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 */
	@Override
	protected void release2() {
		super.release2();
		tableId			= HybsSystem.TBL_MDL_KEY;
		queryType		= null;
		dbid			= null;
		command			= CMD_NEW;
		skipRowCount	= 0;
		maxRowCount		= -1;
		table 			= null;
		sql				= null;
		displayMsg		= HybsSystem.sys( "VIEW_DISPLAY_MSG" );
		overflowMsg		= "MSG0007";	// 検索結果が、制限行数を超えましたので、残りはカットされました。
		notfoundMsg		= "MSG0077";	// 対象データはありませんでした。
		executeCount	= -1;			// 検索/実行件数
		names 			= null;
		outMessage		= true;
		trace			= false;
		errCode			= ErrorMessage.OK;
		updateFlag		= false;		// 6.3.6.1 (2015/08/28) 検索か更新か
		errMessage		= null;
		stopZero		= false;
		stopError		= true;			// 4.3.3.0 (2008/09/22)
		debugMsg		= null;			// 3.5.6.0 (2004/06/18)
		checkNames		= null;			// 3.8.0.5 (2005/08/20)
		quotCheck		= HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );	// 4.0.0 (2005/08/31)
		modifyType      = null;			// 3.8.5.1 (2006/05/08)
		traceMsg		= null;			// 3.8.5.3 (2006/08/07)
		xssCheck		= HybsSystem.sysBool( "USE_XSS_CHECK" );	// 5.0.0.2 (2009/09/15)
		isMainTrans		= true;			// 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
		useBeforeHtmlTag	= true ;	// 5.3.5.0 (2011/05/01)
		useTimeView		= HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );	// 6.3.6.0 (2015/08/16)
		dispError		= true; 		// 5.9.26.1 (2017/11/10)
		listPlaceValue	= null;			// 5.10.2.1 (2018/08/18)
		useSLabel		= false;		// 7.0.7.0 (2019/12/13) ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
		updQuery		= null;			// 7.2.9.1 (2020/10/23)
		insQuery		= null;			// 7.4.1.0 (2021/04/23)
		selQuery		= null;			// 7.2.9.1 (2020/10/23)
//		sqlCnt			= 0;			// 7.2.9.1 (2020/10/23)
	}

	/**
	 * Query を実行します。
	 *
	 * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更
	 * @og.rev 3.4.0.0 (2003/09/01) 登録エラー時のキーと値を表示するように変更。
	 * @og.rev 3.5.6.0 (2004/06/18) デバッグ情報出力用に、printDebug メソッドを追加。
	 * @og.rev 3.6.1.0 (2005/01/05) エラーコードによる commit/rollback の判断追加
	 * @og.rev 5.3.7.0 (2011/07/01) nameの判定にゼロ文字列を付加
	 * @og.rev 6.2.1.0 (2015/03/13) エラーメッセージに SQL文を追加します。
	 * @og.rev 6.3.6.1 (2015/08/28) 検索か更新か判定するフラグ(updateFlag)を用意します。
	 * @og.rev 6.4.2.0 (2016/01/29) HybsSystemException を catch して、再び、throw する処理を廃止します。
	 * @og.rev 6.9.9.0 (2018/08/20) プレースホルダ対応( 5.10.2.1 (2018/08/18) )
	 *
	 * @param	query オブジェクト
	 */
	protected void execute( final Query query ) {
		String[] nameArray = null;
		String[] values    = null;

		// 6.4.2.0 (2016/01/29) HybsSystemException を catch して、再び、throw する処理を廃止します。
	//	try {
//			if( names == null || names.isEmpty() ) {
			if( names == null || names.isEmpty() ) {
				// 6.9.9.0 (2018/08/20) プレースホルダ対応( 5.10.2.1 (2018/08/18) )
				if( listPlaceValue != null && !listPlaceValue.isEmpty() ) {
					values = listPlaceValue.toArray( new String[ listPlaceValue.size() ] );
					query.execute( values );
				}
				else {
					query.execute();
				}
			}
			else {
				nameArray = StringUtil.csv2Array( names );
				values = getRequest( nameArray );
				// 3.5.6.0 (2004/06/18) デバッグ情報出力用
				if( isDebug() ) { printDebug( nameArray,values ); }
				query.execute( values );
			}
			errCode		= query.getErrorCode();
			errMessage	= query.getErrorMessage();
			updateFlag	= query.isUpdate();				// 6.3.6.1 (2015/08/28) 検索か更新か
			// 3.6.1.0 (2005/01/05) エラーコードによる commit/rollback の判断追加
	//	}
	//	catch( final HybsSystemException ex ) {

	//		// 4.0.0 (2005/02/28) エラー時の表示とデバッグ時の表示を統一する。
	//		String errMsg = "DATABASE ERROR! " + CR
	//						+ "SQL=["	+ sql + "]" + CR;		// 6.2.1.0 (2015/03/13)
	//		if( nameArray != null ) {
	//			printDebug( nameArray,values );
	//			errMsg += debugMsg;
	//		}
	//		throw new HybsSystemException( errMsg,ex );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
	//	}
	}

	/**
	 * デバッグ用に、配列データを書き出します。
	 *
	 * @og.rev 3.5.6.0 (2004/06/18) 新規追加
	 *
	 * @param	nms 	names配列
	 * @param	vals	namesに対応するリクエスト情報の配列
	 */
	private void printDebug( final String[] nms,final String[] vals ) {
		if( debugMsg == null ) { debugMsg = new StringBuilder( BUFFER_MIDDLE ); }

		debugMsg.append( "  names=[" )
			.append( StringUtil.array2csv( nms ) )
			.append( ']' )								// 6.0.2.5 (2014/10/31) char を append する。
			.append( CR )
			.append( "  values=[" )
			.append( StringUtil.array2csv( vals ) )
			.append( ']' )								// 6.0.2.5 (2014/10/31) char を append する。
			.append( CR );
	}

	/**
	 * BODY部の TableUpdateParamTag に書かれたquery文を受け取ります。
	 *
	 * typeには、UPDATEかINSERTが指定され、それに応じたqueryを各変数にセットします。
	 *
	 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のマージ(UPDATE,INSERT)対応
	 * @og.rev 7.2.9.3 (2020/11/06) QueryTag に移動
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 *
	 * @param	type	Queryタイプ(UPDATE,INSERT,その他)
	 * @param	query	SQL文
	 */
	protected void setQuery( final String type , final String query ) {
		sql = query;													// とりあえず、セットしておく
//		sqlCnt++ ;														// 0の場合はBODYから、1の場合はそのまま、それ以上の場合は、マージ処理。QueryTag のprotected属性

		if( "UPDATE".equalsIgnoreCase( type ) ) {
			updQuery = query;											// 上位の QueryTag のprotected属性
		}
		else if( "INSERT".equalsIgnoreCase( type ) ) {
			insQuery = query;											// 上位の QueryTag のprotected属性
		}
		else if( "SELECT".equalsIgnoreCase( type ) ) {
			selQuery = query;											// 上位の QueryTag のprotected属性
		}
	}

	/**
	 * 名称配列を元に、リクエスト情報のデータを取得します。
	 * checkNames 属性に設定されているカラムがあれば、値を正規化します。
	 *
	 * @og.rev 3.8.0.5 (2005/08/20) リクエスト変数の正規化(checkNames)対応
	 *
	 * @param	nameArray	キーとなる名称の配列
	 *
	 * @return	そのリクエスト情報の配列(可変長引数)
	 * @og.rtnNotNull
	 */
	protected String[] getRequest( final String... nameArray ) {
		String[] rtn = new String[nameArray.length];

		for( int i=0; i<rtn.length; i++ ) {
			rtn[i] = getRequestValue( nameArray[i] );

			// 3.8.0.5 (2005/08/20) checkNames があり、rtn[i] がある場合。
			// 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined
//			if( checkNames != null && checkNames.length() > 0 && rtn[i].length() > 0 ) {
//				if( ( "," + checkNames + "," ).indexOf( "," + nameArray[i] + "," ) >= 0 ) {
			if( checkNames != null && checkNames.length() > 0 && rtn[i].length() > 0
				&& ( "," + checkNames + "," ).indexOf( "," + nameArray[i] + "," ) >= 0 ) {
					final DBColumn dbColumn = getDBColumn( nameArray[i] );
					final String val = dbColumn.valueSet( rtn[i] );
					if( val != null ) { rtn[i] = val; }
//				}
			}
		}

		return rtn;
	}

	/**
	 * 子タグ(andタグ)でプレースホルダーを設定した時に利用するメソッドです。
	 * queryタグ内部のプレースホルダリストに追加します。
	 *
	 * @og.rev 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
	 *
	 * @param val 追加分
	 */
	protected void addPlaceValue( final String val ) {
		if( !"JDBCPrepared".equals( queryType ) ) {			// placeHolderは、JDBCPrepared専用です。
			final String errMsg = "andタグの、placeHolder は、queryType=\"JDBCPrepared\" 専用です。val=[" + val + "]" + CR ;
			throw new HybsSystemException( errMsg );
		}

		if(listPlaceValue == null) {
			listPlaceValue = new ArrayList<String>();
		}

		// カンマで分割
		final String[] vals = StringUtil.csv2Array( val );
		for( final String tmp: vals ) {
			// リクエストパラメータの値を変換して、リストに追加
			listPlaceValue.add( getRequestParameter( tmp ) );
		}
	}

	/**
	 * 【TAG】(通常は使いません)データの読み始めの初期値を指定します。
	 *
	 * @og.tag
	 * データベース自体の検索は,指定されたSQLの全件を検索しますが,
	 * DBTableModelのデータとしては、スキップ件数分は登録されません。
	 * サーバーのメモリ資源と応答時間の確保の為です。
	 *
	 * @param	count 読み始めの初期値
	 */
	public void setSkipRowCount( final String count ) {
		skipRowCount = nval( getRequestParameter( count ),skipRowCount );
	}

	/**
	 * 【TAG】(通常は使いません)データの最大読み込み件数を指定します
	 *		(初期値:DB_MAX_ROW_COUNT[={@og.value SystemData#DB_MAX_ROW_COUNT}])。
	 *
	 * @og.tag
	 * データベース自体の検索は,指定されたSQLの全件を検索しますが,
	 * DBTableModelのデータとして登録する最大件数をこの値に設定します。
	 * サーバーのメモリ資源と応答時間の確保の為です。
	 * 0 をセットすると、無制限(Integer.MAX_VALUE)になります。
	 * (初期値:ユーザー定数のDB_MAX_ROW_COUNT[={@og.value SystemData#DB_MAX_ROW_COUNT}])。
	 *
	 * @og.rev 5.5.8.5 (2012/11/27) 0を無制限として処理します。
	 *
	 * @param	count 最大件数
	 * @see		org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT
	 */
	public void setMaxRowCount( final String count ) {
		maxRowCount = nval( getRequestParameter( count ),maxRowCount );
		if( maxRowCount == 0 ) { maxRowCount = Integer.MAX_VALUE ; }		// 5.5.8.5 (2012/11/27)
	}

	/**
	 * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
	 *		(初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
	 *
	 * @og.tag
	 * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
	 * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
	 * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
	 * この tableId 属性を利用して、メモリ空間を分けます。
	 *		(初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
	 *
	 * @param	id テーブルID (sessionに登録する時のID)
	 */
	public void setTableId( final String id ) {
		tableId   = nval( getRequestParameter( id ),tableId );	// 3.8.0.9 (2005/10/17)
	}

	/**
	 * 【TAG】Query を発行する為のクラスID(JDBC,JDBCCallable,JDBCErrMsg,JDBCUpdate,JDBCPrepared)を指定します({@og.doc03Link queryType 初期値:JDBC})。
	 *
	 * @og.tag
	 * 検索を実行する手段は、Query インターフェースの実装クラスになります。
	 * このタグでは、Query.execute( String[] ) メソッドが呼ばれます。
	 * 例えば、ストアドプロシージャ等を実行する場合に、queryType="JDBCErrMsg"
	 * を指定することができます。
	 * 初期値は、"JDBC" です。
	 * queryType は、システムリソースの Query_**** 宣言の **** を与えます。
	 * これらは、Query インターフェースを継承したサブクラスである必要があります。
	 * 標準で、org.opengion.hayabusa.db 以下の Query_**** クラスが、Query_**** 宣言 と
	 * して、定義されています。
	 * 属性クラス定義の {@link org.opengion.hayabusa.db.Query Query} を参照願います。
	 * {@og.doc03Link queryType Query_**** クラス}
	 *
	 * @param	id Query実クラス
	 * @see		org.opengion.hayabusa.db.Query  Queryのサブクラス
	 * @see		org.opengion.hayabusa.db.Query#execute( String[] )
	 */
	public void setQueryType( final String id ) {
		queryType = getRequestParameter( id );
	}

	/**
	 * 【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します。
	 *
	 * @og.tag
	 * Queryオブジェクトを作成する時のDB接続IDを指定します。
	 * これは、システムリソースで、DEFAULT_DB_URL 等で指定している データベース接続先
	 * 情報に、XX_DB_URL を定義することで、 dbid="XX" とすると、この 接続先を使用して
	 * データベースにアクセスできます。
	 *
	 * @param	id データベース接続ID
	 */
	public void setDbid( final String id ) {
		dbid = nval( getRequestParameter( id ),dbid );
	}

	/**
	 * 【TAG】コマンド (NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY)。
	 *
	 * @og.tag
	 * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
	 * フィールド定数値のいづれかを、指定できます。
	 *
	 * @param	cmd コマンド (public static final 宣言されている文字列)
	 * @see		<a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.QueryTag.CMD_NEW">コマンド定数</a>
	 */
	public void setCommand( final String cmd ) {
		final String cmd2 = getRequestParameter( cmd );
		if( cmd2 != null && cmd2.length() >= 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
	}

	/**
	 * 【TAG】検索結果が０件のとき処理を停止するかどうか[true/false]を指定します(初期値:false[続行する])。
	 *
	 * @og.tag
	 * 初期値は、false(続行する)です。
	 *
	 * @param	flag	０件時停止可否 [true:処理を中止する/false:続行する]
	 */
	public void setStopZero( final String flag ) {
		stopZero = nval( getRequestParameter( flag ),stopZero );
	}

	/**
	 * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します
	 *		(初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
	 *
	 * @og.tag
	 * ここでは、検索結果の件数や登録された件数をまず出力し、
	 * その次に、ここで指定したメッセージをリソースから取得して表示します。
	 * 件数を表示させる場合は、displayMsg = "MSG0033"[　件検索しました] をセットしてください。
	 * 表示させたくない場合は, displayMsg = "" をセットしてください。
	 * (初期値:システム定数のVIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
	 *
	 * @param	id 表示メッセージID
	 * @see		org.opengion.hayabusa.common.SystemData#VIEW_DISPLAY_MSG
	 */
	public void setDisplayMsg( final String id ) {
		final String ids = getRequestParameter( id );
		if( ids != null ) { displayMsg = ids; }
	}

	/**
	 * 【TAG】検索データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します
	 *		(初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました])。
	 *
	 * @og.tag
	 * 検索結果が、maxRowCount で設定された値より多い場合、何らかのデータは検索されず
	 * 切り捨てられたことになります。
	 * ここでは、displayMsg を表示した後、必要に応じて、このメッセージを表示します。
	 * 表示させたくない場合は, overflowMsg = "" をセットしてください。
	 * 初期値は、MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました]です。
	 *
	 * @param	id オーバー時メッセージID
	 */
	public void setOverflowMsg( final String id ) {
		final String ids = getRequestParameter( id );
		if( ids != null ) { overflowMsg = ids; }
	}

	/**
	 * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。
	 *
	 * @og.tag
	 * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。
	 * 従来は、displayMsg と兼用で、『0　件検索しました』という表示でしたが、
	 * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。
	 * 表示させたくない場合は, notfoundMsg = "" をセットしてください。
	 * 初期値は、MSG0077[対象データはありませんでした]です。
	 *
	 * @param	id ゼロ件メッセージID
	 */
	public void setNotfoundMsg( final String id ) {
		final String ids = getRequestParameter( id );
		if( ids != null ) { notfoundMsg = ids; }
	}

	/**
	 * 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します。
	 *
	 * @og.tag
	 * 複数ある場合は、CSV形式で渡します。
	 * names 属性は、queryType に応じて設定可否が異なりますので、ご注意ください。
	 * names なし：JDBC,JDBCUpdate,JDBCPrepared
	 * names あり：JDBCCallable,JDBCErrMsg,JDBCUpdate
	 * (JDBCUpdateは、names 属性のあり/なし両方に対応しています。)
	 *
	 * @og.rev 3.0.1.3 (2003/03/11) names 属性に null で渡す場合のバグを修正
	 *
	 * @param	nm 引数の名称 (CSV形式)
	 */
	public void setNames( final String nm ) {
		names = nval( getRequestParameter( nm ),names );
	}

	/**
	 * 【TAG】検索結果のメッセージを表示する/しない[true/false]を指定します(初期値:true)。
	 *
	 * @og.tag
	 * 初期値は、表示する：true です。
	 *
	 * @param	flag 検索結果メッセージ [true:表示する/それ以外:含めない]
	 */
	public void setOutMessage( final String flag ) {
		outMessage = nval( getRequestParameter( flag ),outMessage );
	}

	/**
	 * 【TAG】引数の SQL 文を EXPLAIN PLAN を[true:行う/それ以外:行わない]を指定します(初期値:false)。
	 *
	 * @og.tag
	 *
	 * ここでは、以下の処理を行います。
	 * １．引数の SQL 文を画面に表示します。
	 * ２．引数の SQL 文を EXPLAIN PLAN した結果を、画面に表示します。
	 * なお、以前は、セッションのトレースを行っていましたが、その機能は、廃止いたします。
	 * 初期値は、行わない：false です。
	 *
	 * @param	flag トレース [true:行う/それ以外:行わない]
	 */
	public void setTrace( final String flag ) {
		trace = nval( getRequestParameter( flag ),trace );
	}

	/**
	 * 【TAG】リクエスト変数の正規化を行うカラムをCSV形式で複数指定します。
	 *
	 * @og.tag
	 * PL/SQLを利用する場合の引数にセットすべき データを、リクエスト変数の
	 * 値そのままではなく、カラムオブジェクトの valueSet メソッド経由で正規化
	 * した値を使用するようにします。
	 *
	 * これは、ｶﾗﾑﾘｿｰｽ定義の文字種別で定義された変換ﾙｰﾙを適用します。
	 * DBTableModel を使っていないため、ｶﾗﾑ定義は、columnEditor等で変更できません。
	 *
	 * @og.rev 3.8.0.5 (2005/08/20) 新規追加
	 *
	 * @param	nm 正規化処理カラム (CSV形式)
	 */
	public void setCheckNames( final String nm ) {
		checkNames = nval( getRequestParameter( nm ),checkNames );
	}

	/**
	 * 【TAG】DB検索時の モディファイタイプを指定します[A:追加/C:更新/D:削除]。
	 *
	 * @og.tag
	 * DB検索時に、そのデータをA(追加)、C(更新)、D(削除)のモディファイタイプを
	 * つけた状態にします。
	 * その状態で、そのまま、update する事が可能になります。
	 *
	 * @og.rev 3.8.5.1 (2006/05/08) 新規追加
	 *
	 * @param   type Dモディファイタイプ [A:追加/C:更新/D:削除]
	 */
	public void setModifyType( final String type ) {
		modifyType = nval( getRequestParameter( type ),modifyType );
	}

	/**
	 * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します
	 *		(初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。
	 *
	 * @og.tag
	 * ＳＱＬインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに
	 * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。
	 * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、
	 * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、
	 * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。
	 * (') が含まれていたエラーにする(true)／かノーチェックか(false)を指定します。
	 * 初期値は、SystemData#USE_SQL_INJECTION_CHECK です。
	 *
	 * @og.rev 4.0.0.0 (2005/08/31) 新規追加
	 *
	 * @param   flag クォートチェック [true:する/それ以外:しない]
	 */
	public void setQuotCheck( final String flag ) {
		quotCheck = nval( getRequestParameter( flag ),quotCheck );
	}

	/**
	 * 【TAG】リクエスト情報の HTMLTag開始/終了文字(&gt;&lt;) 存在チェックを実施するかどうか[true/false]を設定します
	 *		(初期値:USE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。
	 *
	 * @og.tag
	 * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
	 * (&gt;&lt;) が含まれていたエラーにする(true)／かノーチェックか(false)を指定します。
	 * (初期値:システム定数のUSE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])
	 *
	 * @og.rev 5.0.0.2 (2009/09/15) 新規追加
	 *
	 * @param	flag	XSSチェック [true:する/false:しない]
	 * @see		org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
	 */
	public void setXssCheck( final String flag ) {
		xssCheck = nval( getRequestParameter( flag ),xssCheck );
	}

	/**
	 * 【TAG】PLSQL/SQL処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)。
	 *
	 * @og.tag
	 * false(中止しない)に設定する場合、後続処理では、{&#064;DB.ERR_CODE}の値により、
	 * PLSQL/SQLの異常/正常終了によって分岐処理は可能となります。
	 * 初期値は、true(中止する)です。
	 *
	 * @og.rev 4.3.3.0 (2008/09/22) 新規追加
	 *
	 * @param   flag エラー時処理中止 [true:中止する/false:中止しない]
	 */
	public void setStopError( final String flag ) {
		stopError = nval( getRequestParameter( flag ),stopError );
	}

	/**
	 * 【TAG】PLSQL/SQL処理エラーの時にエラーを画面表示するか[true/false]を設定します(初期値:true)。
	 *
	 * @og.tag
	 * false(表示しない)に設定する場合、後続処理では、{&#064;DB.ERR_MSG}の値により、
	 * 本来表示されるはずだったメッセージを取得可能です。
	 * stopErrorと併用して、JSON形式でエラーを返す場合等に利用します。
	 * 初期値は、true(表示する)です。
	 * ※false指定の場合は件数や、overFlowメッセージ等も表示されなくなります。
	 *
	 * @og.rev 5.9.26.1 (2017/11/10) 新規追加
	 *
	 * @param   flag  [true:表示する/false:表示しない]
	 */
	public void setDispError( final String flag ) {
		dispError = nval( getRequestParameter( flag ),dispError );
	}

	/**
	 * 引数の SQL 文を EXPLAIN PLAN します。
	 *
	 * ここでは、以下の処理を行います。
	 * １．引数の SQL 文を画面に表示します。
	 * ２．引数の SQL 文を EXPLAIN PLAN した結果を、画面に表示します。
	 * なお、この処理は、ORACLE 専用処理です。
	 *
	 * @og.rev 3.8.5.3 (2006/08/07) 新規追加
	 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
	 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
	 * @og.rev 5.5.3.4 (2012/06/19) getUserInfo は、キー部分だけで処理します。
	 *
	 * @param   sql		対象となるSQL文
	 * @param   tran	Transactionオブジェクト
	 *
	 * @return トレース結果の文字列
	 * @og.rtnNotNull
	 */
	private String traceQuery( final String sql , final Transaction tran ) {
		final String userId = getUserInfo( "ID" ) ;			// 5.5.3.4 (2012/06/19) getUserInfo は、キー部分だけで処理します。

		final String[] arg1 = new String[] { userId };
		DBUtil.dbExecute( "DELETE FROM PLAN_TABLE WHERE STATEMENT_ID = ?",arg1,tran,dbid );		// 5.1.9.0 (2010/08/01)

		final String explan1 = "EXPLAIN PLAN SET STATEMENT_ID = '" + userId + "' FOR " + sql ;
		DBUtil.dbExecute( explan1,null,tran,dbid );		// 5.1.9.0 (2010/08/01)

		final String[] arg2 = new String[] { userId,userId,userId };
		final String explan2 = "select LEVEL as LVL"
							+ ",lpad(' ',LEVEL,' ') || rtrim( OPERATION ) || ' ' || rtrim( OPTIONS ) || ' ' || rtrim( OBJECT_NAME ) as EXECUTION_PLAN"
							+ ",OBJECT_NAME			as OBJ_NAME"
							+ ",DECODE(INSTR(OBJECT_TYPE,' '),0,OBJECT_TYPE,SUBSTR(OBJECT_TYPE,1,INSTR(OBJECT_TYPE,' ')-1)) as OBJ_TYPE"
							+ ",OPTIMIZER			as OPT"
							+ ",COST				as CST"
							+ ",CARDINALITY			as CARD"
							+ ",BYTES				as BYTE"
							+ ",ACCESS_PREDICATES	as ACCS"
							+ ",FILTER_PREDICATES	as FILTER"
						+ " from PLAN_TABLE"
						+ " where STATEMENT_ID = ?"
						+ " start with ID = 0"
						+ " 	  and STATEMENT_ID = ?"
						+ " connect by prior ID = PARENT_ID"
						+ " 	  and STATEMENT_ID = ?" ;

		final String[][] plan = DBUtil.dbExecute( explan2,arg2,tran,dbid,true );			// 5.5.3.4 (2012/06/19) ヘッダー情報も同時に取得する。

		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
			.append( "<pre>" ).append( sql ).append( "</pre>" ).append( BR )
			.append( "<table>" )
			.append( "<tr class=\"row_h\">" );			// 1行目のヘッダーの出力
		final int colsize = plan[0].length;
		for( int j=0; j<colsize; j++ ) {
			buf.append( "<th>" ).append( plan[0][j] ).append( "</th>" );
		}
		buf.append( "</tr>" );

		for( int i=1; i<plan.length; i++ ) {
			buf.append( "<tr class=\"row_" ).append( i%2 ).append( "\">" );
			for( int j=0; j<colsize; j++ ) {
				if( j==1 ) {
					buf.append( "<td><pre>" ).append( plan[i][1] ).append( "</pre></td>" );
				}
				else {
					buf.append( "<td>" ).append( plan[i][j] ).append( "</td>" );
				}
			}
			buf.append( "</tr>" );
		}
		buf.append( "</table>" ).append( BR );
		return buf.toString();
	}

	/**
	 * 【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:true)。
	 *
	 * @og.tag
	 * この値は、ファイルダウンロード処理に影響します。この値がtrueに指定された時にcommitされたDBTableModelが
	 * ファイルダウンロードの対象の表になります。
	 *
	 * このパラメーターは、通常、各タグにより実装され、ユーザーが指定する必要はありません。
	 * 但し、1つのJSP内でDBTableModelが複数生成される場合に、前に処理したDBTableModelについてファイルダウンロードをさせたい
	 * 場合は、後ろでDBTableModelを生成するタグで、明示的にこの値をfalseに指定することで、ファイルダウンロード処理の対象から
	 * 除外することができます。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規作成
	 *
	 * @param  flag メイントランザクションかどうか [true:メイン/false:その他]
	 */
	public void setMainTrans( final String flag ) {
		isMainTrans = nval( getRequestParameter( flag ),isMainTrans );
	}

	/**
	 * 【TAG】処理時間(queryTime)などの情報出力[true:有効/false:無効]を指定します(初期値:true)。
	 *
	 * @og.tag
	 * Query で、検索する場合に、処理時間(queryTime)などの情報を出力していますが、
	 * ViewForm で、CustomData などの 非HTML表示ビューを使用する場合、データとして、
	 * 紛れ込んでしまうため、出力を抑制する必要があります。
	 * true(有効)にすると、これらのHTMLが出力されます。false にすると、出力されません。
	 * 初期値は、true(有効) です。
	 *
	 * @og.rev 5.3.5.0 (2011/05/01) 新規追加
	 *
	 * @param  useTag  情報出力の有効/無効 [true:有効/false:無効]
	 */
	public void setUseBeforeHtmlTag( final String useTag ) {
		useBeforeHtmlTag = nval( getRequestParameter( useTag ),useBeforeHtmlTag );
	}

	/**
	 * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します
	 *		(初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
	 *
	 * @og.tag
	 * true に設定すると、処理時間を表示するバーイメージが表示されます。
	 * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで
	 * 表示させる機能です。処理時間の目安になります。
	 * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
	 *
	 * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。
	 *
	 * @param	flag	処理時間を表示 [true:する/false:しない]
	 */
	public void setUseTimeView( final String flag ) {
		useTimeView = nval( getRequestParameter( flag ),useTimeView );
	}

	/**
	 * 【TAG】ｴﾗｰﾒｯｾｰｼﾞにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * 通常のｴﾗｰﾒｯｾｰｼﾞは、ﾗﾍﾞﾙ（長）が使われますが、これをﾗﾍﾞﾙ（短）を使いたい場合に、true にセットします。
	 * ここでのラベル（短）は、ﾀｸﾞ修飾なしの、ﾗﾍﾞﾙ（短）です。
	 * 標準はfalse:利用しない=ﾗﾍﾞﾙ（長）です。
	 * true/false以外を指定した場合はfalse扱いとします。
	 *
	 * ﾗﾍﾞﾙﾘｿｰｽの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
	 *
	 * @og.rev 7.0.7.0 (2019/12/13) 新規追加
	 *
	 * @param prm SLABEL利用 [true:利用する/false:利用しない]
	 */
	public void setUseSLabel( final String prm ) {
		useSLabel = nval( getRequestParameter( prm ),useSLabel );
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @og.rev 6.3.1.1 (2015/07/10) SQL文から、TAB→スペース変換と、余計な改行を削除します。
	 *
	 * @return このクラスの文字列表現
	 */
	@Override
	public String toString() {
		return sql == null ? ""
						   : sql.replaceAll( "[\\\t]+"," " ).replaceAll( "[\\s]+\\\n","\\\n" ) ;
		//									  連続するTABをスペースに     連続する空白文字と改行を改行のみに

	//	return ToString.title( this.getClass().getName() )
	//			.println( "VERSION"			,VERSION		)
	//			.println( "tableId"			,tableId		)
	//			.println( "queryType"		,queryType		)
	//			.println( "dbid"			,dbid			)
	//			.println( "command"			,command		)
	//			.println( "skipRowCount"	,skipRowCount	)
	//			.println( "maxRowCount"		,maxRowCount	)
	//			.println( "sql"				,sql			)
	//			.println( "displayMsg"		,displayMsg		)
	//			.println( "overflowMsg"		,overflowMsg	)
	//			.println( "executeCount"	,executeCount	)
	//			.println( "names"			,names			)
	//			.println( "outMessage"		,outMessage		)
	//			.println( "trace" 			,trace 			)
	//			.println( "errCode"	 		,errCode	 	)
	//			.println( "stopZero"		,stopZero		)
	//			.println( "quotCheck"		,quotCheck		)
	//			.println( "dyStart"			,dyStart		)
	//			.println( "checkNames"		,checkNames		)
	//			.println( "Other..."		,getAttributes().getAttribute() )
	//			.fixForm().toString() ;
	}
}
