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.plugin.table; 017 018import java.io.File; 019import java.io.PrintWriter; 020 021import org.opengion.fukurou.system.OgBuilder ; // 6.4.4.1 (2016/03/18) 022import org.opengion.fukurou.db.DBUtil; 023import org.opengion.fukurou.db.Transaction; // 5.5.2.6 (2012/05/25) 024import org.opengion.fukurou.util.ErrorMessage; 025import org.opengion.fukurou.util.FileUtil; 026import org.opengion.fukurou.util.FixLengthData; 027import org.opengion.fukurou.util.StringUtil; 028import org.opengion.hayabusa.common.HybsSystem; 029import org.opengion.hayabusa.common.HybsSystemException; 030import org.opengion.hayabusa.db.AbstractTableFilter; 031import org.opengion.hayabusa.db.DBTableModel; 032 033/** 034 * TableFilter_INDEX は、TableUpda インターフェースを継承した、DBTableModel 処理用の 035 * 実装クラスです。 036 * 037 * ここでは、インデックス一覧の検索結果より、GF07 のインデックスカラム定義テーブルから 038 * 必要な情報を取得し、インデックス作成スクリプトを作成します。 039 * 出力ファイルは、テーブル名+"C.sql" という命名規則で作成します。 040 * 検索では、(SYSTEM_ID,TBLSYU,TABLE_NAME,TABLE_LABEL,INDEX_NAME,NAME_JA,INDTYPE,TABLESPACE_NAME,INITIAL_EXTENT) 041 * の項目を取得する必要があります。 042 * 043 * 6.1.0.0 (2014/12/26) より、NEXT_EXTENT は使用しなくなりました。 044 * 045 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。 046 * 【パラメータ】 047 * { 048 * DIR : {@BASE_DIR}/sql/install/02_INDEX ; 出力ファイルの基準フォルダ(必須) 049 * XML : false ; XML出力を行うかどうか[true/false]を指定します(初期値:false)。 050 * DROP: false ; INDEX構文の前に、DROP構文を出力するかどうか[true/false]を指定します(初期値:false)。 051 * } 052 * 053 * @og.formSample 054 * ●形式: 055 * select SYSTEM_ID,TBLSYU,TABLE_NAME,TABLE_LABEL,INDEX_NAME,NAME_JA,INDTYPE,TABLESPACE_NAME,INITIAL_EXTENT from GF07 056 * ① <og:tableFilter classId="INDEX" keys="DIR,XML" vals="{@BASE_DIR}/sql/install/02_INDEX,false" /> 057 * 058 * ② <og:tableFilter classId="INDEX" > 059 * { 060 * DIR : {@BASE_DIR}/sql/install/02_INDEX ; 061 * XML : false ; 062 * DROP: false ; 063 * } 064 * </og:tableFilter> 065 * 066 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加 067 * 068 * @version 0.9.0 2000/10/17 069 * @author Kazuhiko Hasegawa 070 * @since JDK1.1, 071 */ 072public class TableFilter_INDEX extends AbstractTableFilter { 073 /** このプログラムのVERSION文字列を設定します。 {@value} */ 074 private static final String VERSION = "6.5.0.1 (2016/10/21)" ; 075 076 // 6.1.0.0 (2014/12/26) NEXT_EXTENT は、使いません。 077 private static final String[] DBKEY = {"SYSTEM_ID","TBLSYU","TABLE_NAME","TABLE_LABEL","INDEX_NAME","NAME_JA","INDTYPE", 078 "TABLESPACE_NAME","INITIAL_EXTENT" }; 079 080 // 5.1.1.0 (2009/12/01) データのアクセス用の配列番号のIDを private ⇒ protected にします。 081 /** データのアクセス用の配列番号 {@value} */ 082 protected static final int SYSTEM_ID = 0; 083 /** データのアクセス用の配列番号 {@value} */ 084 protected static final int TBLSYU = 1; 085 /** データのアクセス用の配列番号 {@value} */ 086 protected static final int TABLE_NAME = 2; 087 /** データのアクセス用の配列番号 {@value} */ 088 protected static final int TABLE_LABEL = 3; // GF02 の NAME_JA より JOIN 089 /** データのアクセス用の配列番号 {@value} */ 090 protected static final int INDEX_NAME = 4; 091 /** データのアクセス用の配列番号 {@value} */ 092 protected static final int INDTYPE = 6; 093 /** データのアクセス用の配列番号 {@value} */ 094 protected static final int TABLESPACE_NAME = 7; 095 /** データのアクセス用の配列番号 {@value} */ 096 protected static final int INITIAL_EXTENT = 8; 097 098 // 5.1.1.2 (2009/12/10) 099 private static final String GF07_SEL = "select A.CLM, B.USE_LENGTH" 100 + " from GF07 A left outer join GF05 B" 101 + " on A.SYSTEM_ID = B.SYSTEM_ID" 102 + " and A.TBLSYU = B.TBLSYU" 103 + " and A.TABLE_NAME = B.TABLE_NAME" 104 + " and A.CLM = B.CLM" 105 + " and B.FGJ = '1'" 106 + " where A.SYSTEM_ID=? and A.TBLSYU=? and A.TABLE_NAME=? and A.INDEX_NAME=?" 107 + " and A.FGJ='1'" 108 + " order by A.SEQNO" ; 109 110 // private static final String ENCODE = "Windows-31J" ; 111 private static final String ENCODE = "UTF-8" ; // 4.3.6.6 (2009/05/15) 112 113 private static final String CMNT = "************************************************************************" ; 114 115 private static final int X = FixLengthData.X ; 116 private static final int K = FixLengthData.K ; 117 118 /** 各種定数 */ 119 // 6.0.2.3 (2014/10/10) AbstractTableFilter へ移動 120 121 /** XML形式かどうか */ 122 123 /** 124 * デフォルトコンストラクター 125 * 126 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。 127 */ 128 public TableFilter_INDEX() { 129 super(); 130 initSet( "DIR" , "出力ファイルの基準フォルダ(必須)" ); 131 initSet( "XML" , "XML出力を行うかどうか[true/false]を指定(初期値:false)" ); 132 initSet( "DROP" , "INDEX構文の前に、DROP構文を出力するかどうか(初期値:false)" ); 133 } 134 135 /** 136 * DBTableModel処理を実行します。 137 * 138 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定 139 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。 140 * @og.rev 4.3.7.0 (2009/06/01) XML出力機能追加 141 * @og.rev 5.1.1.0 (2009/12/01) XML_START_TAG に、tableName をセットします。 142 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 143 * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更 144 * @og.rev 5.6.9.2 (2013/10/18) INDEXを作成する前に、削除構文を入れるかどうかを指定。 145 * @og.rev 6.0.2.3 (2014/10/10) EXEC_END_TAG を追加。キャッシュします。 146 * @og.rev 6.0.2.3 (2014/10/10) isDrop=false の場合、まとめDropファイル(AllDropC.xml)を作成します。 147 * @og.rev 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。 148 * 149 * @return 実行結果のテーブルモデル 150 */ 151 public DBTableModel execute() { 152 isXml = StringUtil.nval( getValue( "XML" ), false ); 153 execEndTag = isXml ? CR + EXEC_END_TAG : ";" ; // 6.0.2.3 (2014/10/10) 154 155 final File dir = new File( getValue( "DIR" ) ); 156 if( ! dir.exists() && ! dir.mkdirs() ) { 157 final String errMsg = "所定のフォルダが作成できませんでした。[" + dir + "]" ; 158 // 4.3.4.4 (2009/01/01) 159 throw new HybsSystemException( errMsg ); 160 } 161 162 // 6.0.2.3 (2014/10/10) isDrop=false の場合、まとめDropファイル(AllDropC.xml)を作成します。 163 // 5.6.9.2 (2013/10/18) DROP構文を出力するかどうか 164 final boolean isDrop = StringUtil.nval( getValue( "DROP" ), false ); 165 PrintWriter dropWriter = null; 166 if( !isDrop ) { // isDrop == false の場合に、まとめDropする。 167 dropWriter = FileUtil.getPrintWriter( new File( dir,"AllDrop" + ( isXml ? "C.xml" : "C.sql" ) ),ENCODE ); 168 if( isXml ) { dropWriter.println( XML_START_TAG ); } 169 } 170 171 String[] data = null; 172 String bkTableName = null; 173 PrintWriter writer = null; 174 final Transaction tran = getTransaction(); // 5.5.2.6 (2012/05/25) 175 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) ; // 6.1.0.0 (2014/12/26) refactoring 176 final int[] clmNo = getTableColumnNo( DBKEY ); 177 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 178 final DBTableModel table = getDBTableModel(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 179 final int rowCnt = table.getRowCount(); 180 for( int row=0; row<rowCnt; row++ ) { 181 String tableName = null; 182 String indexName = null; 183 try { 184 data = table.getValues( row ); 185 final String systemId = data[clmNo[SYSTEM_ID]]; 186 final String tblsyu = data[clmNo[TBLSYU]]; 187 tableName = data[clmNo[TABLE_NAME]]; 188 indexName = data[clmNo[INDEX_NAME]]; 189 190 // テーブルがキーブレイクすると、セーブファイルを切り替える。 191 if( ! tableName.equals( bkTableName ) ) { 192 if( writer != null ) { 193 if( isXml ) { writer.println( XML_END_TAG ); } 194 writer.close(); 195 } 196 bkTableName = tableName; 197 writer = FileUtil.getPrintWriter( new File( dir,tableName + ( isXml ? "C.xml" : "C.sql" ) ),ENCODE ); 198 if( isXml ) { writer.println( XML_START_TAG.replace( "xxx",tableName ) ); } // 5.1.1.0 (2009/12/01) tableName をセット 199 writer.print( makeHeadLine( clmNo,data ) ); 200 } 201 202 final String[] vals = new String[] { systemId,tblsyu,tableName,indexName }; 203 final String[][] gf07 = DBUtil.dbExecute( GF07_SEL,vals,tran ); // 5.1.9.0 (2010/08/01) Transaction 対応 204 if( gf07.length == 0 ) { 205 System.out.println( "TABLE=[" + tableName + "],INDEX=[" + indexName + "] is Not Found!" ); 206 continue; 207 } 208 // テーブルに対するカラム列 209 buf.setLength(0); 210 for( int j=0; j<gf07.length; j++ ) { 211 // 5.1.1.2 (2009/12/10) 212 buf.append( makeIndexClmStr( gf07[j][0], gf07[j][1] ) ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 213 } 214 buf.deleteCharAt( buf.length()-1 ); // 最後の "," を取り除く処理 215 216 // 5.6.9.2 (2013/10/18) DROP構文を出力する。 217 if( isDrop ) { 218 writer.print( makeDropLine( clmNo,data ) ); 219 } 220 // 6.0.2.3 (2014/10/10) isDrop=false の場合、まとめDropファイルを作成します。 221 else { 222 dropWriter.print( makeDropLine( clmNo,data ) ); 223 } 224 225 final String clms = buf.toString(); 226 writer.print( makeLineList( clmNo,data,clms ) ); 227 writer.println( makeEndLine( clmNo,data ) ); 228 } 229 catch( final RuntimeException ex ) { 230 // 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。 231 final ErrorMessage errMessage = makeErrorMessage( "TableFilter_INDEX Error",ErrorMessage.NG ) 232 .addMessage( row+1,ErrorMessage.NG,"INDEX" 233 , "TABLE=[" + tableName + "],INDEX=[" + indexName + "]" 234 , StringUtil.array2csv( data ) 235 ) 236 .addMessage( ex ); 237 // BAT から呼び出す場合があるため、標準エラー出力にも情報を出しておきます。 238 System.out.println( errMessage ); 239 } 240 } 241 if( isXml ) { writer.println( XML_END_TAG ); } 242 writer.close(); // 6.0.2.3 (2014/10/10) nullチェック不要。その前でエラーになるはず。 243 if( dropWriter != null ) { // こちらは使わないケースがあるので、nullチェックは必要。 244 if( isXml ) { dropWriter.println( XML_END_TAG ); } 245 dropWriter.close(); 246 } 247 248 return table; 249 } 250 251 /** 252 * ヘッダー部分の処理を実行します。 253 * 254 * @og.rev 5.6.6.0 (2013/07/05) FixLengthData の簡易コンストラクタを使用 255 * 256 * @param clmNo カラム番号配列 257 * @param data 1行分のデータ配列 258 * 259 * @return ヘッダー部分の文字列 260 * @og.rtnNotNull 261 */ 262 protected String makeHeadLine( final int[] clmNo,final String[] data ) { 263 final String tableName = data[clmNo[TABLE_NAME]]; 264 final String LINE1 = tableName + " ( " + data[clmNo[TABLE_LABEL]] + " )" ; 265 final String LINE2 = "Created : " + HybsSystem.getDate() ; 266 267 // 5.6.6.0 (2013/07/05) FixLengthData の簡易コンストラクタを使用 268 final int[] addLen = new int[] { 0,0,0 }; // 各データ間のスペース 269 final int[] type = new int[] { X,K,X }; // 各データの種別 X:半角 S:空白前埋め K:全角混在 270 final FixLengthData fixData = new FixLengthData( addLen,type ); 271 272 final String[][] outData = new String[][] { 273 { "/**", CMNT , "**/" }, 274 { "/* ", LINE1, " */" }, 275 { "/* ", LINE2, " */" }, 276 { "/**", CMNT , "**/" }, 277 }; 278 279 fixData.addAllListData( outData ); 280 281 return fixData.getAllFixData(); 282 } 283 284 /** 285 * インデックス作成の処理を実行します。 286 * 287 * @og.rev 5.3.8.0 (2011/08/01) プライマリキー対応 288 * @og.rev 5.6.9.2 (2013/10/18) INDTYPE で、その他ではなく、2:通常 で判断する。 289 * @og.rev 6.4.0.5 (2016/01/09) INDTYPE が 9:未使用 の場合の処理を追加。 290 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 291 * 292 * @param clmNo カラム番号配列 293 * @param data 1行分のデータ配列 294 * @param clms カラム名(CSV形式) 295 * 296 * @return 作成された1行分の文字列 297 * @og.rtnNotNull 298 */ 299 protected String makeLineList( final int[] clmNo,final String[] data,final String clms ) { 300 // 6.4.0.5 (2016/01/09) INDTYPE が 9:未使用 の場合の処理を追加。 301 final String idxtype = data[clmNo[INDTYPE]]; 302 303 if( "9".equals( idxtype ) ) { return ""; } // 6.4.0.5 (2016/01/09) 304 305 final String tableName = data[clmNo[TABLE_NAME]]; 306 final String indexName = data[clmNo[INDEX_NAME]]; 307 308 return new OgBuilder() 309 .appendCR() // 先頭に、改行を入れておきます。 310 .appendIfCR( isXml , EXEC_START_TAG ) 311 .appendIf( "0".equals( idxtype ) // 0:プライマリキー 312 , "ALTER TABLE " , tableName 313 , " ADD CONSTRAINT " , indexName 314 , " PRIMARY KEY ( " , clms , " )" ) 315 .appendIf( "1".equals( idxtype ) // 1:ユニークキー 316 , "ALTER TABLE " , tableName 317 , " ADD CONSTRAINT " , indexName 318 , " UNIQUE( " , clms , " )" ) 319 .appendIf( "2".equals( idxtype ) // 2:通常 320 , "CREATE INDEX " , indexName 321 , " ON " , tableName 322 , "( " , clms , " )" ) 323 .toString(); 324 } 325 326 /** 327 * 定義の最後の部分の処理を実行します。 328 * 329 * 6.1.0.0 (2014/12/26) より、 330 * 1.TABLESPACE_NAME を指定しない場合は、TABLESPACE 句を出力しません。 331 * 2.INITIAL_EXTENT を 0 で指定した場合は、STORAGE 句を出力しません。 332 * 3.NEXT と PCTINCREASE は、出力しません。 333 * 334 * @og.rev 5.3.9.0 (2011/09/01) プライマリキー対応2 335 * @og.rev 6.0.2.3 (2014/10/10) isXml で、CR + EXEC_END_TAG のキャッシュ(execEndTag)を利用します。 336 * @og.rev 6.1.0.0 (2014/12/26) TABLESPACE_NAME,INITIAL_EXTENT が未設定の場合、設定しません。 337 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 338 * 339 * @param clmNo カラム番号配列 340 * @param data 1行分のデータ配列 341 * 342 * @return 定義の最後の部分 343 * @og.rtnNotNull 344 */ 345 protected String makeEndLine( final int[] clmNo,final String[] data ) { 346 347 // 6.1.0.0 (2014/12/26) TABLESPACE_NAME,INITIAL_EXTENT が未設定の場合、設定しません。 348 final String tblSpcse = data[clmNo[TABLESPACE_NAME]] ; 349 final String initExt = data[clmNo[INITIAL_EXTENT]] ; 350 351 final OgBuilder buf = new OgBuilder() 352 .appendCR(); // 先頭に、改行を入れておきます。 353 354 if( !StringUtil.isNull( tblSpcse ) || !StringUtil.isNull( initExt ) ) { 355 final String idxtype = data[clmNo[INDTYPE]]; 356 buf.appendIf( "0".equals( idxtype ) || "1".equals( idxtype ) // 0:プライマリキー , 1:ユニークキー 357 , "USING INDEX " ) 358 .appendIfCR( !StringUtil.isNull( tblSpcse ) 359 , "TABLESPACE " , tblSpcse ) 360 .appendIfCR( !StringUtil.isNull( initExt ) && initExt.charAt(0) != '0' 361 , "STORAGE( INITIAL " , initExt , "K )" ); 362 } 363 364 return buf.append( execEndTag ).toString(); 365 } 366 367 /** 368 * インデックス削除の構文を、作成します。 369 * 370 * @og.rev 5.6.9.2 (2013/10/18) 新規作成 371 * @og.rev 6.0.2.3 (2014/10/10) isXml で、CR + EXEC_END_TAG のキャッシュ(execEndTag)を利用します。 372 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 373 * 374 * @param clmNo カラム番号配列 375 * @param data 1行分のデータ配列 376 * 377 * @return 作成された1行分の文字列 378 * @og.rtnNotNull 379 */ 380 protected String makeDropLine( final int[] clmNo,final String[] data ) { 381 final String tableName = data[clmNo[TABLE_NAME]]; 382 final String indexName = data[clmNo[INDEX_NAME]]; 383 final String idxtype = data[clmNo[INDTYPE]]; 384 385 return new OgBuilder() 386 .appendCR() // 先頭に、改行を入れておきます。 387 .appendIfCR( isXml , EXEC_START_TAG ) 388 .appendIf( "0".equals( idxtype ) || "1".equals( idxtype ) // 0:プライマリキー、1:ユニークキー 389 , "ALTER TABLE " , tableName 390 , " DROP CONSTRAINT " , indexName ) 391 .appendIf( "2".equals( idxtype ) // 2:通常 392 , "DROP INDEX " , indexName ) 393 .append( execEndTag ) 394 .toString(); 395 } 396 397 /** 398 * インデックスを作成するための文字列を返します。 399 * 通常、カラム名をそのまま返します。 400 * 但し、唯一、MySQLの場合、500バイト以上のカラムについては、TEXTで定義しており、 401 * この場合、インデックス化するバイト数(最大255)を指定する必要があります。 402 * このケースに対応するため、カラム名とバイト数を元に判定し、部分インデックスを 403 * 作成するための文字列を作成します。 404 * 405 * @param clm カラム名 406 * @param useLen カラムのバイト数 407 * 408 * @return インデックスカラムの文字列 409 * @see TableFilter_INDEX_MYSQL 410 */ 411 protected String makeIndexClmStr( final String clm, final String useLen ) { 412 return clm; 413 } 414}