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