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.fukurou.db;
017
018import java.util.List;
019import java.util.ArrayList;
020import java.util.Locale ;
021import java.util.Arrays ;
022import java.util.Set ;
023import java.util.HashSet ;
024import java.util.LinkedHashSet ;
025import java.util.StringJoiner ;
026
027import org.opengion.fukurou.util.StringUtil;
028import org.opengion.fukurou.system.OgBuilder ;
029import org.opengion.fukurou.system.OgRuntimeException ;
030import static org.opengion.fukurou.system.HybsConst.CR;
031import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
032
033/**
034 * QueryMaker は、カラム名などから、SELECT,INSERT,UPDATE,DALETE 文字列を作成するクラスです。
035 *
036 * 基本的には、カラム名と、それに対応する値のセットで、QUERY文を作成します。
037 * 値には、[カラム名] が使用でき、出力される値として、? が使われます。
038 * これは、PreparedStatement に対する引数で、処理を行うためです。
039 * この[カラム名]のカラム名は、検索された側のカラム名で、INSERT/UPDATE/DELETE等が実行される
040 * データベース(テーブル)のカラム名ではありません。(偶然、一致しているかどうかは別として)
041 *
042 * @og.rev 6.8.6.0 (2018/01/19) 新規作成
043 *
044 * @version  6.8.6.0 (2018/01/19)
045 * @author       Kazuhiko Hasegawa
046 * @since    JDK6.0,
047 */
048public class QueryMaker {
049        private static final String QUERY_TYPE = "SELECT,INSERT,UPDATE,DELETE,MERGE" ;
050
051        private final List<String> whrList = new ArrayList<>() ;        // where条件に含まれる [カラム名] のリスト(パラメータ一覧)
052
053        private String queryType ;              // QUERYタイプ(SELECT,INSERT,UPDATE,DELETE,MERGE) を指定します。
054        private String table ;
055        private String names ;
056        private String omitNames ;
057        private String where ;
058        private String whrNames ;
059        private String orderBy ;
060        private String cnstKeys ;
061        private String cnstVals ;
062
063        private int             clmLen;                 // names カラムの "?" に置き換えられる個数
064        private boolean isSetup ;               // セットアップ済みを管理しておきます。
065        private String[] nameAry;
066
067        /**
068         * デフォルトコンストラクター
069         *
070         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
071         */
072        public QueryMaker() { super(); }                // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
073
074        /**
075         * 処理の前に、入力データの整合性チェックや、初期設定を行います。
076         *
077         * あまり、何度も実行したくないので、フラグ管理しておきます。
078         *
079         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
080         * @og.rev 6.9.0.2 (2018/02/13) omitNamesの対応
081         * @og.rev 6.9.8.0 (2018/05/28) setup() は、内部処理からのみ呼ばれるので、private化します。
082         */
083//      public void setup() {
084        private void setup() {
085                if( isSetup ) { return; }               // セットアップ済み
086
087                if( StringUtil.isNull( table ) ) {
088                        final String errMsg = "指定の table に、null、ゼロ文字列は指定できません。"
089                                                        + " table=" + table ;
090                        throw new OgRuntimeException( errMsg );
091                }
092
093                if( StringUtil.isNull( names ) ) {
094                        final String errMsg = "指定の names に、null、ゼロ文字列は指定できません。"
095                                                        + " names=" + names ;
096                        throw new OgRuntimeException( errMsg );
097                }
098
099                // 6.9.0.2 (2018/02/13) omitNamesの対応
100                final String[]    nmAry  = StringUtil.csv2Array( names );
101                final Set<String> nmSet  = new LinkedHashSet<>( Arrays.asList( nmAry ) );               // names の順番は、キープします。
102                final String[]    omtAry = StringUtil.csv2Array( omitNames );
103                final Set<String> omtSet = new HashSet<>( Arrays.asList( omtAry ) );                    // 除外する順番は、問いません。
104                nmSet.removeAll( omtSet );
105
106                // 初期設定
107                clmLen  = nmSet.size();
108//              nameAry = nmSet.toArray( new String[clmLen] );
109                nameAry = nmSet.toArray( new String[0] );       // 8.5.4.2 (2024/01/12) PMD 7.0.0 OptimizableToArrayCall 対応
110
111//              // 初期設定
112//              nameAry = StringUtil.csv2Array( names );
113//              clmLen = nameAry.length;
114
115                // [カラム名] List は、whereNames + where の順番です。(whrListの登録順を守る必要がある)
116                // where条件も、この順番に連結しなければなりません。
117                where = StringUtil.join( " AND " , whrNames , formatSplit( where ) );   // formatSplit で、whrListの登録を行っている。
118
119                isSetup = true;
120        }
121
122        /**
123         * データを検索する場合に使用するSQL文を作成します。
124         *
125         * SELECT names FROM table WHERE where ORDER BY orderBy ;
126         *
127         * cnstKeys,cnstVals は、使いません。
128         * where,orderBy は、それぞれ、値が存在しない場合は、設定されません。
129         *
130         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
131         * @og.rev 6.9.0.2 (2018/02/13) omitNamesの対応
132         *
133         * @return  検索SQL
134         * @og.rtnNotNull
135         */
136        public String getSelectSQL() {
137                if( !"SELECT".equals( queryType ) ) {
138                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
139                                                        + " 要求SQL=SELECT  queryType=" + queryType ;
140                        throw new OgRuntimeException( errMsg );
141                }
142
143                setup();
144
145                return new OgBuilder()
146//                      .append(   "SELECT "    , names )
147                        .append(   "SELECT "            )
148                        .join(     ","          , nameAry )             // 6.9.0.2 (2018/02/13) names ではなく、omitNames後のカラム配列を使用します。
149                        .append(   " FROM "     , table )
150                        .appendNN( " WHERE "    , where )               // nullなら、追加しない。where + whereNames
151                        .appendNN( " ORDER BY " , orderBy )             // nullなら、追加しない。
152                        .toString();
153        }
154
155        /**
156         * データを追加する場合に使用するSQL文を作成します。
157         *
158         * INSERT INTO table ( names,cnstKeys ) VALUES ( values,cnstVals ) ;
159         *
160         * cnstKeys,cnstVals は、INSERTカラムとして使います。
161         * where,orderBy は、使いません。
162         *
163         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
164         * @og.rev 6.9.0.2 (2018/02/13) omitNamesの対応
165         *
166         * @return  追加SQL
167         * @og.rtnNotNull
168         */
169        public String getInsertSQL() {
170                if( !"INSERT".equals( queryType ) && !"MERGE".equals( queryType ) ) {
171                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
172                                                        + " 要求SQL=INSERT  queryType=" + queryType ;
173                        throw new OgRuntimeException( errMsg );
174                }
175
176                setup();
177
178                return new OgBuilder()
179                        .append( "INSERT INTO " ).append( table )
180//                      .append( " ( " ).append( names )
181                        .append( " ( " )
182                        .join(     "," , nameAry  )                     // 6.9.0.2 (2018/02/13) names ではなく、omitNames後のカラム配列を使用します。
183                        .appendNN( "," , cnstKeys )
184                        .append( " ) VALUES ( " )
185                        .appendRoop( 0,clmLen,",",i -> "?" )
186                        .appendNN( "," , cnstVals )
187                        .append( " )" )
188                        .toString();
189        }
190
191        /**
192         * データを更新する場合に使用するSQL文を作成します。
193         *
194         * UPDATE table SET names[i]=values[i], ・・・cnstKeys[i]=cnstVals[i], ・・・ WHERE where;
195         *
196         * cnstKeys,cnstVals は、UPDATEカラムとして使います。
197         * orderBy は、使いません。
198         *
199         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
200         * @og.rev 6.9.9.1 (2018/08/27) cnstKeys,cnstValsは、個数違いの場合のみ、エラーです。
201         *
202         * @return  更新SQL
203         * @og.rtnNotNull
204         */
205        public String getUpdateSQL() {
206                if( !"UPDATE".equals( queryType ) && !"MERGE".equals( queryType ) ) {
207                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
208                                                        + " 要求SQL=UPDATE  queryType=" + queryType ;
209                        throw new OgRuntimeException( errMsg );
210                }
211
212                setup();
213
214                final String[] cnKey = StringUtil.csv2Array( cnstKeys );        // @og.rtnNotNull
215                final String[] cnVal = StringUtil.csv2Array( cnstVals );        // @og.rtnNotNull
216
217                // 整合性チェック
218                // 6.9.8.0 (2018/05/28) FindBugs:null でないことがわかっている値の冗長な null チェック
219//              if( cnKey != null && cnVal == null ||
220//                      cnKey == null && cnVal != null ||
221//                      cnKey != null && cnVal != null && cnKey.length != cnVal.length ) {
222                // 6.9.9.1 (2018/08/27) cnstKeys,cnstValsは、個数違いの場合のみ、エラーです。
223//              if( cnKey.length == 0 || cnVal.length == 0 || cnKey.length != cnVal.length ) {
224//                      final String errMsg = "指定の keys,vals には、null、ゼロ件配列、または、個数違いの配列は指定できません。"
225                if( cnKey.length != cnVal.length ) {
226                        final String errMsg = "指定の keys,vals の個数が違ます。"
227                                                        + " keys=" + cnstKeys
228                                                        + " vals=" + cnstVals ;
229                        throw new OgRuntimeException( errMsg );
230                }
231
232                // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
233                // queryType と、nameAry は、setup() メソッドで設定されるため、FindBugs の指摘は、対応済みとなります。
234                // とりあえず、条件判定を入れておいて、FindBugs の警告が出ないようにしておきます。
235                if( nameAry == null ) {
236                        // nameAry は、setup() メソッドで設定されるため、このエラーは出ません。
237                        final String errMsg = "何らかの不測の事態が発生しました。本来、このエラーは出ません。";
238                        throw new OgRuntimeException( errMsg );
239                }
240
241                return new OgBuilder()
242                        .append( "UPDATE " ).append( table )
243                        .append( " SET " )
244                        .appendRoop( 0,clmLen      ,",",i -> nameAry[i] + "=?" )
245                        .appendRoop( 0,cnVal.length,",",i -> cnKey[i]   + "="  + cnVal[i] )
246                        .appendNN( " WHERE " , where )          // nullなら、追加しない。where + whereNames
247                        .toString();
248        }
249
250        /**
251         * データを削除する場合に使用するSQL文を作成します。
252         *
253         * DELETE FROM table WHERE where;
254         *
255         * cnstKeys,cnstVal,orderBys は、使いません。
256         * where は、値が存在しない場合は、設定されません。
257         * orderBy は、使いません。
258         *
259         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
260         *
261         * @return  削除SQL
262         * @og.rtnNotNull
263         */
264        public String getDeleteSQL() {
265                if( !"DELETE".equals( queryType ) ) {
266                        final String errMsg = "指定のQUERYタイプと異なるSQL文を要求しています。" + CR
267                                                        + " 要求SQL=DELETE  queryType=" + queryType ;
268                        throw new OgRuntimeException( errMsg );
269                }
270
271                setup();
272
273                return new OgBuilder()
274                        .append(   "DELETE FROM " ).append( table )
275                        .appendNN( " WHERE " , where )          // nullなら、追加しない。where + whereNames
276                        .toString();
277        }
278
279        /**
280         * [カラム名]を含む文字列を分解し、Map に登録します。
281         *
282         * これは、[カラム名]を含む文字列を分解し、カラム名 を取り出し、whrList に
283         * 追加していきます。
284         * 戻り値は、[XXXX] を、? に置換済みの文字列になります。
285         *
286         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
287         *
288         * @param       fmt     [カラム名]を含む文字列
289         * @return  PreparedStatementに対応した変換後の文字列
290         */
291        private String formatSplit( final String fmt ) {
292                if( StringUtil.isNull( fmt ) ) { return fmt; }          // null,ゼロ文字列チェック
293
294                final StringBuilder rtnStr = new StringBuilder( BUFFER_MIDDLE );
295
296                int start = 0;
297                int index = fmt.indexOf( '[' );
298                while( index >= 0 ) {
299                        final int end = fmt.indexOf( ']',index );
300                        if( end < 0 ) {
301                                final String errMsg = "[ と ] との対応関係がずれています。"
302                                                                + "format=[" + fmt + "] : index=" + index ;
303                                throw new OgRuntimeException( errMsg );
304                        }
305
306                        // [ より前方の文字列は、rtnStr へ追加する。
307                        if( index > 0 ) { rtnStr.append( fmt.substring( start,index ) ); }
308        //              index == 0 は、][ と連続しているケース
309
310                        // [XXXX] の XXXX部分と、位置(?の位置になる)を、Listに登録
311                        whrList.add( fmt.substring( index+1,end ) );
312
313                        rtnStr.append( '?' );           // [XXXX] を、? に置換する。
314
315                        start = end+1 ;
316                        index = fmt.indexOf( '[',start );
317                }
318                // ] の後方部分は、rtnStr へ追加する。
319                rtnStr.append( fmt.substring( start ) );                // '[' が見つからなかった場合は、この処理で、すべての fmt データが、append される。
320
321                return rtnStr.toString();
322        }
323
324        /**
325         * QUERYタイプ(SELECT,INSERT,UPDATE,DELETE,MERGE) を指定します。
326         *
327         * 引数が nullか、ゼロ文字列の場合は、登録しません。
328         *
329         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
330         *
331         * @param       queryType       QUERYタイプ
332         */
333        public void setQueryType( final String queryType ) {
334                if( !StringUtil.isNull( queryType ) ) {
335                        if( QUERY_TYPE.contains( queryType ) ) {
336                                this.queryType = queryType;
337                        }
338                        else {
339                                final String errMsg = "queryType は、" + QUERY_TYPE + " から、指定してください。";
340                                throw new OgRuntimeException( errMsg );
341                        }
342                }
343        }
344
345        /**
346         * テーブル名をセットします。
347         *
348         * 引数が nullか、ゼロ文字列の場合は、登録しません。
349         *
350         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
351         *
352         * @param       table   テーブル名
353         */
354        public void setTable( final String table ) {
355                if( !StringUtil.isNull( table ) ) {
356                        this.table = table;
357                }
358        }
359
360        /**
361         * テーブル名を取得します。
362         *
363         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
364         *
365         * @return      テーブル名
366         */
367        public String getTable() {
368                return table;
369        }
370
371        /**
372         * カラム名をセットします。
373         *
374         * カラム名は、登録時に、大文字に変換しておきます。
375         * カラム名は、CSV形式でもかまいません。
376         * 引数が nullか、ゼロ文字列の場合は、登録しません。
377         *
378         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
379         *
380         * @param   names  キー(大文字のみ。内部で変換しておきます。)
381         */
382        public void setNames( final String names ) {
383                if( !StringUtil.isNull( names ) ) {
384                        this.names = names.toUpperCase(Locale.JAPAN);
385                }
386        }
387
388        /**
389         * カラム名を取得します。
390         *
391         * 登録時に、すでに、大文字に変換していますので、
392         * ここで取得するカラム名も、大文字に変換されています。
393         *
394         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
395         *
396         * @return      カラム名(大文字に変換済み)
397         */
398        public String getNames() {
399                return names;
400        }
401
402        /**
403         * 除外するカラム名をセットします。
404         *
405         * カラム名は、登録時に、大文字に変換しておきます。
406         * カラム名は、CSV形式でもかまいません。
407         * 引数が nullか、ゼロ文字列の場合は、登録しません。
408         *
409         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
410         *
411         * @param   omitNames  キー(大文字のみ。内部で変換しておきます。)
412         */
413        public void setOmitNames( final String omitNames ) {
414                if( !StringUtil.isNull( omitNames ) ) {
415                        this.omitNames = omitNames.toUpperCase(Locale.JAPAN);
416                }
417        }
418
419        /**
420         * WHERE条件をセットします。
421         *
422         * whereNames属性と同時に使用する場合は、"AND" で、処理します。
423         * 引数が nullか、ゼロ文字列の場合は、登録しません。
424         *
425         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
426         *
427         * @param   where  WHERE条件
428         */
429        public void setWhere( final String where ) {
430                if( !StringUtil.isNull( where ) ) {
431                        this.where = where;
432                }
433        }
434
435        /**
436         * WHERE条件となるカラム名をCSV形式でセットします。
437         *
438         * カラム名配列より、WHERE条件を、KEY=[KEY] 文字列で作成します。
439         * where属性と同時に使用する場合は、"AND" で、処理します。
440         * 引数が nullか、ゼロ件配列の場合は、登録しません。
441         *
442         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
443         *
444         * @param       whNames WHERE句作成のためのカラム名
445         */
446        public void setWhereNames( final String whNames ) {
447                if( !StringUtil.isNull( whNames ) ) {
448                        final String[] whAry = StringUtil.csv2Array( whNames );
449
450                        final StringJoiner sj = new StringJoiner( " AND " );            // 区切り文字
451                        for( final String whName : whAry ) {
452                                whrList.add( whName );
453                                sj.add( whName + "=?" );
454                        }
455                        whrNames = sj.toString();
456                }
457        }
458
459        /**
460         * orderBy条件をセットします。
461         *
462         * 引数が nullか、ゼロ文字列の場合は、登録しません。
463         *
464         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
465         *
466         * @param   orderBy  orderBy条件
467         */
468        public void setOrderBy( final String orderBy ) {
469                if( !StringUtil.isNull( orderBy ) ) {
470                        this.orderBy = orderBy;
471                }
472        }
473
474        /**
475         * 固定値のカラム名をセットします。
476         *
477         * nullでなく、ゼロ文字列でない場合のみセットします。
478         * カラム名は、CSV形式でもかまいません。
479         * 引数が nullか、ゼロ文字列の場合は、登録しません。
480         *
481         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
482         *
483         * @param   keys  固定値のカラム名
484         */
485        public void setConstKeys( final String keys ) {
486                if( !StringUtil.isNull( keys ) ) {
487                        this.cnstKeys = keys;
488                }
489        }
490
491        /**
492         * 固定値のカラム名に対応した、固定値文字列をセットします。
493         *
494         * nullでなく、ゼロ文字列でない場合のみセットします。
495         * 固定値は、CSV形式でもかまいません。
496         * 引数が nullか、ゼロ文字列の場合は、登録しません。
497         *
498         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
499         *
500         * @param   vals  固定値
501         */
502        public void setConstVals( final String vals ) {
503                if( !StringUtil.isNull( vals ) ) {
504                        this.cnstVals = vals;
505                }
506        }
507
508        /**
509         * PreparedStatement で、パラメータとなるカラム名の配列を返します。
510         *
511         * これは、QUERYの変数部分 "[カラム名]" を、"?" に置き換えており、
512         * この、カラム名の現れた順番に、配列として返します。
513         * データベース処理では、パラメータを設定する場合に、このカラム名を取得し、
514         * オリジナル(SELECT)のカラム番号から、その値を取得しなければなりません。
515         *
516         * カラム名配列は、QUERYタイプ(queryType)に応じて作成されます。
517         * SELECT : パラメータ は使わないので、長さゼロの配列
518         * INSERT : where条件は使わず、names部分のみなので、0 ~ clmLen までの配列
519         * UPDATE : names も、where条件も使うため、すべての配列
520         * DELETE : names条件は使わず、where部分のみなので、clmLen ~ clmLen+whrLen までの配列(clmLen以降の配列)
521         *
522         * @og.rev 6.8.6.0 (2018/01/19) 新規作成
523         * @og.rev 6.9.8.0 (2018/05/28) セットアップチェックが漏れていた。
524         * @og.rev 8.5.5.1 (2024/02/29) switch式の使用
525         *
526         * @param       useInsert       queryType="MERGE" の場合に、false:UPDATE , true:INSERT のパラメータのカラム名配列を返します。
527         * @return      パラメータとなるカラム名の配列
528         * @og.rtnNotNull
529         */
530        public String[] getParamNames( final boolean useInsert ) {
531                // 6.9.8.0 (2018/05/28) FindBugs:コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している
532                // queryType と、nameAry は、setup() メソッドで設定されるため、FindBugs の指摘は、対応済みとなります。
533                // とりあえず、条件判定を入れておいて、FindBugs の警告が出ないようにしておきます。
534
535                // 6.9.8.0 (2018/05/28) セットアップチェックが漏れていた。
536                // 8.5.5.1 (2024/02/29) spotbugs UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
537                // ※ StringUtil.isNull( queryType ) で、queryType の nullチェックは出来ているが、spotbugs で判断できないので、あえて入れておきます。
538//              if( !isSetup || StringUtil.isNull( queryType ) || nameAry == null ) {
539                if( queryType == null || !isSetup || StringUtil.isNull( queryType ) || nameAry == null ) {
540                        final String errMsg = "getParamNames(boolean) は、SQL文を取得してから、行ってください。";
541                        throw new OgRuntimeException( errMsg );
542                }
543
544//              final String[] whrAry = whrList.toArray( new String[whrList.size()] );
545                final String[] whrAry = whrList.toArray( new String[0] );       // 8.5.4.2 (2024/01/12) PMD 7.0.0 OptimizableToArrayCall 対応
546                final String[] allAry = Arrays.copyOf( nameAry , nameAry.length + whrList.size() );
547                System.arraycopy( whrAry , 0 , allAry , nameAry.length , whrAry.length );               // allAry = nameAry + whrAry の作成
548
549                // 8.5.5.1 (2024/02/29) switch式の使用
550//              String[] rtnClms = null;
551//              switch( queryType ) {
552//                      case "SELECT" : rtnClms = new String[0];        break;          // パラメータはない。
553//                      case "INSERT" : rtnClms = nameAry;                      break;          // names指定の分だけ、パラメータセット
554//                      case "UPDATE" : rtnClms = allAry;                       break;          // names+whereの分だけ、パラメータセット
555//                      case "DELETE" : rtnClms = whrAry;                       break;          // whereの分だけ、パラメータセット
556//                      case "MERGE"  : rtnClms = allAry;                       break;          // useInsert=false は、UPDATEと同じ
557//                      default : break;
558//              }
559                String[] rtnClms = switch( queryType ) {
560                        case "SELECT" -> new String[0];         // パラメータはない。
561                        case "INSERT" -> nameAry;                       // names指定の分だけ、パラメータセット
562                        case "UPDATE" -> allAry;                        // names+whereの分だけ、パラメータセット
563                        case "DELETE" -> whrAry;                        // whereの分だけ、パラメータセット
564                        case "MERGE"  -> allAry;                        // useInsert=false は、UPDATEと同じ
565                        default           -> null;                              // 上記以外は、null
566                };
567
568                if( useInsert && "MERGE".equals( queryType ) ) {
569                        rtnClms = nameAry;                                      // MERGEで、useInsert=true は、INSERTと同じ
570                }
571
572                return rtnClms;
573        }
574}