1 #title = "ora"
  2 #tooltip = "SQL*Plus"
  3 #include "lib\sgc4jsee.jsee"
  4 
  5 /**
  6  * @fileOverview
  7  * SQL*Plusを立ち上げてSQLを実行させます。<br />
  8  * 選択文字列があればそれを、なければドキュメント全体をSQLとして実行します。<br />
  9  * <p>
 10  * select時はテンポラリ領域にファイルを作成して実行結果を書き込み、それを開いて結果を表示します。<br />
 11  * このファイルは開いた後に削除してしまうので、必要があれば別途保存してください。
 12  * </p>
 13  * <p>
 14  * 環境変数"PATH"に、sqlplus.exeを含むOracleのBINフォルダが登録されている必要があります。
 15  * </p>
 16  * <p>
 17  * <strong>SQL*Plusは終了後に自動でコミットします。注意してください。</strong>
 18  * </p>
 19  * <p>
 20  * CSEを参考にしました。<br />
 21  * 下記のサイトを参考にしました。<br />
 22  * <ul>
 23  * <li><a href="http://www.ne.jp/asahi/hishidama/home/tech/oracle/sqlplus.html">SQL*Plusメモ(Hishidama's sqlplus for Oracle9i Memo)</a></li>
 24  * <li><a href="http://www.fsinet.or.jp/~akifumi/oracle.html">Oracle Tips</a></li>
 25  * </p>
 26  * 
 27  * @author gecca from 雪月花 (http://setsugecca.org/)
 28  * @version 1.00 for EmEditor v10.0
 29  */
 30 
 31 (function() {
 32 	// 設定情報
 33 	var ini = JseeUtil.getSnipIniObj("SQL*Plus");
 34 	var user = ini.user;
 35 	var pass = ini.pass;
 36 	var connectingString = ini.connectingString;
 37 	var showSql = (ini.showSql == "true");
 38 	var showSeparator = (ini.showSeparator == "true");
 39 	
 40 	// 準備
 41 	var shell = JseeUtil.shell();
 42 	var fso = JseeUtil.fso();
 43 	var workDirPath = JseeUtil.getTempDirPath() + "EmEditorMacros_SQLPlus_configColumnsLength\\";
 44 	if(!fso.FolderExists(workDirPath)) {
 45 		fso.CreateFolder(workDirPath);
 46 	}
 47 	shell.CurrentDirectory = workDirPath;
 48 	
 49 	// 選択文字列があればそれを、なければドキュメント全体をSQLとして実行
 50 	var text = document.selection.Text ? document.selection.Text : JseeUtil.getAllText();
 51 	// 最初に現れるコメント以外の文字が「select」かどうか
 52 	var isSelect = /^(?!.*--).*(^|\s)select/i.test(text);
 53 	
 54 	if(!isSelect) {
 55 		if(!confirm("SQL*Plusは終了後に自動でコミットします。\nSQLを実行してよろしいですか?")) {
 56 			return;
 57 		}
 58 	}
 59 	
 60 	// 実際に実行するSQL
 61 	var sql = (function() {
 62 		// META
 63 		var confPairs = [
 64 			["LINE", "32767"],
 65 			["LONG", "32767"],
 66 			["ECHO", showSql ? "ON" : "OFF"],
 67 			["TRIMSPOOL", "ON"],
 68 			["COLSEP", "゛~゜"],
 69 			["FEEDBACK", "OFF"],
 70 			["SQLNUMBER", "OFF"],
 71 			["UNDERLINE", "`"],
 72 			["SERVEROUTPUT", "ON"]
 73 		];
 74 		var meta = "";
 75 		for(var i=0;i<confPairs.length;i++) {
 76 			meta += "SET " + confPairs[i].join(" ") + ";\n";
 77 		}
 78 		
 79 		// META (COLUMN NAMES LENGTH)
 80 		var text = document.selection.Text ? document.selection.Text : JseeUtil.getAllText();
 81 		if(isSelect) {
 82 			meta += "SET HEADING OFF\n";
 83 			meta += "SET ECHO OFF\n";
 84 			meta += "SPOOL EmEditorMacros_SQLPlus.sql\n";
 85 			meta += getCommand2configColumnsLength(text);
 86 			meta += "SPOOL OFF\n";
 87 			meta += "@EmEditorMacros_SQLPlus.sql\n";
 88 			meta += "SET HEADING ON\n";
 89 			meta += "SET ECHO " + (showSql ? "ON" : "OFF") + "\n";
 90 		}
 91 		
 92 		// 実行結果の整形時に、ここまでを消去する
 93 		meta += "CLEAR SCREEN;\n";
 94 		
 95 		// BODY
 96 		body = text;
 97 		if(!/;\s*$/.test(body)) {
 98 			body += ";";
 99 		}
100 		// 実行結果の整形時に、ここからを消去する
101 		body += "\nCLEAR SCREEN;\nexit;\n";
102 		
103 		// META + BODY
104 		return meta + body;
105 	})();
106 	
107 	(function() {
108 		// 実際に実行するSQLをファイルにする
109 		var sqlFilePath = workDirPath + "input.sql";
110 		JseeUtil.writeFile(sqlFilePath, sql);
111 		
112 		// SQL*Plusを実行
113 		var std = JseeUtil.exec("sqlplus " + user + "/" + pass + "@" + connectingString + " @\"" + sqlFilePath + "\"");
114 		
115 		// 実行した結果(標準出力)を整形
116 		var result = std.out
117 			// 実行結果部分を抜き出す
118 			.replace(/(CLEAR SCREEN;\r\n)?\f[^\f]*$/, "")
119 			.replace(/^[\s\S]*?\f\r\n/, "")
120 			.replace(/SQL> /g, "")
121 			.replace(/^\r\n/g, "")
122 			
123 			// 区切り文字のみをタブにし、桁合わせ用のホワイトスペースを除去
124 			//.replace(/\t/g, "    ")
125 			.replace(/\t/g, "")
126 			.replace(/゛~゜/g, "\t")
127 			.replace(/(^|\t) +/g, "$1")
128 			.replace(/ +(\t|$)/g, "$1")
129 			
130 			// 最初のカラム名表示だけを残して、後は除去
131 			.replace(/(\r\n)(`.*\r\n)/, "$1\f$2")
132 			.replace(/\r\n.+\r\n`.*\r\n/g, "")
133 			.replace(/\f`/, "`")
134 		;
135 		if(showSeparator) {
136 			result = result.replace(/(^|\r\n)`[`\t]*/g, function($0) { return $0.replace(/`/g, "-"); });
137 		} else {
138 			result = result.replace(/(^|\r\n)`[`\t]*\r\n/g, "$1")
139 		}
140 		
141 		// 整形した結果が空白でなければ、結果をファイルに出力して開く
142 		if(result) {
143 			var resultFilePath = workDirPath + JseeUtil.dateFormat(new Date(), "yyMMdd_HHmmss");
144 			JseeUtil.writeFile(resultFilePath, result);
145 			JseeUtil.openFile(resultFilePath);
146 			
147 		// 整形した結果が空白なら、メッセージを表示するだけ
148 		} else {
149 			if(isSelect) {
150 				alert("0件が取得されました。");
151 			} else {
152 				alert("更新が完了しました。");
153 			}
154 		}
155 		
156 		JseeUtil.fso().DeleteFile(sqlFilePath);
157 		// 整形した結果が空白でなければ、結果を出力したファイルを削除
158 		if(result) {
159 			JseeUtil.fso().DeleteFile(resultFilePath);
160 		}
161 	})();
162 })();
163 
164 /**
165  * @function
166  * 表示するカラムの長さを設定するための命令文を取得します。<br />
167  * from句とjoin句を解析してテーブル名を取得し、そのテーブルのメタ情報を取得し、カラム名の長さとデータ長のうち大きい方を採用して命令文を生成します。
168  * 
169  * @param {String} sql 実際に実行されるSQL
170  * @returns {String} 表示するカラムの長さを設定するための命令文
171  */
172 function getCommand2configColumnsLength(sql) {
173 	var tables = [];
174 	
175 	var fromClauses = sql.match(/\sfrom\s+\S+(\s*,\s*\S)*/gi);
176 	if(fromClauses) {
177 		var fromTables = [];
178 		var fromTables = fromClauses.join(" ").replace(/(^|\s)from\s+/gi, "").split(",");
179 		for(var i=0;i<fromTables.length;i++) {
180 			tables.push(fromTables[i].replace(/^\s*(.*?)\s*$/gi, "$1").replace(/^"(.*)"$/gi, "$1"));
181 		}
182 	}
183 	
184 	var joinClauses = sql.match(/\sjoin\s+\S+/gi);
185 	if(joinClauses) {
186 		for(var i=0;i<joinClauses.length;i++) {
187 			tables.push(joinClauses[i].replace(/\sjoin\s+"?(.+)"?/gi, "$1"));
188 		}
189 	}
190 	
191 	return "select 'col ' || COLUMN_NAME || ' format ' || decode(DATA_TYPE, 'NUMBER', to_char(power(10, max(LEN)) - 1), 'a' || max(LEN) * 2) || ';' from (select COLUMN_NAME, length(COLUMN_NAME) as LEN, DATA_TYPE from USER_TAB_COLUMNS where TABLE_NAME in ('" + tables.join("','") + "') union all select COLUMN_NAME, nvl(DATA_PRECISION, CHAR_COL_DECL_LENGTH) as LEN, DATA_TYPE from USER_TAB_COLUMNS where TABLE_NAME in ('" + tables.join("','") + "')) group by COLUMN_NAME, DATA_TYPE;\n";
192 }
193