001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.taglib;
017
018import static org.opengion.fukurou.util.StringUtil.nval;
019
020import java.util.Locale;
021import java.util.Set;                                                                                   // 6.4.3.4 (2016/03/11)
022import java.util.ArrayList;                                                                             // 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
023import java.util.List;                                                                                  // 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
024
025import org.opengion.fukurou.db.DBUtil;
026import org.opengion.fukurou.db.Transaction;
027import org.opengion.fukurou.util.ErrorMessage;
028import org.opengion.fukurou.util.StringUtil;
029import org.opengion.fukurou.util.ArraySet;                                              // 6.4.3.4 (2016/03/11)
030import org.opengion.hayabusa.common.HybsSystem;
031import org.opengion.hayabusa.common.HybsSystemException;                // 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
032import org.opengion.hayabusa.db.DBColumn;
033import org.opengion.hayabusa.db.DBEditConfig;
034import org.opengion.hayabusa.db.DBTableModel;
035import org.opengion.hayabusa.db.Query;
036import org.opengion.hayabusa.db.QueryFactory;
037import org.opengion.hayabusa.resource.GUIInfo;
038
039import static org.opengion.fukurou.system.HybsConst.BR;                 // 6.1.0.0 (2014/12/26) refactoring
040
041/**
042 * データベースの検索を行うタグです。
043 *
044 * このタグの内容に、SQL文を記述します。 whereタグ、 andタグ を使うと引数に応じて
045 * 実行されるSQL文が異なります(使用例参照)。
046 * また、PL/SQLのSPで検索を行うときもこのタグを使います。
047 * SystemData の USE_SQL_INJECTION_CHECK が true か、quotCheck 属性が true の場合は、
048 * SQLインジェクション対策用のシングルクォートチェックを行います。リクエスト引数に
049 * シングルクォート(')が含まれると、エラーになります。
050 * 同様にUSE_XSS_CHECKがtrueか、xssCheck属性がtrueの場合は、
051 * クロスサイトススクリプティング(XSS)対策のためless/greater than signのチェックを行います。
052 *
053 * 実行後にリクエストパラメータに以下の値がセットされます。
054 *   DB.COUNT     : 検索結果の件数
055 *   DB.ERR_CODE  : 検索結果のエラーコード
056 *   DB.IS_UPDATE : 更新(=true)/検索(false)
057 *
058 * ※ このタグは、Transaction タグの対象です。
059 *
060 * @og.formSample
061 * ●形式:
062 *       ・<og:query command="NEW" >
063 *              SELECT文 
064 *         </og:query>
065 *       ・<og:query command="NEW" names="・・・" queryType="JDBCErrMsg" >
066 *              { call PL/SQL(?,?,?,? ) } 
067 *         </og:query>
068 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します)
069 *
070 * ●Tag定義:
071 *   <og:query
072 *       queryType          【TAG】Query を発行する為のクラスID(JDBC,JDBCCallable,JDBCErrMsg,JDBCUpdate,JDBCPrepared)を指定します{@og.doc03Link queryType 初期値:JDBC})
073 *       command            【TAG】コマンド (NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY)
074 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/application]を指定します(初期値:session)
075 *       maxRowCount        【TAG】(通常は使いません)データの最大読み込み件数を指定します (初期値:DB_MAX_ROW_COUNT[=1000])(0:[無制限])
076 *       skipRowCount       【TAG】(通常は使いません)データの読み始めの初期値を指定します
077 *       displayMsg         【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します (初期値:VIEW_DISPLAY_MSG[=])
078 *       overflowMsg        【TAG】検索データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します (初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました])
079 *       notfoundMsg        【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])
080 *       names              【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します
081 *       stopZero           【TAG】検索結果が0件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])
082 *       tableId            【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
083 *       dbid               【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します
084 *       trace              【TAG】引数の SQL 文を EXPLAIN PLAN を[true:行う/それ以外:行わない]を指定します(初期値:false)
085 *       checkNames         【TAG】リクエスト変数の正規化を行うカラムをCSV形式で複数指定します
086 *       modifyType         【TAG】DB検索時の モディファイタイプを指定します[A:追加/C:更新/D:削除]
087 *       stopError          【TAG】PLSQL/SQL処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)
088 *       dispError                      【TAG】エラー時にメッセージを表示するか[true/false]を設定します。通常はstopErrorと併用(初期値:true)
089 *       quotCheck          【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します(初期値:USE_SQL_INJECTION_CHECK)
090 *       xssCheck           【TAG】リクエスト情報の HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_XSS_CHECK[=true])
091 *       mainTrans          【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:true)
092 *       useBeforeHtmlTag   【TAG】処理時間(queryTime)などの情報出力[true:有効/false:無効]を指定します(初期値:true)
093 *       useTimeView        【TAG】処理時間を表示する TimeView を表示するかどうかを指定します
094 *                                                                              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
095 *       useSLabel          【TAG】7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
096 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
097 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
098 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
099 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
100 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
101 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
102 *   >   ... Body ...
103 *   </og:query>
104 *
105 * ●使用例
106 *     <og:query command="NEW">
107 *             select PN,YOBI,NMEN,HINM from XX01 where PN = '{@PN}' order by PN
108 *     </og:query>
109 *
110 *          ・検索条件が入力された時({@PN}がNOT NULLのとき)
111 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where PN = 'AAA' order by PN
112 *          ・検索条件が入力されなかった時({@PN}がNULLのとき)
113 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 where PN = '' order by PN
114 *
115 *     <og:query command="NEW">
116 *             select PN,YOBI,NMEN,HINM from XX01
117 *         <og:where>
118 *             <og:and value="PN = '{@PN}%'" />
119 *             <og:and value="YOBI like '{@YOBI}%'" />
120 *         </og:where>
121 *             order by PN
122 *     </og:query>
123 *
124 *          ・検索条件が入力された時({@PN}がNOT NULLのとき)
125 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 PN = 'AAA%' and YOBI like 'BBB%' order by PN
126 *          ・検索条件が入力されなかった時({@PN}がNULLのとき) WHERE句がなくなる。
127 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 order by PN
128 *
129 *        注意:WhereTagを使った場合、下のようにはなりません。
130 *            select PN,YOBI,NMEN,HINM from XX01 PN = '' and YOBI like '%' order by PN
131 *
132 *     <og:query command="NEW">
133 *             select PN,YOBI,NMEN,HINM from XX01 where PN="11111"
134 *         <og:where startKey="and">
135 *             <og:and value="YOBI like '{@PN}%'" />
136 *         </og:where>
137 *             order by PN
138 *     </og:query>
139 *
140 *          ・検索条件が入力された時({@YOBI}がNOT NULLのとき)
141 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 PN = '11111' and YOBI like 'BBB%' order by PN
142 *          ・検索条件が入力されなかった時({@YOBI}がNULLのとき) WHERE句がなくなる。
143 *            作成されるSQL文⇒select PN,YOBI,NMEN,HINM from XX01 PN = '11111' order by PN
144 *
145 *     <og:query
146 *          command    = "NEW"
147 *          names      = "SYSTEM_ID,LANG,CLM,NAME_JA,LABEL_NAME,KBSAKU,USER.ID"
148 *          checkNames = "CLM,NAME_JA"
149 *          queryType  = "JDBCErrMsg"
150 *          displayMsg = "">
151 *              {call TYPE3B01.TYPE3B01(?,?,?,?)}
152 *     </og:query>
153 *
154 *          ・queryType に JDBCErrMsg を指定して、PL/SQL をコールできます。
155 *            引数は、names 属性をキーにリクエスト変数から読み込みます。
156 *          ・checkNames にカラム名を指定すると、columns.valueSet による
157 *            リクエスト変数の正規化を行います。
158 *
159 * @og.group DB検索
160 * @og.group DB登録
161 *
162 * @version  4.0
163 * @author       Kazuhiko Hasegawa
164 * @since    JDK5.0,
165 */
166public class QueryTag extends CommonTagSupport {
167        /** このプログラムのVERSION文字列を設定します。   {@value} */
168        private static final String VERSION = "7.0.7.0 (2019/12/13)" ;
169        private static final long serialVersionUID = 707020191213L ;
170
171        /** command 引数に渡す事の出来る コマンド  新規 {@value} */
172        public static final String CMD_NEW       = "NEW" ;
173        /** command 引数に渡す事の出来る コマンド  再検索 {@value} */
174        public static final String CMD_RENEW = "RENEW" ;
175        // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
176        private static final Set<String> COMMAND_SET = new ArraySet<>( CMD_NEW , CMD_RENEW );
177
178        /** エラーメッセージID {@value} */
179        // 6.4.1.1 (2016/01/16) QueryTag.errMsgId  → QueryTag.ERR_MSG_ID  refactoring
180        protected static final String ERR_MSG_ID         = HybsSystem.ERR_MSG_KEY;
181
182        // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
183        protected transient DBTableModel        table           ;
184        protected transient ErrorMessage        errMessage      ;
185        protected String        tableId         = HybsSystem.TBL_MDL_KEY;
186        protected String        command         = CMD_NEW;
187        protected int           skipRowCount;
188        protected int           maxRowCount     = -1;
189        protected String        sql                     ;
190        protected int           executeCount = -1;                      // 検索/実行件数
191        protected String        names           ;
192        protected boolean       outMessage      = true;
193        protected int           errCode         = ErrorMessage.OK;
194        private   boolean       updateFlag      ;                               // 6.3.6.1 (2015/08/28) 検索か更新か
195
196        private   String        dbid            ;
197        private   String        queryType       ;
198        private   boolean       trace           ;                               // 4.0.0 (2005/01/31) 廃止
199        private   boolean       stopZero        ;
200        private   String        modifyType      ;                               // 3.8.5.1 (2006/05/08) modifyType 属性を追加します。
201        protected String        displayMsg      = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
202        private   String        overflowMsg     = "MSG0007";    // 検索結果が、制限行数を超えましたので、残りはカットされました。
203        private   String        notfoundMsg     = "MSG0077";    // 対象データはありませんでした。
204        private   boolean       isMainTrans     = true;                 // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
205
206        // 3.5.4.7 (2004/02/06) 実行時間測定用のDIV要素を出力します。
207        protected long          dyStart         ;
208        protected boolean       useTimeView     = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );             // 6.3.6.0 (2015/08/16)
209        // 4.3.3.0 (2008/09/22) PLSQL/SQL実行エラーの際に、処理を中止するかどうか。
210        protected boolean       stopError       = true;
211        // 5.9.26.1 (2017/11/10) 実行エラーの際に、エラーを画面に出力するかどうか。
212        protected boolean dispError             = true;
213
214        private StringBuilder debugMsg  ;                               // 3.5.6.0 (2004/06/18)
215        private String          checkNames      ;                               // 3.8.0.5 (2005/08/20) リクエスト変数の正規化を行います。
216        private String          traceMsg        ;                               // 3.8.5.3 (2006/08/07) トレース時のメッセージ文字列を保存しておきます。
217        protected boolean       quotCheck       = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );      // 4.0.0 (2005/08/31)
218        protected boolean       xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );                        // 5.0.0.2 (2009/09/15)
219
220        private boolean         useBeforeHtmlTag = true ;       // 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定
221        protected boolean       useSLabel       ;                               // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
222
223        // 5.10.2.1 (2018/08/18) where-andタグのplaceHolder情報を保持する
224        private transient List<String> listPlaceValue   ;
225
226        /**
227         * デフォルトコンストラクター
228         *
229         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
230         */
231        public QueryTag() { super(); }          // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
232
233        /**
234         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
235         *
236         * @og.rev 3.5.4.7 (2004/02/06) 実行時間測定用に、開始時刻を取得します。
237         * @og.rev 3.5.6.5 (2004/08/09) 暫定的に、DBTableModelを先行削除します。
238         * @og.rev 3.6.0.0 (2004/09/24) DBTableModel の先行削除は、scope="session" の場合のみ。
239         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
240         * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
241         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
242         * @og.rev 6.4.8.1 (2016/07/02) xssCheckを、doStartTag に移動
243         *
244         * @return      後続処理の指示
245         */
246        @Override
247        public int doStartTag() {
248                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
249                if( useTag() ) {
250                        useXssCheck( xssCheck );                        // 6.4.8.1 (2016/07/02)
251
252                        dyStart = System.currentTimeMillis();
253                        if( ! check( command, COMMAND_SET ) ) { return SKIP_BODY ; }
254
255                        useMainTrans( isMainTrans );                    // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
256                        startQueryTransaction( tableId );               // 3.6.0.8 (2004/11/19)
257
258                        // 3.5.6.5 (2004/08/09) 削除するのは、セッションのオブジェクトでよい。
259                        // 3.6.0.0 (2004/09/24) 削除するのは、scope="session" の場合のみ。
260                        if( "session".equals( getScope() ) ) {
261                                removeSessionAttribute( tableId );
262                                removeSessionAttribute( HybsSystem.VIEWFORM_KEY );
263                        }
264
265                        return EVAL_BODY_BUFFERED ;     // Body を評価する。( extends BodyTagSupport 時)
266                }
267                return SKIP_BODY ;                              // Body を評価しない
268        }
269
270        /**
271         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
272         *
273         * @og.rev 3.1.1.0 (2003/03/28) ボディの内容を取得する処理を、CommonTagSupport で行う。
274         * @og.rev 3.6.0.8 (2004/11/19) エラー発生時に確実にリリースされるように try finally 追加
275         * @og.rev 3.8.5.3 (2006/08/07) USER.LASTSQL へのSQL文の保存は、実行前に行っておきます。
276         * @og.rev 3.8.6.3 (2006/11/30) SQL 文の前後のスペースを取り除きます。
277         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
278         * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更
279         * @og.rev 4.0.0.0 (2005/08/31) useQuotCheck() によるSQLインジェクション対策
280         * @og.rev 4.3.4.0 (2008/12/01) GE20(ユーザー定数)へ登録するかのフラグへの対応
281         * @og.rev 5.0.0.2 (2009/09/15) XSS対応
282         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
283         * @og.rev 5.1.9.0 (2010/08/01) TransactionTag 対応。上位に TransactionTag があれば、そこからConnection をもらう。
284         * @og.rev 5.3.6.0 (2011/06/01) 集計、合計などのEdit機能に対応します。
285         * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更
286         * @og.rev 5.3.7.0 (2011/07/01) PL/SQLかつscope="request"で正しく出力するためqueryType,namesも保存する。
287         * @og.rev 5.3.8.0 (2011/08/01) Transaction発生箇所でclose()
288         * @og.rev 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更。
289         * @og.rev 5.6.5.3 (2013/06/28) LASTQUERYTYPE ⇒ LASTSQLTYPE 変更。
290         * @og.rev 6.3.1.1 (2015/07/10) BodyString,BodyRawStringは、CommonTagSupport で、trim() します。
291         * @og.rev 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
292         * @og.rev 6.3.6.1 (2015/08/28) QueryFactory.close( Query ) 廃止。Queryはキャッシュしません。
293         * @og.rev 6.4.0.2 (2015/12/11) Transaction の commit は、errCode で判定します。
294         * @og.rev 6.4.8.1 (2016/07/02) xssCheckを、doStartTag に移動
295         *
296         * @return      後続処理の指示(SKIP_BODY)
297         */
298        @Override
299        public int doAfterBody() {
300
301                // 4.0.0 (2005/08/31) useQuotCheck() によるSQLインジェクション対策
302                useQuotCheck( quotCheck );
303                // 5.0.0.2 (2009/09/15) XSS対策
304
305                sql = getBodyString();                                  // 6.3.1.1 (2015/07/10)
306
307                // 3.2.1.0 (2003/05/28) 最終SQL文を、UserInfo に、キャッシュしておく。
308                // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
309                if( isMainTrans ) {
310                        setUserInfo( "LASTSQL", sql, false );
311                        // 5.3.7.0 (2011/07/01) PL/SQLかつscope="request"で正しく出力するためqueryType,namesも保存する。
312                        setUserInfo( "LASTSQLTYPE", queryType, false );                         // 5.6.5.3 (2013/06/28)
313                        setUserInfo( "LASTNAMES", names, false );
314                }
315
316                // 6.3.6.1 (2015/08/28) Transaction でAutoCloseableを使用したtry-with-resources構築に対応。
317                try( final Transaction tran = getTransaction() ) {
318                        if( maxRowCount < 0 ) {
319                                maxRowCount     = sysInt( "DB_MAX_ROW_COUNT" );                         // 4:個人設定可
320                        }
321
322                        if( trace ) {
323                                traceMsg = traceQuery( sql,tran );              // 5.1.9.0 (2010/08/01) TransactionTag 対応
324                                // 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更
325                                return SKIP_BODY ;
326                        }
327
328                        final Query query = QueryFactory.newInstance( queryType );
329                        query.setConnection( tran.getConnection( dbid ) );                      // 6.3.6.1 (2015/08/28)
330
331                        query.setSkipRowCount( skipRowCount );
332                        query.setMaxRowCount( maxRowCount );
333                        query.setResourceManager( getResource() );      // 4.0.0 (2005/01/31)
334                        query.setStatement( sql );
335
336                        // 5.3.6.0 (2011/06/01) 集計、合計などのEdit機能に対応します。
337                        if( isMainTrans ) {
338                                final String guikey = getGUIInfoAttri( "KEY" );
339                                final String editName = getRequestValue( "editName" );
340                                final DBEditConfig config = getUser().getEditConfig( guikey, editName );
341                                if( config != null ) {
342                                        query.setEditConfig( config );
343                                }
344                        }
345
346                        execute( query );
347
348                        executeCount = query.getExecuteCount();
349                        if( errCode < ErrorMessage.NG && executeCount >= 0 ) {          // 異常以外の場合
350                                table = query.getDBTableModel();
351                                // 3.8.5.1 (2006/05/08) modifyType 属性を追加します。
352                                if( modifyType != null ) {
353                                        for( int row=0; row<executeCount; row++ ) {
354                                                table.setModifyType( row,modifyType );
355                                        }
356                                }
357                        }
358
359                        // 6.4.0.2 (2015/12/11) errCode のみで判定します。(PL/SQL の場合は、executeCountがセットされない)
360                        if( errCode >= ErrorMessage.NG ) {
361                                tran.rollback();
362                        }
363
364                        tran.commit();
365                }
366                return SKIP_BODY ;
367        }
368
369        /**
370         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
371         *
372         * @og.rev 2.0.0.8 (2002/10/09) command="NEW" のときのみ、displayMsg を表示させます。
373         * @og.rev 2.1.1.4 (2002/11/25) デバッグ時に最終SQLをユーザー情報をセットするように変更。
374         * @og.rev 2.1.2.1 (2002/11/27) ErrorMessage をクリアしないように変更。
375         * @og.rev 3.1.1.0 (2003/03/28) JspWriter オブジェクトの使用箇所を、jspPrint() を使用するように変更。
376         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
377         * @og.rev 3.2.1.0 (2003/05/28) 最終SQL文を、UserInfo に、キャッシュしておく。
378         * @og.rev 3.3.3.3 (2003/08/06) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
379         * @og.rev 3.3.3.3 (2003/08/06) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。
380         * @og.rev 3.5.4.7 (2004/02/06) 実行時間測定用のDIV要素を出力しておきます。
381         * @og.rev 3.5.4.9 (2004/02/25) 警告時に停止していましたが、継続処理させます。
382         * @og.rev 3.5.5.0 (2004/03/12) ErrorMessage オブジェクトを、query が成功した時にもクリアするように変更
383         * @og.rev 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
384         * @og.rev 3.5.5.8 (2004/05/20) ErrorMessage オブジェクトを、コマンドが NEW の場合のみ、クリア
385         * @og.rev 3.5.6.0 (2004/06/18) debugMsg 属性を出力するように修正します。
386         * @og.rev 3.6.0.8 (2004/11/19) DBTableModel をセーブする時に、トランザクションチェックを行います。
387         * @og.rev 3.8.5.3 (2006/08/07) USER.LASTSQL へのSQL文の保存は、実行前に行っておきます。
388         * @og.rev 4.0.0.0 (2006/11/14) notfoundMsg 属性を追加。displayMsg は、VIEW_USE_DISPLAY_MSG で制御
389         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel )
390         * @og.rev 4.3.3.0 (2008/09/22) 属性 stopError の設定により、JSP処理を中止するかどうかを制御します。
391         * @og.rev 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
392         * @og.rev 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定します。
393         * @og.rev 5.5.0.3 (2012/03/13) オーバーフローメッセージが存在しないときは、何もしない。(改行も入れない)
394         * @og.rev 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更。
395         * @og.rev 5.6.3.0 (2013/04/01) エラー時メッセージ変更
396         * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
397         * @og.rev 5.9.16.1 (2017/11/10) dispErrorの動作追加
398         * @og.rev 6.9.9.0 (2018/08/20) 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。
399         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
400         *
401         * @return      後続処理の指示
402         */
403        @Override
404        public int doEndTag() {
405                debugPrint();           // 4.0.0 (2005/02/28)
406                // 5.2.2.0 (2010/11/01) caseKey 、caseVal 属性対応
407                if( !useTag() ) { return EVAL_PAGE ; }
408
409                if( trace ) {
410                        jspPrint( traceMsg );
411                        return SKIP_PAGE;               // 5.5.3.4 (2012/06/19) trace 時は、実際の検索処理を行わない様に変更。
412                }
413
414                String label  = "";                             // 4.0.0 (2005/11/30) 検索しなかった場合。
415                if( check( command, COMMAND_SET ) ) {
416                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
417
418                        // 実行件数の表示 command="NEW" のときのみ、displayMsg を表示させます。
419                        // 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。
420                        if( CMD_NEW.equals( command ) ) {
421                                if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
422                                        buf.append( executeCount )
423                                                .append( getResource().getLabel( displayMsg ) )
424                                                .append( BR );
425                                }
426                                else if( executeCount == 0 && notfoundMsg != null && notfoundMsg.length() > 0 ) {
427                                        buf.append( getResource().getLabel( notfoundMsg ) )
428                                                .append( BR );
429                                }
430                        }
431
432                        // 3.3.3.3 (2003/08/06) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。
433                        setRequestAttribute( "DB.COUNT"   , String.valueOf( executeCount ) );
434                        // 3.3.3.3 (2003/08/06) 検索結果を、"DB.ERR_CODE" キーでリクエストにセットする。
435                        setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) );
436                        // 6.3.6.1 (2015/08/28) 検索か更新か判定するフラグ(updateFlag)を用意します。
437                        setRequestAttribute( "DB.IS_UPDATE", String.valueOf( updateFlag ) );
438
439                        // オーバーフロー時のメッセージを表示
440                        // 5.5.0.3 (2012/03/09) オーバーフローメッセージが存在しないときは、何もしない。(改行も入れない)
441                        if( table != null && table.isOverflow() && overflowMsg != null && overflowMsg.length() > 0  ) {
442                                buf.append( getResource().getLabel( overflowMsg ) )
443                                        .append( BR );
444                        }
445
446                        // 6.9.9.0 (2018/08/20) 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。
447                        if( table != null && ! commitTableObject( tableId, table ) ) {
448                                if( errMessage == null ) { errMessage = new ErrorMessage( "QueryTag Query Error!" ); }
449                                // ERR0041:検索処理中に割り込みの検索要求がありました。処理されません。
450                                errMessage.addMessage( 0,ErrorMessage.NG,"ERR0041" );   
451                                errCode = ErrorMessage.NG;
452                        }
453
454                        // 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
455//                      final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() );
456                        final String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource(),useSLabel );         // 7.0.7.0 (2019/12/13)
457                        if( err != null && err.length() > 0 ) {
458                                buf.append( err );
459                                setSessionAttribute( ERR_MSG_ID,errMessage );
460                        }
461                        else if( CMD_NEW.equals( command ) ) {          // 3.5.5.8 (2004/05/20)
462                                removeSessionAttribute( ERR_MSG_ID );
463                        }
464                        label = buf.toString();
465                        // 5.9.26.1 (2017/11/10) エラーメッセージをリクエスト変数で持つようにしておく
466                        setRequestAttribute( "DB.ERR_MSG", label );
467
468//                      // 6.9.9.0 (2018/08/20) 「ERR0041:検索処理中に割り込みの検索要求がありました」エラーを、標準のErrorMessageに追加するようにします。
469//                      if( table != null && ! commitTableObject( tableId, table ) ) {
470//                              // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
471//                              // jspPrint( "QueryTag Query処理が割り込まれました。DBTableModel は登録しません。" );
472//
473//                              // 5.6.4.0 (2013/04/01) リソースから出力するように対応
474//                              final ErrorMessage errMsgObj = new ErrorMessage( "QueryTag Query Error!" );
475//                              errMsgObj.addMessage( 0,ErrorMessage.NG,"ERR0041" );
476//                              jspPrint( TaglibUtil.makeHTMLErrorTable( errMsgObj,getResource() ) );
477//
478//                              return SKIP_PAGE ;
479//                      }
480                }
481
482                // 5.9.26.1 (2017/11/10) dispErrorで表示をコントロール
483                if( dispError ) {
484                        jspPrint( label );
485                }
486
487                // 3.5.4.9 (2004/02/25) 警告時に停止していましたが、継続処理させます。
488                final int rtnCode ;
489                if( errCode >= ErrorMessage.NG )  {     // 異常
490                        rtnCode = stopError ? SKIP_PAGE : EVAL_PAGE ;
491                }
492                else {
493                        // 件数0件かつ stopZero = true
494                        rtnCode = executeCount == 0 && stopZero ? SKIP_PAGE : EVAL_PAGE ;
495                }
496
497                // 3.5.4.7 (2004/02/06)
498                final long dyTime = System.currentTimeMillis()-dyStart;
499
500                // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録)
501                final GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY );
502                if( guiInfo != null ) { guiInfo.addReadCount( executeCount,dyTime,sql ); }
503
504                // 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定します。
505                if( useTimeView && useBeforeHtmlTag ) {         // 6.3.6.0 (2015/08/16)
506                        jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" );  // 3.5.6.3 (2004/07/12)
507                }
508
509                return rtnCode ;
510        }
511
512        /**
513         * タグリブオブジェクトをリリースします。
514         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
515         *
516         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
517         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
518         * @og.rev 3.5.4.7 (2004/02/06) 実行時間測定用に、dyStart を追加します。
519         * @og.rev 3.5.6.0 (2004/06/18) debugMsg 属性を追加します。
520         * @og.rev 3.8.0.5 (2005/08/20) checkNames 属性を追加します。
521         * @og.rev 3.8.5.1 (2006/05/08) modifyType 属性を追加します。
522         * @og.rev 3.8.5.1 (2006/05/08) traceMsg 属性(トレース時のメッセージ文字列)を追加します。
523         * @og.rev 4.0.0.0 (2005/08/31) quotCheck 属性の追加
524         * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
525         * @og.rev 4.3.3.0 (2008/09/22) stopError 属性の追加
526         * @og.rev 5.0.0.2 (2009/09/15) XSS対応
527         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
528         * @og.rev 5.3.5.0 (2011/05/01) 処理時間(queryTime)などの情報出力の有効/無効を指定します。
529         * @og.rev 6.3.6.1 (2015/08/28) 検索か更新か判定するフラグ(updateFlag)を用意します。
530         * @og.rev 5.9.26.1 (2017/11/10) dispError追加
531         * @og.rev 5.10.2.1 (2018/08/18) プレースホルダ対応
532         * @og.rev 7.0.7.0 (2019/12/13) useSLabel 属性を追加。
533         *
534         */
535        @Override
536        protected void release2() {
537                super.release2();
538                tableId                 = HybsSystem.TBL_MDL_KEY;
539                queryType               = null;
540                dbid                    = null;
541                command                 = CMD_NEW;
542                skipRowCount    = 0;
543                maxRowCount             = -1;
544                table                   = null;
545                sql                             = null;
546                displayMsg              = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
547                overflowMsg             = "MSG0007";    // 検索結果が、制限行数を超えましたので、残りはカットされました。
548                notfoundMsg             = "MSG0077";    // 対象データはありませんでした。
549                executeCount    = -1;                   // 検索/実行件数
550                names                   = null;
551                outMessage              = true;
552                trace                   = false;
553                errCode                 = ErrorMessage.OK;
554                updateFlag              = false;                // 6.3.6.1 (2015/08/28) 検索か更新か
555                errMessage              = null;
556                stopZero                = false;
557                stopError               = true;                 // 4.3.3.0 (2008/09/22)
558                debugMsg                = null;                 // 3.5.6.0 (2004/06/18)
559                checkNames              = null;                 // 3.8.0.5 (2005/08/20)
560                quotCheck               = HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );      // 4.0.0 (2005/08/31)
561                modifyType      = null;                 // 3.8.5.1 (2006/05/08)
562                traceMsg                = null;                 // 3.8.5.3 (2006/08/07)
563                xssCheck                = HybsSystem.sysBool( "USE_XSS_CHECK" );        // 5.0.0.2 (2009/09/15)
564                isMainTrans             = true;                 // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
565                useBeforeHtmlTag        = true ;        // 5.3.5.0 (2011/05/01)
566                useTimeView             = HybsSystem.sysBool( "VIEW_USE_TIMEBAR" );     // 6.3.6.0 (2015/08/16)
567                dispError               = true;                 // 5.9.26.1 (2017/11/10) 
568                listPlaceValue  = null;                 // 5.10.2.1 (2018/08/18)
569                useSLabel               = false;                // 7.0.7.0 (2019/12/13) エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)
570        }
571
572        /**
573         * Query を実行します。
574         *
575         * @og.rev 2.1.2.3 (2002/12/02) データベース更新時に、更新フラグをセットするように変更
576         * @og.rev 3.4.0.0 (2003/09/01) 登録エラー時のキーと値を表示するように変更。
577         * @og.rev 3.5.6.0 (2004/06/18) デバッグ情報出力用に、printDebug メソッドを追加。
578         * @og.rev 3.6.1.0 (2005/01/05) エラーコードによる commit/rollback の判断追加
579         * @og.rev 5.3.7.0 (2011/07/01) nameの判定にゼロ文字列を付加
580         * @og.rev 6.2.1.0 (2015/03/13) エラーメッセージに SQL文を追加します。
581         * @og.rev 6.3.6.1 (2015/08/28) 検索か更新か判定するフラグ(updateFlag)を用意します。
582         * @og.rev 6.4.2.0 (2016/01/29) HybsSystemException を catch して、再び、throw する処理を廃止します。
583         * @og.rev 6.9.9.0 (2018/08/20) プレースホルダ対応( 5.10.2.1 (2018/08/18) )
584         *
585         * @param       query オブジェクト
586         */
587        protected void execute( final Query query ) {
588                String[] nameArray = null;
589                String[] values    = null;
590
591                // 6.4.2.0 (2016/01/29) HybsSystemException を catch して、再び、throw する処理を廃止します。
592        //      try {
593//                      if( names == null || names.isEmpty() ) {
594                        if( names == null || names.isEmpty() ) {
595                                // 6.9.9.0 (2018/08/20) プレースホルダ対応( 5.10.2.1 (2018/08/18) )
596                                if( listPlaceValue != null && !listPlaceValue.isEmpty() ) {
597                                        values = listPlaceValue.toArray( new String[ listPlaceValue.size() ] );
598                                        query.execute( values );
599                                }
600                                else {
601                                        query.execute();
602                                }
603                        }
604                        else {
605                                nameArray = StringUtil.csv2Array( names );
606                                values = getRequest( nameArray );
607                                // 3.5.6.0 (2004/06/18) デバッグ情報出力用
608                                if( isDebug() ) { printDebug( nameArray,values ); }
609                                query.execute( values );
610                        }
611                        errCode         = query.getErrorCode();
612                        errMessage      = query.getErrorMessage();
613                        updateFlag      = query.isUpdate();                             // 6.3.6.1 (2015/08/28) 検索か更新か
614                        // 3.6.1.0 (2005/01/05) エラーコードによる commit/rollback の判断追加
615        //      }
616        //      catch( final HybsSystemException ex ) {
617
618        //              // 4.0.0 (2005/02/28) エラー時の表示とデバッグ時の表示を統一する。
619        //              String errMsg = "DATABASE ERROR! " + CR
620        //                                              + "SQL=["       + sql + "]" + CR;               // 6.2.1.0 (2015/03/13)
621        //              if( nameArray != null ) {
622        //                      printDebug( nameArray,values );
623        //                      errMsg += debugMsg;
624        //              }
625        //              throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
626        //      }
627        }
628
629        /**
630         * デバッグ用に、配列データを書き出します。
631         *
632         * @og.rev 3.5.6.0 (2004/06/18) 新規追加
633         *
634         * @param       nms     names配列
635         * @param       vals    namesに対応するリクエスト情報の配列
636         */
637        private void printDebug( final String[] nms,final String[] vals ) {
638                if( debugMsg == null ) { debugMsg = new StringBuilder( BUFFER_MIDDLE ); }
639
640                debugMsg.append( "  names=[" )
641                        .append( StringUtil.array2csv( nms ) )
642                        .append( ']' )                                                          // 6.0.2.5 (2014/10/31) char を append する。
643                        .append( CR )
644                        .append( "  values=[" )
645                        .append( StringUtil.array2csv( vals ) )
646                        .append( ']' )                                                          // 6.0.2.5 (2014/10/31) char を append する。
647                        .append( CR );
648        }
649
650        /**
651         * 名称配列を元に、リクエスト情報のデータを取得します。
652         * checkNames 属性に設定されているカラムがあれば、値を正規化します。
653         *
654         * @og.rev 3.8.0.5 (2005/08/20) リクエスト変数の正規化(checkNames)対応
655         *
656         * @param       nameArray       キーとなる名称の配列
657         *
658         * @return      そのリクエスト情報の配列(可変長引数)
659         * @og.rtnNotNull
660         */
661        protected String[] getRequest( final String... nameArray ) {
662                String[] rtn = new String[nameArray.length];
663
664                for( int i=0; i<rtn.length; i++ ) {
665                        rtn[i] = getRequestValue( nameArray[i] );
666
667                        // 3.8.0.5 (2005/08/20) checkNames があり、rtn[i] がある場合。
668                        // 6.9.7.0 (2018/05/14) PMD These nested if statements could be combined
669//                      if( checkNames != null && checkNames.length() > 0 && rtn[i].length() > 0 ) {
670//                              if( ( "," + checkNames + "," ).indexOf( "," + nameArray[i] + "," ) >= 0 ) {
671                        if( checkNames != null && checkNames.length() > 0 && rtn[i].length() > 0
672                                && ( "," + checkNames + "," ).indexOf( "," + nameArray[i] + "," ) >= 0 ) {
673                                        final DBColumn dbColumn = getDBColumn( nameArray[i] );
674                                        final String val = dbColumn.valueSet( rtn[i] );
675                                        if( val != null ) { rtn[i] = val; }
676//                              }
677                        }
678                }
679
680                return rtn;
681        }
682
683        /**
684         * 子タグ(andタグ)でプレースホルダーを設定した時に利用するメソッドです。
685         * queryタグ内部のプレースホルダリストに追加します。
686         *
687         * @og.rev 6.9.9.0 (2018/08/20) 新規追加( 5.10.2.1 (2018/08/18) )
688         *
689         * @param val 追加分
690         */
691        protected void addPlaceValue( final String val ) {
692                if( !"JDBCPrepared".equals( queryType ) ) {                     // placeHolderは、JDBCPrepared専用です。
693                        final String errMsg = "andタグの、placeHolder は、queryType=\"JDBCPrepared\" 専用です。val=[" + val + "]" + CR ;
694                        throw new HybsSystemException( errMsg );
695                }
696
697                if(listPlaceValue == null) {
698                        listPlaceValue = new ArrayList<String>();
699                }
700
701                // カンマで分割
702                final String[] vals = StringUtil.csv2Array( val );
703                for( final String tmp: vals ) {
704                        // リクエストパラメータの値を変換して、リストに追加
705                        listPlaceValue.add( getRequestParameter( tmp ) );
706                }
707        }
708
709        /**
710         * 【TAG】(通常は使いません)データの読み始めの初期値を指定します。
711         *
712         * @og.tag
713         * データベース自体の検索は,指定されたSQLの全件を検索しますが,
714         * DBTableModelのデータとしては、スキップ件数分は登録されません。
715         * サーバーのメモリ資源と応答時間の確保の為です。
716         *
717         * @param       count 読み始めの初期値
718         */
719        public void setSkipRowCount( final String count ) {
720                skipRowCount = nval( getRequestParameter( count ),skipRowCount );
721        }
722
723        /**
724         * 【TAG】(通常は使いません)データの最大読み込み件数を指定します
725         *              (初期値:DB_MAX_ROW_COUNT[={@og.value SystemData#DB_MAX_ROW_COUNT}])。
726         *
727         * @og.tag
728         * データベース自体の検索は,指定されたSQLの全件を検索しますが,
729         * DBTableModelのデータとして登録する最大件数をこの値に設定します。
730         * サーバーのメモリ資源と応答時間の確保の為です。
731         * 0 をセットすると、無制限(Integer.MAX_VALUE)になります。
732         * (初期値:ユーザー定数のDB_MAX_ROW_COUNT[={@og.value SystemData#DB_MAX_ROW_COUNT}])。
733         *
734         * @og.rev 5.5.8.5 (2012/11/27) 0を無制限として処理します。
735         *
736         * @param       count 最大件数
737         * @see         org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT
738         */
739        public void setMaxRowCount( final String count ) {
740                maxRowCount = nval( getRequestParameter( count ),maxRowCount );
741                if( maxRowCount == 0 ) { maxRowCount = Integer.MAX_VALUE ; }            // 5.5.8.5 (2012/11/27)
742        }
743
744        /**
745         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
746         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
747         *
748         * @og.tag
749         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
750         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
751         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
752         * この tableId 属性を利用して、メモリ空間を分けます。
753         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
754         *
755         * @param       id テーブルID (sessionに登録する時のID)
756         */
757        public void setTableId( final String id ) {
758                tableId   = nval( getRequestParameter( id ),tableId );  // 3.8.0.9 (2005/10/17)
759        }
760
761        /**
762         * 【TAG】Query を発行する為のクラスID(JDBC,JDBCCallable,JDBCErrMsg,JDBCUpdate,JDBCPrepared)を指定します({@og.doc03Link queryType 初期値:JDBC})。
763         *
764         * @og.tag
765         * 検索を実行する手段は、Query インターフェースの実装クラスになります。
766         * このタグでは、Query.execute( String[] ) メソッドが呼ばれます。
767         * 例えば、ストアドプロシージャ等を実行する場合に、queryType="JDBCErrMsg"
768         * を指定することができます。
769         * 初期値は、"JDBC" です。
770         * queryType は、システムリソースの Query_**** 宣言の **** を与えます。
771         * これらは、Query インターフェースを継承したサブクラスである必要があります。
772         * 標準で、org.opengion.hayabusa.db 以下の Query_**** クラスが、Query_**** 宣言 と
773         * して、定義されています。
774         * 属性クラス定義の {@link org.opengion.hayabusa.db.Query Query} を参照願います。
775         * {@og.doc03Link queryType Query_**** クラス}
776         *
777         * @param       id Query実クラス
778         * @see         org.opengion.hayabusa.db.Query  Queryのサブクラス
779         * @see         org.opengion.hayabusa.db.Query#execute( String[] )
780         */
781        public void setQueryType( final String id ) {
782                queryType = getRequestParameter( id );
783        }
784
785        /**
786         * 【TAG】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します。
787         *
788         * @og.tag
789         * Queryオブジェクトを作成する時のDB接続IDを指定します。
790         * これは、システムリソースで、DEFAULT_DB_URL 等で指定している データベース接続先
791         * 情報に、XX_DB_URL を定義することで、 dbid="XX" とすると、この 接続先を使用して
792         * データベースにアクセスできます。
793         *
794         * @param       id データベース接続ID
795         */
796        public void setDbid( final String id ) {
797                dbid = nval( getRequestParameter( id ),dbid );
798        }
799
800        /**
801         * 【TAG】コマンド (NEW,RENEW)をセットします(PlsqlUpdateTag,UpdateTag の場合は、ENTRY)。
802         *
803         * @og.tag
804         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
805         * フィールド定数値のいづれかを、指定できます。
806         *
807         * @param       cmd コマンド (public static final 宣言されている文字列)
808         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.QueryTag.CMD_NEW">コマンド定数</a>
809         */
810        public void setCommand( final String cmd ) {
811                final String cmd2 = getRequestParameter( cmd );
812                if( cmd2 != null && cmd2.length() >= 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
813        }
814
815        /**
816         * 【TAG】検索結果が0件のとき処理を停止するかどうか[true/false]を指定します(初期値:false[続行する])。
817         *
818         * @og.tag
819         * 初期値は、false(続行する)です。
820         *
821         * @param  cmd 0件時停止可否 [true:処理を中止する/false:続行する]
822         */
823        public void setStopZero( final String cmd ) {
824                stopZero = nval( getRequestParameter( cmd ),stopZero );
825        }
826
827        /**
828         * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します
829         *              (初期値:VIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
830         *
831         * @og.tag
832         * ここでは、検索結果の件数や登録された件数をまず出力し、
833         * その次に、ここで指定したメッセージをリソースから取得して表示します。
834         * 件数を表示させる場合は、displayMsg = "MSG0033"[ 件検索しました] をセットしてください。
835         * 表示させたくない場合は, displayMsg = "" をセットしてください。
836         * (初期値:システム定数のVIEW_DISPLAY_MSG[={@og.value SystemData#VIEW_DISPLAY_MSG}])。
837         *
838         * @param       id 表示メッセージID
839         * @see         org.opengion.hayabusa.common.SystemData#VIEW_DISPLAY_MSG
840         */
841        public void setDisplayMsg( final String id ) {
842                final String ids = getRequestParameter( id );
843                if( ids != null ) { displayMsg = ids; }
844        }
845
846        /**
847         * 【TAG】検索データが最大検索数をオーバーした場合に表示するメッセージリソースIDを指定します
848         *              (初期値:MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました])。
849         *
850         * @og.tag
851         * 検索結果が、maxRowCount で設定された値より多い場合、何らかのデータは検索されず
852         * 切り捨てられたことになります。
853         * ここでは、displayMsg を表示した後、必要に応じて、このメッセージを表示します。
854         * 表示させたくない場合は, overflowMsg = "" をセットしてください。
855         * 初期値は、MSG0007[検索結果が、制限行数を超えましたので、残りはカットされました]です。
856         *
857         * @param       id オーバー時メッセージID
858         */
859        public void setOverflowMsg( final String id ) {
860                final String ids = getRequestParameter( id );
861                if( ids != null ) { overflowMsg = ids; }
862        }
863
864        /**
865         * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。
866         *
867         * @og.tag
868         * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。
869         * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、
870         * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。
871         * 表示させたくない場合は, notfoundMsg = "" をセットしてください。
872         * 初期値は、MSG0077[対象データはありませんでした]です。
873         *
874         * @param       id ゼロ件メッセージID
875         */
876        public void setNotfoundMsg( final String id ) {
877                final String ids = getRequestParameter( id );
878                if( ids != null ) { notfoundMsg = ids; }
879        }
880
881        /**
882         * 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します。
883         *
884         * @og.tag
885         * 複数ある場合は、CSV形式で渡します。
886         * names 属性は、queryType に応じて設定可否が異なりますので、ご注意ください。
887         * names なし:JDBC,JDBCUpdate,JDBCPrepared
888         * names あり:JDBCCallable,JDBCErrMsg,JDBCUpdate
889         * (JDBCUpdateは、names 属性のあり/なし両方に対応しています。)
890         *
891         * @og.rev 3.0.1.3 (2003/03/11) names 属性に null で渡す場合のバグを修正
892         *
893         * @param       nm 引数の名称 (CSV形式)
894         */
895        public void setNames( final String nm ) {
896                names = nval( getRequestParameter( nm ),names );
897        }
898
899        /**
900         * 【TAG】検索結果のメッセージを表示する/しない[true/false]を指定します(初期値:true)。
901         *
902         * @og.tag
903         * 初期値は、表示する:true です。
904         *
905         * @param       flag 検索結果メッセージ [true:表示する/それ以外:含めない]
906         */
907        public void setOutMessage( final String flag ) {
908                outMessage = nval( getRequestParameter( flag ),outMessage );
909        }
910
911        /**
912         * 【TAG】引数の SQL 文を EXPLAIN PLAN を[true:行う/それ以外:行わない]を指定します(初期値:false)。
913         *
914         * @og.tag
915         *
916         * ここでは、以下の処理を行います。
917         * 1.引数の SQL 文を画面に表示します。
918         * 2.引数の SQL 文を EXPLAIN PLAN した結果を、画面に表示します。
919         * なお、以前は、セッションのトレースを行っていましたが、その機能は、廃止いたします。
920         * 初期値は、行わない:false です。
921         *
922         * @param       flag トレース [true:行う/それ以外:行わない]
923         */
924        public void setTrace( final String flag ) {
925                trace = nval( getRequestParameter( flag ),trace );
926        }
927
928        /**
929         * 【TAG】リクエスト変数の正規化を行うカラムをCSV形式で複数指定します。
930         *
931         * @og.tag
932         * PL/SQLを利用する場合の引数にセットすべき データを、リクエスト変数の
933         * 値そのままではなく、カラムオブジェクトの valueSet メソッド経由で正規化
934         * した値を使用するようにします。
935         *
936         * これは、カラムリソース定義の文字種別で定義された変換ルールを適用します。
937         * DBTableModel を使っていないため、カラム定義は、columnEditor等で変更できません。
938         *
939         * @og.rev 3.8.0.5 (2005/08/20) 新規追加
940         *
941         * @param       nm 正規化処理カラム (CSV形式)
942         */
943        public void setCheckNames( final String nm ) {
944                checkNames = nval( getRequestParameter( nm ),checkNames );
945        }
946
947        /**
948         * 【TAG】DB検索時の モディファイタイプを指定します[A:追加/C:更新/D:削除]。
949         *
950         * @og.tag
951         * DB検索時に、そのデータをA(追加)、C(更新)、D(削除)のモディファイタイプを
952         * つけた状態にします。
953         * その状態で、そのまま、update する事が可能になります。
954         *
955         * @og.rev 3.8.5.1 (2006/05/08) 新規追加
956         *
957         * @param   type Dモディファイタイプ [A:追加/C:更新/D:削除]
958         */
959        public void setModifyType( final String type ) {
960                modifyType = nval( getRequestParameter( type ),modifyType );
961        }
962
963        /**
964         * 【TAG】リクエスト情報の シングルクォート(') 存在チェックを実施するかどうか[true/false]を設定します
965         *              (初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])。
966         *
967         * @og.tag
968         * SQLインジェクション対策の一つとして、暫定的ではありますが、SQLのパラメータに
969         * 渡す文字列にシングルクォート(') を許さない設定にすれば、ある程度は防止できます。
970         * 数字タイプの引数には、 or 5=5 などのシングルクォートを使用しないコードを埋めても、
971         * 数字チェックで検出可能です。文字タイプの場合は、必ず (')をはずして、
972         * ' or 'A' like 'A のような形式になる為、(')チェックだけでも有効です。
973         * (') が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
974         * 初期値は、SystemData#USE_SQL_INJECTION_CHECK です。
975         *
976         * @og.rev 4.0.0.0 (2005/08/31) 新規追加
977         *
978         * @param   flag クォートチェック [true:する/それ以外:しない]
979         */
980        public void setQuotCheck( final String flag ) {
981                quotCheck = nval( getRequestParameter( flag ),quotCheck );
982        }
983
984        /**
985         * 【TAG】リクエスト情報の HTMLTag開始/終了文字(&gt;&lt;) 存在チェックを実施するかどうか[true/false]を設定します
986         *              (初期値:USE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。
987         *
988         * @og.tag
989         * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
990         * (&gt;&lt;) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
991         * (初期値:システム定数のUSE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])
992         *
993         * @og.rev 5.0.0.2 (2009/09/15) 新規追加
994         *
995         * @param       flag    XSSチェック [true:する/false:しない]
996         * @see         org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
997         */
998        public void setXssCheck( final String flag ) {
999                xssCheck = nval( getRequestParameter( flag ),xssCheck );
1000        }
1001
1002        /**
1003         * 【TAG】PLSQL/SQL処理エラーの時に処理を中止するかどうか[true/false]を設定します(初期値:true)。
1004         *
1005         * @og.tag
1006         * false(中止しない)に設定する場合、後続処理では、{&#064;DB.ERR_CODE}の値により、
1007         * PLSQL/SQLの異常/正常終了によって分岐処理は可能となります。
1008         * 初期値は、true(中止する)です。
1009         *
1010         * @og.rev 4.3.3.0 (2008/09/22) 新規追加
1011         *
1012         * @param   flag エラー時処理中止 [true:中止する/false:中止しない]
1013         */
1014        public void setStopError( final String flag ) {
1015                stopError = nval( getRequestParameter( flag ),stopError );
1016        }
1017
1018        /**
1019         * 【TAG】PLSQL/SQL処理エラーの時にエラーを画面表示するか[true/false]を設定します(初期値:true)。
1020         *
1021         * @og.tag
1022         * false(表示しない)に設定する場合、後続処理では、{&#064;DB.ERR_MSG}の値により、
1023         * 本来表示されるはずだったメッセージを取得可能です。
1024         * stopErrorと併用して、JSON形式でエラーを返す場合等に利用します。
1025         * 初期値は、true(表示する)です。
1026         * ※false指定の場合は件数や、overFlowメッセージ等も表示されなくなります。
1027         *
1028         * @og.rev 5.9.26.1 (2017/11/10) 新規追加
1029         *
1030         * @param   flag  [true:表示する/false:表示しない]
1031         */
1032        public void setDispError( final String flag ) {
1033                dispError = nval( getRequestParameter( flag ),dispError );
1034        }
1035
1036        /**
1037         * 引数の SQL 文を EXPLAIN PLAN します。
1038         *
1039         * ここでは、以下の処理を行います。
1040         * 1.引数の SQL 文を画面に表示します。
1041         * 2.引数の SQL 文を EXPLAIN PLAN した結果を、画面に表示します。
1042         * なお、この処理は、ORACLE 専用処理です。
1043         *
1044         * @og.rev 3.8.5.3 (2006/08/07) 新規追加
1045         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
1046         * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応します。
1047         * @og.rev 5.5.3.4 (2012/06/19) getUserInfo は、キー部分だけで処理します。
1048         *
1049         * @param   sql         対象となるSQL文
1050         * @param   tran        Transactionオブジェクト
1051         *
1052         * @return トレース結果の文字列
1053         * @og.rtnNotNull
1054         */
1055        private String traceQuery( final String sql , final Transaction tran ) {
1056                final String userId = getUserInfo( "ID" ) ;                     // 5.5.3.4 (2012/06/19) getUserInfo は、キー部分だけで処理します。
1057
1058                final String[] arg1 = new String[] { userId };
1059                DBUtil.dbExecute( "DELETE FROM PLAN_TABLE WHERE STATEMENT_ID = ?",arg1,tran,dbid );             // 5.1.9.0 (2010/08/01)
1060
1061                final String explan1 = "EXPLAIN PLAN SET STATEMENT_ID = '" + userId + "' FOR " + sql ;
1062                DBUtil.dbExecute( explan1,null,tran,dbid );             // 5.1.9.0 (2010/08/01)
1063
1064                final String[] arg2 = new String[] { userId,userId,userId };
1065                final String explan2 = "select LEVEL as LVL"
1066                                                        + ",lpad(' ',LEVEL,' ') || rtrim( OPERATION ) || ' ' || rtrim( OPTIONS ) || ' ' || rtrim( OBJECT_NAME ) as EXECUTION_PLAN"
1067                                                        + ",OBJECT_NAME                 as OBJ_NAME"
1068                                                        + ",DECODE(INSTR(OBJECT_TYPE,' '),0,OBJECT_TYPE,SUBSTR(OBJECT_TYPE,1,INSTR(OBJECT_TYPE,' ')-1)) as OBJ_TYPE"
1069                                                        + ",OPTIMIZER                   as OPT"
1070                                                        + ",COST                                as CST"
1071                                                        + ",CARDINALITY                 as CARD"
1072                                                        + ",BYTES                               as BYTE"
1073                                                        + ",ACCESS_PREDICATES   as ACCS"
1074                                                        + ",FILTER_PREDICATES   as FILTER"
1075                                                + " from PLAN_TABLE"
1076                                                + " where STATEMENT_ID = ?"
1077                                                + " start with ID = 0"
1078                                                + "       and STATEMENT_ID = ?"
1079                                                + " connect by prior ID = PARENT_ID"
1080                                                + "       and STATEMENT_ID = ?" ;
1081
1082                final String[][] plan = DBUtil.dbExecute( explan2,arg2,tran,dbid,true );                        // 5.5.3.4 (2012/06/19) ヘッダー情報も同時に取得する。
1083
1084                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
1085                        .append( "<pre>" ).append( sql ).append( "</pre>" ).append( BR )
1086                        .append( "<table>" )
1087                        .append( "<tr class=\"row_h\">" );                      // 1行目のヘッダーの出力
1088                final int colsize = plan[0].length;
1089                for( int j=0; j<colsize; j++ ) {
1090                        buf.append( "<th>" ).append( plan[0][j] ).append( "</th>" );
1091                }
1092                buf.append( "</tr>" );
1093
1094                for( int i=1; i<plan.length; i++ ) {
1095                        buf.append( "<tr class=\"row_" ).append( i%2 ).append( "\">" );
1096                        for( int j=0; j<colsize; j++ ) {
1097                                if( j==1 ) {
1098                                        buf.append( "<td><pre>" ).append( plan[i][1] ).append( "</pre></td>" );
1099                                }
1100                                else {
1101                                        buf.append( "<td>" ).append( plan[i][j] ).append( "</td>" );
1102                                }
1103                        }
1104                        buf.append( "</tr>" );
1105                }
1106                buf.append( "</table>" ).append( BR );
1107                return buf.toString();
1108        }
1109
1110        /**
1111         * 【TAG】(通常は使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:true)。
1112         *
1113         * @og.tag
1114         * この値は、ファイルダウンロード処理に影響します。この値がtrueに指定された時にcommitされたDBTableModelが
1115         * ファイルダウンロードの対象の表になります。
1116         *
1117         * このパラメーターは、通常、各タグにより実装され、ユーザーが指定する必要はありません。
1118         * 但し、1つのJSP内でDBTableModelが複数生成される場合に、前に処理したDBTableModelについてファイルダウンロードをさせたい
1119         * 場合は、後ろでDBTableModelを生成するタグで、明示的にこの値をfalseに指定することで、ファイルダウンロード処理の対象から
1120         * 除外することができます。
1121         *
1122         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
1123         *
1124         * @param  flag メイントランザクションかどうか [true:メイン/false:その他]
1125         */
1126        public void setMainTrans( final String flag ) {
1127                isMainTrans = nval( getRequestParameter( flag ),isMainTrans );
1128        }
1129
1130        /**
1131         * 【TAG】 処理時間(queryTime)などの情報出力[true:有効/false:無効]を指定します(初期値:true)。
1132         *
1133         * @og.tag
1134         * Query で、検索する場合に、処理時間(queryTime)などの情報を出力していますが、
1135         * ViewForm で、CustomData などの 非HTML表示ビューを使用する場合、データとして、
1136         * 紛れ込んでしまうため、出力を抑制する必要があります。
1137         * true(有効)にすると、これらのHTMLが出力されます。false にすると、出力されません。
1138         * 初期値は、true(有効) です。
1139         *
1140         * @og.rev 5.3.5.0 (2011/05/01) 新規追加
1141         *
1142         * @param  useTag  情報出力の有効/無効 [true:有効/false:無効]
1143         */
1144        public void setUseBeforeHtmlTag( final String useTag ) {
1145                useBeforeHtmlTag = nval( getRequestParameter( useTag ),useBeforeHtmlTag );
1146        }
1147
1148        /**
1149         * 【TAG】処理時間を表示する TimeView を表示するかどうか[true:する/false:しない]を指定します
1150         *              (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
1151         *
1152         * @og.tag
1153         * true に設定すると、処理時間を表示するバーイメージが表示されます。
1154         * これは、DB検索、APサーバー処理、画面表示の各処理時間をバーイメージで
1155         * 表示させる機能です。処理時間の目安になります。
1156         * (初期値:VIEW_USE_TIMEBAR[={@og.value SystemData#VIEW_USE_TIMEBAR}])。
1157         *
1158         * @og.rev 6.3.6.0 (2015/08/16) useTimeView の初期値を、VIEW_USE_TIMEBAR にする。
1159         *
1160         * @param       flag    処理時間を表示 [true:する/false:しない]
1161         */
1162        public void setUseTimeView( final String flag ) {
1163                useTimeView = nval( getRequestParameter( flag ),useTimeView );
1164        }
1165
1166        /**
1167         * 【TAG】エラーメッセージにSLABELを利用するかどうか[true/false]を指定します(初期値:false)。
1168         *
1169         * @og.tag
1170         * 通常のエラーメッセージは、ラベル(長)が使われますが、これをラベル(短)を使いたい場合に、true にセットします。
1171         * ここでのラベル(短)は、タグ修飾なしの、ラベル(短)です。
1172         * 標準はfalse:利用しない=ラベル(長)です。
1173         * true/false以外を指定した場合はfalse扱いとします。
1174         *
1175         * ラベルリソースの概要説明があれば表示しますが、useSLabel="true" 時は、概要説明を表示しません。
1176         *
1177         * @og.rev 7.0.7.0 (2019/12/13) 新規追加
1178         *
1179         * @param prm SLABEL利用 [true:利用する/false:利用しない]
1180         */
1181        public void setUseSLabel( final String prm ) {
1182                useSLabel = nval( getRequestParameter( prm ),useSLabel );
1183        }
1184
1185        /**
1186         * このオブジェクトの文字列表現を返します。
1187         * 基本的にデバッグ目的に使用します。
1188         *
1189         * @og.rev 6.3.1.1 (2015/07/10) SQL文から、TAB→スペース変換と、余計な改行を削除します。
1190         *
1191         * @return このクラスの文字列表現
1192         */
1193        @Override
1194        public String toString() {
1195                return sql == null ? ""
1196                                                   : sql.replaceAll( "[\\\t]+"," " ).replaceAll( "[\\s]+\\\n","\\\n" ) ;
1197                //                                                                        連続するTABをスペースに     連続する空白文字と改行を改行のみに
1198
1199        //      return ToString.title( this.getClass().getName() )
1200        //                      .println( "VERSION"                     ,VERSION                )
1201        //                      .println( "tableId"                     ,tableId                )
1202        //                      .println( "queryType"           ,queryType              )
1203        //                      .println( "dbid"                        ,dbid                   )
1204        //                      .println( "command"                     ,command                )
1205        //                      .println( "skipRowCount"        ,skipRowCount   )
1206        //                      .println( "maxRowCount"         ,maxRowCount    )
1207        //                      .println( "sql"                         ,sql                    )
1208        //                      .println( "displayMsg"          ,displayMsg             )
1209        //                      .println( "overflowMsg"         ,overflowMsg    )
1210        //                      .println( "executeCount"        ,executeCount   )
1211        //                      .println( "names"                       ,names                  )
1212        //                      .println( "outMessage"          ,outMessage             )
1213        //                      .println( "trace"                       ,trace                  )
1214        //                      .println( "errCode"                     ,errCode                )
1215        //                      .println( "stopZero"            ,stopZero               )
1216        //                      .println( "quotCheck"           ,quotCheck              )
1217        //                      .println( "dyStart"                     ,dyStart                )
1218        //                      .println( "checkNames"          ,checkNames             )
1219        //                      .println( "Other..."            ,getAttributes().getAttribute() )
1220        //                      .fixForm().toString() ;
1221        }
1222}