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.Locale;
019import java.util.Arrays;                                        // 6.3.9.1 (2015/11/27)
020import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
021
022/**
023 * 各データベースに対応するenum名を返します。
024 * 主に、各データベースにおける関数名の差異を吸収するためのenumです。
025 * 本来は、互換性のあるファンクション以外、使用しないようにしましょう。
026 * また、無ければ互換性パックなどで、ファンクションを定義してしまうのも
027 * 一つの方法です。
028 *
029 * <table class="plain">
030 *  <caption>各データベースにおける関数</caption>
031 *  <tr><th>データベース名 </th><th>連結</th><th>部分文字列</th><th>日付関数         </th><th>DUAL検索         </th></tr>
032 *  <tr><td>{&#064;DBF.XXX}</td><td>CON </td><td>SUBSTR    </td><td>SYSDATE          </td><td>FROM_DUAL        </td></tr>
033 *  <tr><td>ORACLE         </td><td>||  </td><td>SUBSTR    </td><td>SYSDATE          </td><td>FROM DUAL        </td></tr>
034 *  <tr><td>HSQL           </td><td>||  </td><td>SUBSTR    </td><td>CURRENT_TIMESTAMP</td><td>FROM DUAL        </td></tr>
035 *  <tr><td>POSTGRES       </td><td>||  </td><td>SUBSTR    </td><td>CURRENT_DATE     </td><td>                 </td></tr>
036 *  <tr><td>MYSQL          </td><td>||  </td><td>SUBSTR    </td><td>now()            </td><td>FROM DUAL        </td></tr>
037 *  <tr><td>SQLSERVER      </td><td>+   </td><td>SUBSTRING </td><td>GETDATE()        </td><td>                 </td></tr>
038 *  <tr><td>FIREBIRD       </td><td>||  </td><td>SUBSTR    </td><td>CURRENT_DATE     </td><td>FROM RDB$DATABASE</td></tr>
039 *  <tr><td>DERBY          </td><td>||  </td><td>SUBSTR    </td><td>CURRENT_TIMESTAMP</td><td>FROM SYSIBM.SYSDUMMY1</td></tr>
040 *  <tr><td>CACHE          </td><td>||  </td><td>SUBSTRING </td><td>SYSDATE          </td><td>FROM DUAL        </td></tr>
041 *  <tr><td>H2             </td><td>||  </td><td>SUBSTR    </td><td>SYSDATE          </td><td>FROM DUAL        </td></tr>
042 *  <tr><td>OTHER          </td><td>||  </td><td>SUBSTR    </td><td>SYSDATE          </td><td>FROM DUAL        </td></tr>
043 * </table>
044 *
045 * ※ MySQLでは、通常文字列連結は、CONCAT関数を使います。パイプ(||)で結合するには、
046 * SET sql_mode='PIPES_AS_CONCAT' しておく必要があります。
047 * JDBCで設定する場合は、jdbc:mysql://《サーバー》/《DB名》?sessionVariables=sql_mode='PIPES_AS_CONCAT'
048 * と指定します。
049 *
050 * @og.rev 5.1.4.0 (2010/03/01) 新規作成
051 * @og.rev 5.8.5.0 (2015/03/06) CACHE追加
052 * @og.rev 8.0.1.1 (2021/11/12) FROM DUAL 項目追加
053 *
054 * @version  5.0
055 * @author   Kazuhiko Hasegawa
056 * @since    JDK5.0,
057 */
058public enum DBFunctionName {
059        /** 引数付きenum定義:ここに、必要な関数が増えるたびに、追加していきます。 */
060        // 6.2.2.1 (2015/03/31) SYSDATE 項目追加
061        //                                                                        CON     SUBSTR                  SYSDATE                                 FROM_DUAL
062          /** ファンクション名 */ ORACLE        ( "||" , "SUBSTR"               , "SYSDATE"                             ,"FROM DUAL" )
063        , /** ファンクション名 */ HSQL          ( "||" , "SUBSTR"               , "CURRENT_TIMESTAMP"   ,"FROM DUAL" )          // 良く判らん
064        , /** ファンクション名 */ POSTGRES      ( "||" , "SUBSTR"               , "CURRENT_DATE"                ,""     )
065        , /** ファンクション名 */ MYSQL         ( "||" , "SUBSTR"               , "now()"                               ,"FROM DUAL" )          // SYSDATE() でもほとんど同じ
066        , /** ファンクション名 */ SQLSERVER     ( "+"  , "SUBSTRING"    , "GETDATE()"                   ,""     )
067        , /** ファンクション名 */ FIREBIRD      ( "||" , "SUBSTR"               , "CURRENT_DATE"                ,"FROM RDB$DATABASE" )
068        , /** ファンクション名 */ DERBY         ( "||" , "SUBSTR"               , "CURRENT_TIMESTAMP"   ,"FROM SYSIBM.SYSDUMMY1" )      // 6.4.9.3 (2016/08/26)
069        , /** ファンクション名 */ CACHE         ( "||" , "SUBSTRING"    , "SYSDATE"                             ,"FROM DUAL" )          // 良く判らん
070        , /** ファンクション名 */ H2            ( "||" , "SUBSTR"               , "SYSDATE"                             ,"FROM DUAL" )          // 6.8.3.0 (2017/11/27) 追加
071        , /** ファンクション名 */ OTHER         ( "||" , "SUBSTR"               , "SYSDATE"                             ,"FROM DUAL" ) ;        // 6.4.5.0 (2016/04/08) 適当
072
073        private final String dbfCON ;
074        private final String dbfSUBSTR ;
075        private final String dbfSYSDATE ;                                                       // 6.2.2.1 (2015/03/31) SYSDATE 項目追加
076        // 8.5.4.2 (2024/01/12) PMD 7.0.0 FieldNamingConventions
077//      private final String dbfFROM_DUAL ;                                                     // 8.0.1.1 (2021/11/12) FROM DUAL 項目追加
078        private final String dbfFROMDUAL ;                                                      // 8.0.1.1 (2021/11/12) FROM DUAL 項目追加
079
080        /**
081         * コンストラクター(enum の場合は、private宣言される)
082         *
083         * @og.rev 5.1.4.0 (2010/03/01) 新規作成
084         * @og.rev 6.2.2.1 (2015/03/31) SYSDATE 項目追加
085         * @og.rev 8.0.1.1 (2021/11/12) FROM DUAL 項目追加
086         *
087         * @param       con             第1引数にて指定(CON)
088         * @param       substr  第2引数にて指定(SUBSTR)
089         * @param       sysdt   第3引数にて指定(SYSDATE)
090         * @param       dual    第4引数にて指定(FROM DUAL)
091         */
092//      private DBFunctionName( final String con , final String substr , final String sysdt ) {
093//      DBFunctionName( final String con , final String substr , final String sysdt ) {
094        DBFunctionName( final String con , final String substr , final String sysdt , final String dual ) {
095                dbfCON    = con;
096                dbfSUBSTR = substr;
097                dbfSYSDATE= sysdt;
098//              dbfFROM_DUAL= dual;
099                dbfFROMDUAL= dual;              // 8.5.4.2 (2024/01/12) PMD 7.0.0
100        }
101
102        /**
103         * 共通ファンクションに対応するデータベース個別のファンクション名を返します。
104         *
105         * 現時点では、NAME,CON,SUBSTR のみ使用できます。
106         *
107         * @og.rev 5.1.4.0 (2010/03/01) 新規作成
108         * @og.rev 6.2.2.1 (2015/03/31) SYSDATE 項目追加
109         * @og.rev 6.3.9.1 (2015/11/27) メソッドの出口は、最後の1か所にすべきです(PMD)。
110         * @og.rev 5.9.19.1 (2017/04/14) DBF.TYPE追加
111         * @og.rev 8.0.1.1 (2021/11/12) FROM DUAL 項目追加
112         *
113         * @param   func 共通ファンクション
114         *
115         * @return  ファンクション名
116         */
117        public String getFunctionName( final String func ) {
118
119                final String rtn ;
120                if(      "NAME"     .equalsIgnoreCase( func ) ) { rtn = toString();     }               // この、enum の NAME
121                else if( "CON"      .equalsIgnoreCase( func ) ) { rtn = dbfCON;         }
122                else if( "SUBSTR"   .equalsIgnoreCase( func ) ) { rtn = dbfSUBSTR;      }
123                else if( "SYSDATE"  .equalsIgnoreCase( func ) ) { rtn = dbfSYSDATE;     }
124//              else if( "FROM_DUAL".equalsIgnoreCase( func ) ) { rtn = dbfFROM_DUAL;   }       // 8.0.1.1 (2021/11/12)
125                else if( "FROM_DUAL".equalsIgnoreCase( func ) ) { rtn = dbfFROMDUAL; }          // 8.5.4.2 (2024/01/12) PMD 7.0.0
126                else if( "TYPE"     .equalsIgnoreCase( func ) ) { rtn = toString().toLowerCase( Locale.JAPAN ); }               // 5.9.19.1 (2017/04/14)
127                else {                                                                                  rtn = func;                     }
128
129                return rtn ;
130        }
131
132        /**
133         * 各データベースに対応するファンクション名を返します。
134         *
135         * @og.rev 4.3.8.0 (2009/08/01) SUBSTRを追加
136         * @og.rev 5.1.2.0 (2010/01/01) MySQL対応、SUBSTRB廃止(帳票データの分割の内部処理化に伴う)
137         * @og.rev 5.1.4.0 (2010/03/01) データベース名 ではなく、dbid で判断するように変更
138         * @og.rev 5.7.7.2 (2014/06/20) DBF.NAME 時の処理の簡素化
139         * @og.rev 6.2.1.0 (2015/03/13) NAME だけ特殊処理するのではなく、統一する。
140         *
141         * @param   func ファンクション名(定義文字)
142         * @param   dbid 接続先ID
143         *
144         * @return  実ファンクション名
145         */
146        public static String getFunctionName( final String func ,final String dbid ) {
147
148                // 5.7.7.2 (2014/06/20) DBF.NAME 時の処理の簡素化
149                final String dbName = ConnectionFactory.getDBName( dbid );
150        // 6.2.1.0 (2015/03/13) NAME だけ特殊処理するのではなく、統一する。
151        //      if( "NAME".equals( func ) ) { return dbName; }
152        //      else {
153                        return getDBName( dbName ).getFunctionName( func );
154        //      }
155        }
156
157        /**
158         * シーケンス名よりシーケンスオブジェクトを検索し、次の値を取り出します。
159         * DBに対するシーケンスオブジェクトは予め作成されている必要があります。
160         *
161         * また、MySQLの場合は、シーケンスオブジェクトが実装されていないため、
162         * 内部的には、引数のシーケンス名と同じ名前のテーブルから、Integer型の
163         * "SEQID"という項目名を検索することにより、シーケンスをエミュレートしています。
164         *
165         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
166         * @og.rev 5.8.5.0 (2015/03/06) CACHE追加
167         * @og.rev 5.9.31.1 (2018/04/13) DBID追加対応
168         *
169         * @param seqName シーケンス名
170         * @param tran トランザクション
171         *
172         * @return シーケンス番号
173         */
174        public int getSequence( final String seqName, final Transaction tran ) {
175                return getSequence( seqName, tran, null );
176        }
177
178        /**
179         * シーケンス名よりシーケンスオブジェクトを検索し、次の値を取り出します。
180         * DBに対するシーケンスオブジェクトは予め作成されている必要があります。
181         *
182         * また、MySQLの場合は、シーケンスオブジェクトが実装されていないため、
183         * 内部的には、引数のシーケンス名と同じ名前のテーブルから、Integer型の
184         * "SEQID"という項目名を検索することにより、シーケンスをエミュレートしています。
185         *
186         * @og.rev 5.1.9.0 (2010/08/01) 新規追加
187         * @og.rev 5.8.5.0 (2015/03/06) CACHE追加
188         * @og.rev 6.4.9.3 (2016/08/26) JavaDB(APACHE DERBY) の追加
189         * @og.rev 5.9.31.1 (2018/04/13) DBID追加対応
190         * @og.rev 6.9.8.0 (2018/05/28) DBID 対応漏れ、arg引数がないことを明確にするため、nullを渡します。
191         * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 FormalParameterNamingConventions 対応
192         * @og.rev 8.5.5.1 (2024/02/29) switch式の使用
193         *
194         * @param seqName シーケンス名
195         * @param tran トランザクション
196         * @param dbid データベースID
197         *
198         * @return シーケンス番号
199         */
200//      public int getSequence( final String seqName, final Transaction tran ) {
201//      public int getSequence( final String seqName, final Transaction tran, final String DBID ) {
202        // 8.5.4.2 (2024/01/12) PMD 7.0.0 FormalParameterNamingConventions 対応 (DBID ⇒ dbid)
203        public int getSequence( final String seqName, final Transaction tran, final String dbid ) {
204                // 8.5.5.1 (2024/02/29) switch式の使用
205//              String sql = null;
206//              // 6.0.2.5 (2014/10/31) refactoring:無効な代入です。
207//              switch ( this ) {
208//                      case ORACLE:
209//                              sql = "select " + seqName + ".nextval from dual";
210//                              break;
211//                      case HSQL:
212//                              sql = "select next value for " + seqName + " from dual";
213//                              break;
214//                      case POSTGRES:
215//                              sql = "select nextval('" + seqName + "')";
216//                              break;
217//                      case MYSQL:
218//                              sql = "update " + seqName + " set SEQID = last_insert_id(SEQID+1)";
219////                            DBUtil.dbExecute( sql, new String[0], tran );
220////                            DBUtil.dbExecute( sql, null, tran, DBID );              // 6.9.8.0 (2018/05/28) DBID 対応漏れ
221//                              DBUtil.dbExecute( sql, null, tran, dbid );              // 6.9.8.0 (2018/05/28) dbid 対応漏れ
222//                              sql = "select last_insert_id()";
223//                              break;
224//                      case SQLSERVER:
225//                              throw new OgRuntimeException( "現在、SQLSERVERではシーケンス機能はサポートされていません。" );
226//                      case FIREBIRD:
227//                              sql = "select gen_id(" + seqName + ", 1) from rdb$database";
228//                              break;
229//                      // 6.4.9.3 (2016/08/26)
230//                      case DERBY:
231//                              throw new OgRuntimeException( "現在、DERBYではシーケンス機能はサポートされていません。" );
232//                      // 5.8.5.0 (2015/03/06) CACHE追加
233//                      case CACHE:
234//                              throw new OgRuntimeException( "現在、CACHEではシーケンス機能はサポートされていません。" );
235//                      default:
236//                              throw new OgRuntimeException( "現在、このデータベースではシーケンス機能はサポートされていません。" );
237//              }
238                // 6.0.2.5 (2014/10/31) refactoring:無効な代入です。
239                final String sql = switch( this ) {
240                        case ORACLE     -> "select " + seqName + ".nextval from dual";
241                        case HSQL       -> "select next value for " + seqName + " from dual";
242                        case POSTGRES -> "select nextval('" + seqName + "')";
243                        case MYSQL      -> {
244                                final String seqget = "update " + seqName + " set SEQID = last_insert_id(SEQID+1)";
245//                              DBUtil.dbExecute( sql, new String[0], tran );
246//                              DBUtil.dbExecute( sql, null, tran, DBID );              // 6.9.8.0 (2018/05/28) DBID 対応漏れ
247                                DBUtil.dbExecute( seqget, null, tran, dbid );   // 6.9.8.0 (2018/05/28) dbid 対応漏れ
248                                yield "select last_insert_id()";
249                        }
250                        case SQLSERVER -> {
251                                throw new OgRuntimeException( "現在、SQLSERVERではシーケンス機能はサポートされていません。" );
252                        }
253                        case FIREBIRD -> "select gen_id(" + seqName + ", 1) from rdb$database";         // 6.4.9.3 (2016/08/26)
254                        case DERBY -> {
255                                throw new OgRuntimeException( "現在、DERBYではシーケンス機能はサポートされていません。" );
256                        }
257                        // 5.8.5.0 (2015/03/06) CACHE追加
258                        case CACHE -> {
259                                throw new OgRuntimeException( "現在、CACHEではシーケンス機能はサポートされていません。" );
260                        }
261                        default -> {
262                                throw new OgRuntimeException( "現在、このデータベースではシーケンス機能はサポートされていません。" );
263                        }
264                };
265
266                // 6.0.2.5 (2014/10/31) refactoring:無効な代入です。
267//              final String[][] rtn = DBUtil.dbExecute( sql, new String[0], tran );
268//              final String[][] rtn = DBUtil.dbExecute( sql, new String[0], tran, DBID );              // 5.9.31.1 (2018/04/13) DBID追加対応
269//              final String[][] rtn = DBUtil.dbExecute( sql, null, tran, DBID );                               // 6.9.8.0 (2018/05/28)
270                final String[][] rtn = DBUtil.dbExecute( sql, null, tran, dbid );                               // 6.9.8.0 (2018/05/28)
271
272                return Integer.parseInt( rtn[0][0] );           // 6.0.2.4 (2014/10/17) メソッド間違い
273        }
274
275        /**
276         * 各データベースに対応するenum名を返します。
277         *
278         * @og.rev 5.1.4.0 (2010/03/01) 新規作成
279         * @og.rev 5.8.5.0 (2015/03/06) CACHE追加
280         * @og.rev 6.3.9.1 (2015/11/27) メソッドの出口は、最後の1か所にすべきです(PMD)。
281         * @og.rev 6.4.5.0 (2016/04/08) 規定以外のデータベースでも、動作するように、OTHER を用意する。
282         * @og.rev 6.4.9.3 (2016/08/26) JavaDB(APACHE DERBY) の追加
283         * @og.rev 6.8.3.0 (2017/11/27) H2 の追加
284         *
285         * @param   dbName データベース名(null不可)
286         *
287         * @return  データベースに対応するenum名
288         * @og.rtnNotNull
289         */
290        public static DBFunctionName getDBName( final String dbName ) {
291                final String dbn = dbName.toUpperCase( Locale.JAPAN );
292
293                final DBFunctionName rtn ;
294                // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName
295//              if(              dbn.contains( "ORACLE"         ) ) { rtn = DBFunctionName.ORACLE;              }
296//              else if( dbn.contains( "HSQL"           ) ) { rtn = DBFunctionName.HSQL;                }
297//              else if( dbn.contains( "POSTGRES"       ) ) { rtn = DBFunctionName.POSTGRES;    }
298//              else if( dbn.contains( "MYSQL"          ) ) { rtn = DBFunctionName.MYSQL;               }
299//              else if( dbn.contains( "SQLSERVER"      ) ) { rtn = DBFunctionName.SQLSERVER;   }
300//              else if( dbn.contains( "FIREBIRD"       ) ) { rtn = DBFunctionName.FIREBIRD;    }
301//              else if( dbn.contains( "DERBY"          ) ) { rtn = DBFunctionName.DERBY;               }       // 6.4.9.3 (2016/08/26)
302//              else if( dbn.contains( "CACHE"          ) ) { rtn = DBFunctionName.CACHE;               }       // 5.8.5.0 (2015/03/06)
303//              else if( dbn.contains( "H2"                     ) ) { rtn = DBFunctionName.H2;                  }       // 5.8.5.0 (2015/03/06)
304                if(              dbn.contains( "ORACLE"         ) ) { rtn = ORACLE;             }
305                else if( dbn.contains( "HSQL"           ) ) { rtn = HSQL;               }
306                else if( dbn.contains( "POSTGRES"       ) ) { rtn = POSTGRES;   }
307                else if( dbn.contains( "MYSQL"          ) ) { rtn = MYSQL;              }
308                else if( dbn.contains( "SQLSERVER"      ) ) { rtn = SQLSERVER;  }
309                else if( dbn.contains( "FIREBIRD"       ) ) { rtn = FIREBIRD;   }
310                else if( dbn.contains( "DERBY"          ) ) { rtn = DERBY;              }       // 6.4.9.3 (2016/08/26)
311                else if( dbn.contains( "CACHE"          ) ) { rtn = CACHE;              }       // 5.8.5.0 (2015/03/06)
312                else if( dbn.contains( "H2"                     ) ) { rtn = H2;                 }       // 5.8.5.0 (2015/03/06)
313                else {
314//                      rtn = DBFunctionName.OTHER;                     // 6.4.5.0 (2016/04/08)
315                        rtn = OTHER;                    // 6.4.5.0 (2016/04/08) // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName
316                        final String errMsg = "初期化時に、指定の dbName キーが存在しません。"
317//                                                      + "[" + dbName + "] キーは、" + Arrays.toString( DBFunctionName.values() ) ;
318                                                        + "[" + dbName + "] キーは、" + Arrays.toString( values() ) ;       // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName
319                        System.out.println( errMsg );
320                }
321
322                return rtn;
323        }
324}