/*******************************************************************************
* Copyright (c) 2014 hangum.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v2.1
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* hangum - initial API and implementation
* billy.goo - add dialog to view detail record
******************************************************************************/
package com.hangum.tadpole.rdb.core.editors.main.composite;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import com.hangum.tadpole.ace.editor.core.texteditor.function.EditorFunctionService;
import com.hangum.tadpole.commons.dialogs.message.dao.RequestResultDAO;
import com.hangum.tadpole.commons.libs.core.dao.LicenseDAO;
import com.hangum.tadpole.commons.libs.core.define.PublicTadpoleDefine;
import com.hangum.tadpole.commons.libs.core.define.PublicTadpoleDefine.SQL_STATEMENT_TYPE;
import com.hangum.tadpole.commons.libs.core.define.PublicTadpoleDefine.SQL_TYPE;
import com.hangum.tadpole.commons.libs.core.message.CommonMessages;
import com.hangum.tadpole.commons.libs.core.utils.LicenseValidator;
import com.hangum.tadpole.engine.define.DBDefine;
import com.hangum.tadpole.engine.define.DBGroupDefine;
import com.hangum.tadpole.engine.manager.TadpoleSQLManager;
import com.hangum.tadpole.engine.manager.TadpoleSQLTransactionManager;
import com.hangum.tadpole.engine.permission.PermissionChecker;
import com.hangum.tadpole.engine.query.dao.system.UserDBDAO;
import com.hangum.tadpole.engine.query.sql.TadpoleSystem_SchemaHistory;
import com.hangum.tadpole.engine.sql.paremeter.lang.GenericTokenParser;
import com.hangum.tadpole.engine.sql.paremeter.lang.JavaNamedParameterUtil;
import com.hangum.tadpole.engine.sql.paremeter.lang.OracleStyleSQLNamedParameterUtil;
import com.hangum.tadpole.engine.sql.util.ObjectCompileUtil;
import com.hangum.tadpole.engine.sql.util.OracleDbmsOutputUtil;
import com.hangum.tadpole.engine.sql.util.PartQueryUtil;
import com.hangum.tadpole.engine.sql.util.QueryUtils;
import com.hangum.tadpole.engine.sql.util.SQLUtil;
import com.hangum.tadpole.engine.sql.util.resultset.QueryExecuteResultDTO;
import com.hangum.tadpole.engine.sql.util.resultset.TadpoleResultSet;
import com.hangum.tadpole.engine.utils.EditorDefine;
import com.hangum.tadpole.engine.utils.RequestQuery;
import com.hangum.tadpole.preference.define.AdminPreferenceDefine;
import com.hangum.tadpole.preference.define.GetAdminPreference;
import com.hangum.tadpole.preference.get.GetPreferenceGeneral;
import com.hangum.tadpole.rdb.core.Activator;
import com.hangum.tadpole.rdb.core.Messages;
import com.hangum.tadpole.rdb.core.editors.main.composite.plandetail.mysql.MySQLExtensionViewDialog;
import com.hangum.tadpole.rdb.core.editors.main.composite.resultdetail.AbstractResultDetailComposite;
import com.hangum.tadpole.rdb.core.editors.main.composite.resultdetail.ResultTableComposite;
import com.hangum.tadpole.rdb.core.editors.main.execute.TransactionManger;
import com.hangum.tadpole.rdb.core.editors.main.execute.sub.ExecuteBatchSQL;
import com.hangum.tadpole.rdb.core.editors.main.execute.sub.ExecuteOtherSQL;
import com.hangum.tadpole.rdb.core.editors.main.execute.sub.ExecuteQueryPlan;
import com.hangum.tadpole.rdb.core.editors.main.parameter.ParameterDialog;
import com.hangum.tadpole.rdb.core.extensionpoint.definition.IMainEditorExtension;
import com.hangum.tadpole.rdb.core.util.GrantCheckerUtils;
import com.hangum.tadpole.rdb.core.util.QueryResultSaved;
import com.hangum.tadpole.rdb.core.viewers.object.ExplorerViewer;
import com.hangum.tadpole.session.manager.SessionManager;
import com.hangum.tadpole.tajo.core.connections.manager.ConnectionPoolManager;
import com.swtdesigner.ResourceManager;
/**
* result set composite
*
* @author hangum
*
*/
public class ResultSetComposite extends Composite {
private static final long serialVersionUID = -3706926974815713584L;
/** Logger for this class. */
private static final Logger logger = Logger.getLogger(ResultSetComposite.class);
/** parameter dialog */
private List<ParameterDialog> listParameterDialog = new ArrayList<>();
/** 명령 완료 메시지 */
private static final String CMD_COMPLETE_MSG = CommonMessages.get().CommandCoompleted;
/** 쿼리를 배치실행했을때 수행 할 수 있는 SQL 수 */
private int BATCH_EXECUTE_SQL_LIMIT = 5;
/**
* 에디터가 select 에디터인지 즉 구분자로 쿼리를 검색하는 상태인지 나타냅니다.
*/
private boolean isSelect = true;
/**
* 현재 사용자의 데이터의 궈한타입.
*/
private String dbUserRoleType = ""; //$NON-NLS-1$
/** execute job */
private Job jobQueryManager = null;
/** result composite */
private ResultMainComposite rdbResultComposite;
private ProgressBar progressBarQuery;
private Button btnStopQuery;
private ScrolledComposite scrolledComposite;
private SashForm sashFormResult;
/** 쿼리 결과 컴포짖 */
private AbstractResultDetailComposite compositeResult;
/**
* Create the composite.
*
* @param parent
* @param style
* @param resultMainComposite
*/
public ResultSetComposite(Composite parent, int style, ResultMainComposite rdbResultComposite) {
super(parent, style);
GridLayout gridLayout = new GridLayout(1, false);
gridLayout.verticalSpacing = 2;
gridLayout.horizontalSpacing = 2;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 2;
setLayout(gridLayout);
this.rdbResultComposite = rdbResultComposite;
Composite compHead = new Composite(this, SWT.NONE);
compHead.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
GridLayout gl_composite = new GridLayout(7, false);
gl_composite.verticalSpacing = 2;
gl_composite.horizontalSpacing = 2;
gl_composite.marginHeight = 0;
gl_composite.marginWidth = 2;
compHead.setLayout(gl_composite);
Label lblProgress = new Label(compHead, SWT.NONE);
lblProgress.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblProgress.setText(Messages.get().ResultSetComposite_3);
progressBarQuery = new ProgressBar(compHead, SWT.NULL);
// progressBarQuery.setBackground(SWTResourceManager.getColor(127,255,0));
progressBarQuery.setSelection(0);
btnStopQuery = new Button(compHead, SWT.NONE);
btnStopQuery.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
isUserInterrupt = false;
}
});
btnStopQuery.setImage(ResourceManager.getPluginImage(Activator.PLUGIN_ID, "resources/icons/editor/stop.png"));
btnStopQuery.setToolTipText(Messages.get().QueryStop);
btnStopQuery.setEnabled(false);
btnAddVertical = new Button(compHead, SWT.NONE);
btnAddVertical.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if(SWT.VERTICAL == sashFormResult.getOrientation()) {
sashFormResult.setOrientation(SWT.HORIZONTAL);
btnAddVertical.setImage(ResourceManager.getPluginImage(Activator.PLUGIN_ID, "resources/icons/editor/layouts_split_vertical.png"));
} else {
sashFormResult.setOrientation(SWT.VERTICAL);
btnAddVertical.setImage(ResourceManager.getPluginImage(Activator.PLUGIN_ID, "resources/icons/editor/layouts_split_horizontal.png"));
}
layout();
}
});
btnAddVertical.setToolTipText(Messages.get().ChangeRotation);
btnAddVertical.setImage(ResourceManager.getPluginImage(Activator.PLUGIN_ID, "resources/icons/editor/layouts_split_horizontal.png"));
Label lblTemp = new Label(compHead, SWT.NONE);
lblTemp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
// scrolled composite
scrolledComposite = new ScrolledComposite(this, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setExpandVertical(true);
sashFormResult = new SashForm(scrolledComposite, SWT.VERTICAL);
sashFormResult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
scrolledComposite.setContent(sashFormResult);
scrolledComposite.setMinSize(sashFormResult.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
/**
* 결과탭을 새로 생성하거나 합니다.
*
* @param reqQuery
* @param listRSDao
* @param longHistorySeq
*/
private void changeResultType(final RequestQuery reqQuery, final List<QueryExecuteResultDTO> listRSDao, List<Long> listLongHistorySeq) {
// 모든 쿼리를 수행 하면 기존 결과 화면을 핀 유무와 상관없이 닫는다.
if(reqQuery.getExecuteType() == EditorDefine.EXECUTE_TYPE.ALL) {
Control[] childControls = sashFormResult.getChildren();
for (int i=0; i<childControls.length; i++) {
if(childControls[i] instanceof AbstractResultDetailComposite) {
AbstractResultDetailComposite resultComposite = (AbstractResultDetailComposite)childControls[i];
resultComposite.dispose();
}
}
}
// 화면 결과를 출력한다.
try {
int index = 0;
for (QueryExecuteResultDTO rsDAO : listRSDao) {
long longHistorySeq = listLongHistorySeq.get(index);
index++;
boolean isMakePing = listRSDao.size()==1?false:true;
RequestQuery reqNewQuery = (RequestQuery)reqQuery.clone();
reqNewQuery.setSql(rsDAO.getReqQuery());
if(compositeResult != null && !compositeResult.getCompositeTail().getBtnPinSelection()) {
compositeResult.printUI(reqNewQuery, rsDAO, isMakePing, longHistorySeq);
} else {
compositeResult = new ResultTableComposite(sashFormResult, SWT.BORDER, this);
compositeResult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2));
compositeResult.printUI(reqNewQuery, rsDAO, isMakePing, longHistorySeq);
}
/** 쿼리 결과를 저장합니다 */
if(PublicTadpoleDefine.YES_NO.YES.name().equals(rsDAO.getUserDB().getIs_result_save())) {
QueryResultSaved.queryResult(GetAdminPreference.getQueryResultSaved() + PublicTadpoleDefine.DIR_SEPARATOR + SessionManager.getUserSeq(), ""+longHistorySeq, rsDAO);
}
}
resultSashLayout();
} catch(CloneNotSupportedException e) {
logger.error("show execute result", e);
}
}
/**
* 화면을 다시 조절한다.
*/
private void resultSashLayout() {
sashFormResult.layout();
scrolledComposite.setMinSize(sashFormResult.computeSize(sashFormResult.getClientArea().width, sashFormResult.getClientArea().height));
scrolledComposite.layout();
}
/**
* get rdb result composite
* @return
*/
public ResultMainComposite getRdbResultComposite() {
return rdbResultComposite;
}
/**
* 파라미터 쿼리인지 검사하여 쿼리를 만듭니다.
* @return
*/
private boolean ifIsParameterQuery(final RequestQuery reqQuery) {
if(reqQuery.getExecuteType() == EditorDefine.EXECUTE_TYPE.ALL) return true;
final DBGroupDefine dbGroup = getUserDB().getDBGroup();
if(DBGroupDefine.HIVE_GROUP == dbGroup
|| DBGroupDefine.TAJO_GROUP == dbGroup
|| DBGroupDefine.CUBRID_GROUP == dbGroup) return true;
final Shell runShell = btnStopQuery.getShell();
// java named parameter (오라클 디비의 경우는 :parameter도 변수 취급합니다.)
try {
JavaNamedParameterUtil javaNamedParameterUtil = new JavaNamedParameterUtil();
int paramCnt = javaNamedParameterUtil.calcParamCount(getUserDB(), reqQuery.getSql());
if(paramCnt > 0) {
ParameterDialog epd = new ParameterDialog(runShell, this, PublicTadpoleDefine.PARAMETER_TYPE.JAVA_BASIC, reqQuery, getUserDB(), reqQuery.getSql(), paramCnt);
epd.open();
listParameterDialog.add(epd);
return false;
}
} catch(Exception e) {
logger.error("Java style parameter parse", e); //$NON-NLS-1$
}
// oracle parameter
try {
OracleStyleSQLNamedParameterUtil oracleNamedParamUtil = new OracleStyleSQLNamedParameterUtil();
String strSQL = oracleNamedParamUtil.parse(SQLUtil.removeComment(reqQuery.getSql()));
Map<Integer, String> mapIndexToName = oracleNamedParamUtil.getMapIndexToName();
if(!mapIndexToName.isEmpty()) {
ParameterDialog epd = new ParameterDialog(runShell, this, PublicTadpoleDefine.PARAMETER_TYPE.ORACLE, reqQuery, getUserDB(), strSQL, mapIndexToName);
epd.open();
listParameterDialog.add(epd);
return false;
}
} catch(Exception e) {
logger.error("Oracle sytle parameter parse", e); //$NON-NLS-1$
}
// mybatis shap
GenericTokenParser mybatisShapeUtil = new GenericTokenParser("#{", "}");
String strSQL = mybatisShapeUtil.parse(reqQuery.getSql());
Map<Integer, String> mapIndexToName = mybatisShapeUtil.getMapIndexToName();
if(!mapIndexToName.isEmpty()) {
ParameterDialog epd = new ParameterDialog(runShell, this, PublicTadpoleDefine.PARAMETER_TYPE.MYBATIS_SHARP, reqQuery, getUserDB(), strSQL, mapIndexToName);
epd.open();
listParameterDialog.add(epd);
return false;
}
if(GetPreferenceGeneral.getIsMyBatisDollor()) {
GenericTokenParser mybatisDollarUtil = new GenericTokenParser("${", "}");
strSQL = mybatisDollarUtil.parse(reqQuery.getSql());
mapIndexToName = mybatisDollarUtil.getMapIndexToName();
if(!mapIndexToName.isEmpty()) {
ParameterDialog epd = new ParameterDialog(runShell, this, PublicTadpoleDefine.PARAMETER_TYPE.MYBATIS_DOLLAR, reqQuery, getUserDB(), strSQL, mapIndexToName);
epd.open();
listParameterDialog.add(epd);
return false;
}
}
return true;
}
/**
* 쿼리를 수행합니다.
*
* @param reqQuery
*/
public boolean executeCommand(final RequestQuery reqQuery) {
// 쿼리를 이미 실행 중이라면 무시합니다
if(jobQueryManager != null) {
if(Job.RUNNING == jobQueryManager.getState()) {
if(logger.isDebugEnabled()) logger.debug("\t\t================= return already running query job "); //$NON-NLS-1$
executeErrorProgress(reqQuery, new Exception(Messages.get().ResultSetComposite_1), Messages.get().ResultSetComposite_1);
return false;
}
}
// agens graph는 preparement 가 없습니다.
if(DBDefine.AGENSGRAPH_DEFAULT != getUserDB().getDBDefine()){
// 파라미터 쿼리이라면 파라미터 쿼리 상태로 만듭니다.
if(!ifIsParameterQuery(reqQuery)) return false;
}
return _executeQuery(reqQuery);
}
/**
* 실제 쿼리를 호출한다.
*
* @param reqQuery
* @return
*/
public boolean _executeQuery(final RequestQuery reqQuery) {
// 쿼리가 실행 가능한 상태인지(디비 락상태인지?, 프러덕디비이고 select가 아닌지?,설정인지?)
try {
if(!GrantCheckerUtils.ifExecuteQuery(getUserDB(), reqQuery)) {
return false;
}
} catch(Exception e) {
executeErrorProgress(reqQuery, e, e.getMessage());
return false;
}
// 프로그래스 상태와 쿼리 상태를 초기화한다.
controlProgress(true);
if(compositeResult != null) compositeResult.initUI();
/** 쿼리 */
final List<String> listStrSQL = new ArrayList<String>();
/** 쿼리 결과 */
final List<QueryExecuteResultDTO> listRSDao = new ArrayList<>();
final int intSelectLimitCnt = GetPreferenceGeneral.getSelectLimitCount();
final String strPlanTBName = GetPreferenceGeneral.getPlanTableName();
final String strUserEmail = SessionManager.getEMAIL();
final int queryTimeOut = GetPreferenceGeneral.getQueryTimeOut();
final int intCommitCount = Integer.parseInt(GetPreferenceGeneral.getRDBCommitCount());
final UserDBDAO tmpUserDB = getUserDB();
final String errMsg = Messages.get().MainEditor_21;
final RequestResultDAO reqResultDAO = new RequestResultDAO();
// is profilling
final boolean isProfilling = GetPreferenceGeneral.getRDBQueryProfilling();
jobQueryManager = new Job(Messages.get().MainEditor_45) {
@Override
public IStatus run(IProgressMonitor monitor) {
monitor.beginTask(reqQuery.getSql(), IProgressMonitor.UNKNOWN);
reqResultDAO.setStartDateExecute(new Timestamp(System.currentTimeMillis()));
reqResultDAO.setIpAddress(reqQuery.getUserIp());
StringBuffer sbParameter = new StringBuffer("/* Execute type is ").append(reqQuery.getMode());
// prepared statement 일 경우는 인자도 넣어준다.
if(reqQuery.getSqlStatementType() == SQL_STATEMENT_TYPE.PREPARED_STATEMENT) {
sbParameter.append(", Parameter is ");
for (int i=0; i<reqQuery.getStatementParameter().length; i++) {
Object objParam = reqQuery.getStatementParameter()[i];
sbParameter.append(String.format("[ %d = %s ]", i, ""+objParam));
}
}
sbParameter.append(" */ \n");
reqResultDAO.setStrSQLText(sbParameter.toString() + reqQuery.getOriginalSql());
try {
if(reqQuery.getExecuteType() == EditorDefine.EXECUTE_TYPE.ALL) {
List<String> listStrExecuteQuery = new ArrayList<String>();
for (String strSQL : reqQuery.getSql().split(PublicTadpoleDefine.SQL_DELIMITER)) {
String strExeSQL = SQLUtil.makeExecutableSQL(tmpUserDB, strSQL);
// execute batch update는 ddl문이 있으면 안되어서 실행할 수 있는 쿼리만 걸러 줍니다.
if(!SQLUtil.isStatement(strExeSQL)) {
listStrExecuteQuery.add(strExeSQL);
} else {
listStrSQL.add(strExeSQL);
}
}
// select 이외의 쿼리 실행
if(!listStrExecuteQuery.isEmpty()) {
ExecuteBatchSQL.runSQLExecuteBatch(errMsg, listStrExecuteQuery, reqQuery, getUserDB(), getDbUserRoleType(), intCommitCount, strUserEmail);
}
if(!listStrSQL.isEmpty()) {
for(int i=0;i<listStrSQL.size(); i++) {
if(i >= BATCH_EXECUTE_SQL_LIMIT) break;
reqQuery.setSql(listStrSQL.get(i));
QueryExecuteResultDTO qeResultDao = runSelect(reqQuery, queryTimeOut, strUserEmail, intSelectLimitCnt, 0);
listRSDao.add(qeResultDao);
}
}
} else {
if(reqQuery.isStatement()) {
if(reqQuery.getMode() == EditorDefine.QUERY_MODE.EXPLAIN_PLAN) {
listRSDao.add(ExecuteQueryPlan.runSQLExplainPlan(getUserDB(), reqQuery, strPlanTBName));
} else {
QueryExecuteResultDTO rsDAO = null;
//
// mysql profile, show status, query plan 모드의 쿼리.
//
// SET, CALL 명령도 프로파일을 해야하는가? - hangum
//
//
if(DBGroupDefine.MYSQL_GROUP == getUserDB().getDBGroup()) {
if(isProfilling) {
QueryUtils.executeQuery(tmpUserDB, "SET PROFILING = 1", 0, 10);
QueryUtils.executeQuery(tmpUserDB, "SET profiling_history_size = 0", 0, 10);
QueryUtils.executeQuery(tmpUserDB, "SET profiling_history_size = 15", 0, 10);
// 사용자 쿼리를 날리고.
QueryExecuteResultDTO startStatus = QueryUtils.executeQuery(tmpUserDB, "SHOW STATUS", 0, 500);
//
rsDAO = runSelect(reqQuery, queryTimeOut, strUserEmail, intSelectLimitCnt, 0);
listRSDao.add(rsDAO);
//
QueryExecuteResultDTO endStatus = QueryUtils.executeQuery(tmpUserDB, "SHOW STATUS", 0, 500);
QueryExecuteResultDTO _tmppShowProfiles = QueryUtils.executeQuery(tmpUserDB, "SHOW PROFILES", 0, 100);
String strQueryID = getLastQueryID(_tmppShowProfiles);
if(logger.isDebugEnabled()) logger.debug("profile query id is : " + strQueryID);
QueryExecuteResultDTO showProfiles = QueryUtils.executeQuery(tmpUserDB,
String.format("SELECT state, ROUND(SUM(duration),5) AS `duration(sec)` FROM information_schema.profiling WHERE query_id=%s GROUP BY state ORDER BY `duration(sec)` DESC", strQueryID), 0, 100);
rsDAO.setMapExtendResult(MySQLExtensionViewDialog.MYSQL_EXTENSION_VIEW.SHOW_PROFILLING.name(), showProfiles);
// diff data
QueryExecuteResultDTO diffStatusDAO = diffStatus(startStatus, endStatus);
rsDAO.setMapExtendResult(MySQLExtensionViewDialog.MYSQL_EXTENSION_VIEW.STATUS_VARIABLE.name(), diffStatusDAO);
// free profiling
QueryUtils.executeQuery(tmpUserDB, "SET PROFILING = 0", 0, 10);
// EXECUTE_PLAN
if(!(StringUtils.startsWithIgnoreCase(StringUtils.trimToEmpty(reqQuery.getSql()), "SET") ||
StringUtils.startsWithIgnoreCase(StringUtils.trimToEmpty(reqQuery.getSql()), "CALL") ||
StringUtils.startsWithIgnoreCase(StringUtils.trimToEmpty(reqQuery.getSql()), "SHOW"))
) {
try {
QueryExecuteResultDTO queryPlanDAO = ExecuteQueryPlan.runSQLExplainPlan(getUserDB(), reqQuery, strPlanTBName);
rsDAO.setMapExtendResult(MySQLExtensionViewDialog.MYSQL_EXTENSION_VIEW.EXECUTE_PLAN.name(), queryPlanDAO);
} catch(Exception e) {
logger.error("MySQL Profiling execute plan", e);
}
}
} else {
rsDAO = runSelect(reqQuery, queryTimeOut, strUserEmail, intSelectLimitCnt, 0);
listRSDao.add(rsDAO);
}
} else {
rsDAO = runSelect(reqQuery, queryTimeOut, strUserEmail, intSelectLimitCnt, 0);
listRSDao.add(rsDAO);
}
// 공통 코드.
if(rsDAO == null || rsDAO.getDataList() == null) {
reqResultDAO.setRows(0);
} else {
reqResultDAO.setRows(rsDAO.getDataList().getData().size());
}
//DBMS_OUTPUT 에서 출력된 메시지가 있으면 쿼리History에 함께 저장하도록 한다.
reqResultDAO.setMesssage(rsDAO.getQueryMsg());
}
} else if(TransactionManger.isTransaction(reqQuery.getSql())) {
if(TransactionManger.isStartTransaction(reqQuery.getSql())) {
startTransactionMode();
reqQuery.setAutoCommit(false);
} else {
TransactionManger.calledCommitOrRollback(reqQuery.getSql(), strUserEmail, getUserDB());
}
} else {
ExecuteOtherSQL.runPermissionSQLExecution(errMsg, reqQuery, getUserDB(), getDbUserRoleType(), strUserEmail);
}
}
} catch(Exception e) {
reqResultDAO.setResult(PublicTadpoleDefine.SUCCESS_FAIL.F.name()); //$NON-NLS-1$
reqResultDAO.setMesssage(e.getMessage());
return new Status(Status.WARNING, Activator.PLUGIN_ID, e.getMessage(), e);
} finally {
reqResultDAO.setEndDateExecute(new Timestamp(System.currentTimeMillis()));
monitor.done();
}
/////////////////////////////////////////////////////////////////////////////////////////
return Status.OK_STATUS;
}
/**
* MYSQL get Last query id
*
* @param query id
* @return
*/
private String getLastQueryID(QueryExecuteResultDTO showProfiles) {
List<Map<Integer, Object>> listShowProfiles = showProfiles.getDataList().getData();
Map<Integer, Object> mapLastData = listShowProfiles.get(listShowProfiles.size()-2);
return ""+mapLastData.get(0);
}
/**
* MYSQL Diffrent show status
*
* @param startStatus
* @param endStatus
* @return
*/
private QueryExecuteResultDTO diffStatus(QueryExecuteResultDTO startStatus, QueryExecuteResultDTO endStatus) {
List<Map<Integer, Object>> trsStart = startStatus.getDataList().getData();
List<Map<Integer, Object>> trsEnd = endStatus.getDataList().getData();
QueryExecuteResultDTO renewDiffObject = new QueryExecuteResultDTO();
// setting column type
Map<Integer, Integer> columnType = new HashMap<>();
columnType.put(0, java.sql.Types.VARCHAR);
columnType.put(1, java.sql.Types.DOUBLE);
renewDiffObject.setColumnType(columnType);;
// setting column label name
Map<Integer, String> columnLabelName = new HashMap<>();
columnLabelName.put(0, "variable");
columnLabelName.put(1, "value");
renewDiffObject.setColumnName(columnLabelName);
TadpoleResultSet dataList = new TadpoleResultSet();
List<Map<Integer, Object>> diffData = new ArrayList<>();
for (int i=0; i<trsStart.size(); i++) {
Map<Integer, Object> mapStartObject = trsStart.get(i);
Map<Integer, Object> mapEndObject = trsEnd.get(i);
// 숫자만 비교 값을 출력해 주기위해..
if(!NumberUtils.isNumber(""+mapStartObject.get(1))) continue;
// 두개의 오브젝트가 틀리면 입력한다.
if(!StringUtils.equals(""+mapStartObject.get(1), ""+mapEndObject.get(1))) {
Map<Integer, Object> mapData = new HashMap<>();
mapData.put(0, mapStartObject.get(0));
double longDiff = NumberUtils.createDouble(""+mapEndObject.get(1)) - NumberUtils.createDouble(""+mapStartObject.get(1));
mapData.put(1, longDiff);
diffData.add(mapData);
}
}
dataList.getData().addAll(diffData);
renewDiffObject.setDataList(dataList);
return renewDiffObject;
}
};
// job의 event를 처리해 줍니다.
jobQueryManager.addJobChangeListener(new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
final IJobChangeEvent jobEvent = event;
getRdbResultComposite().getSite().getShell().
getDisplay().asyncExec(new Runnable() {
public void run() {
// 처리를 위해 결과를 담아 둡니다.
reqQuery.setResultDao(reqResultDAO);
// 히스토리 화면을 갱신합니다.
List<Long> listLongHistorySeq = new ArrayList<>();
if(listStrSQL.isEmpty()) {
listLongHistorySeq.add(getRdbResultComposite().getCompositeQueryHistory().afterQueryInit(reqResultDAO));
} else {
for (String strSQL : listStrSQL) {
reqResultDAO.setStrSQLText(strSQL);
listLongHistorySeq.add(getRdbResultComposite().getCompositeQueryHistory().afterQueryInit(reqResultDAO));
}
}
// 쿼리가 정상일 경우 결과를 테이블에 출력하고, 히스토리를 남기며, 필요하면 오브젝트익스플로에 리프레쉬한다.
if(jobEvent.getResult().isOK()) {
executeFinish(reqQuery, listRSDao, listLongHistorySeq);
} else {
executeErrorProgress(reqQuery, jobEvent.getResult().getException(), jobEvent.getResult().getMessage());
getRdbResultComposite().getMainEditor().browserEvaluateToStr(EditorFunctionService.SET_SELECTED_TEXT); //$NON-NLS-1$
}
// 모든 쿼리가 종료 되었음을 알린다.
finallyEndExecuteCommand(listRSDao);
}
}); // end display.asyncExec
} // end done
}); // end job
jobQueryManager.setPriority(Job.INTERACTIVE);
jobQueryManager.setName(getUserDB().getDisplay_name() + reqQuery.getOriginalSql());
jobQueryManager.schedule();
return true;
}
/**
* 쿼리 중간에 begin 으로 시작하는 구문이 있어서 트랜잭션을 시작합니다.
*/
private void startTransactionMode() {
rdbResultComposite.getMainEditor().beginTransaction();
}
private boolean isCheckRunning = true;
private boolean isUserInterrupt = false;
private ExecutorService execServiceQuery = null;
private ExecutorService esCheckStop = null;
private Button btnAddVertical;
private OracleDbmsOutputUtil dbmsOutput = null;
private String tadpole_system_message = "";
/**
* 실제쿼리를 호출한다.
*
* @param reqQuery
* @param queryTimeOut
* @param strUserEmail
* @param intSelectLimitCnt
* @param intStartCnt
* @param strNullValue
* @return
* @throws Exception
*/
public QueryExecuteResultDTO runSelect(final RequestQuery reqQuery, final int queryTimeOut, final String strUserEmail, final int intSelectLimitCnt, final int intStartCnt) throws Exception {
String strSQL = reqQuery.getSql();
if(!PermissionChecker.isExecute(getDbUserRoleType(), getUserDB(), strSQL)) {
throw new Exception(Messages.get().MainEditor_21);
}
if(logger.isDebugEnabled()) logger.debug("==> real execute query : " + strSQL);
tadpole_system_message = "";
QueryExecuteResultDTO queryResultDAO = null;
// 확장 포인트가 있다면 확장 포인트의 쿼리로 대체합니다.
IMainEditorExtension[] extensions = getRdbResultComposite().getMainEditor().getMainEditorExtions();
if(extensions != null) {
for (IMainEditorExtension iMainEditorExtension : extensions) {
String strCostumSQL = iMainEditorExtension.sqlCostume(strSQL);
if(!strCostumSQL.equals(strSQL)) {
if(logger.isDebugEnabled()) logger.debug("** extension costume sql is : " + strCostumSQL); //$NON-NLS-1$
strSQL = strCostumSQL;
}
}
}
// 확장 포인트가 있다면 확장 포인트의 쿼리로 대체합니다.
ResultSet resultSet = null;
java.sql.Connection javaConn = null;
Statement statement = null;
PreparedStatement preparedStatement = null;
try {
if(DBGroupDefine.TAJO_GROUP == getUserDB().getDBGroup()) {
javaConn = ConnectionPoolManager.getDataSource(getUserDB()).getConnection();
} else {
if(reqQuery.isAutoCommit()) {
javaConn = TadpoleSQLManager.getConnection(getUserDB());
} else {
javaConn = TadpoleSQLTransactionManager.getInstance(strUserEmail, getUserDB());
}
}
// if(logger.isDebugEnabled()) {
// logger.debug("################# ################# ################# ################# ################# ################# ################# ################# ################# ################# ");
// logger.debug("################# schema name is : " + javaConn.getCatalog() );
// logger.debug("################# schema name is : " + javaConn.getSchema() );
// logger.debug("################# ################# ################# ################# ################# ################# ################# ################# ################# ################# ");
// }
// if(javaConn == null) {
// throw new Exception("Cann't create session. Please check system.");
// }
// if statement type is prepared statement?
if(reqQuery.getSqlStatementType() == SQL_STATEMENT_TYPE.NONE) {
statement = javaConn.createStatement();
statement.setFetchSize(intSelectLimitCnt);
if(DBGroupDefine.HIVE_GROUP != getUserDB().getDBGroup()) {
statement.setQueryTimeout(queryTimeOut);
//
// setMaxRows 를 설정하면 SET SQL_SELECT_LIMIT=500 를 호출하게되고 그 후 풀텍스트 검색을하면 mysql 디비가 죽는다.
//
if(DBGroupDefine.MYSQL_GROUP != getUserDB().getDBGroup()) {
statement.setMaxRows(intSelectLimitCnt);
}
}
// check stop thread
esCheckStop = Executors.newSingleThreadExecutor();
CheckStopThread cst = new CheckStopThread(statement);
cst.setName("TDB Query Stop checker"); //$NON-NLS-1$
esCheckStop.execute(cst);
// execute query
execServiceQuery = Executors.newSingleThreadExecutor();
if(intStartCnt == 0) {
resultSet = _runSQLSelect(statement, strSQL);
} else {
strSQL = PartQueryUtil.makeSelect(getUserDB(), strSQL, intStartCnt, intSelectLimitCnt);
if(logger.isDebugEnabled()) logger.debug("part sql called : " + strSQL);
resultSet = _runSQLSelect(statement, strSQL);
}
} else if(reqQuery.getSqlStatementType() == SQL_STATEMENT_TYPE.PREPARED_STATEMENT) {
preparedStatement = javaConn.prepareStatement(strSQL);
preparedStatement.setFetchSize(intSelectLimitCnt);
if(DBGroupDefine.HIVE_GROUP != getUserDB().getDBGroup()) {
preparedStatement.setQueryTimeout(queryTimeOut);
if(DBGroupDefine.MYSQL_GROUP != getUserDB().getDBGroup()) {
preparedStatement.setMaxRows(intSelectLimitCnt);
}
}
// check stop thread
esCheckStop = Executors.newSingleThreadExecutor();
CheckStopThread cst = new CheckStopThread(preparedStatement);
cst.setName("TDB Query Stop checker"); //$NON-NLS-1$
esCheckStop.execute(cst);
// execute query
execServiceQuery = Executors.newSingleThreadExecutor();
// if(intStartCnt == 0) {
// resultSet = _runSQLSelect(preparedStatement, reqQuery.getStatementParameter());
// } else {
strSQL = PartQueryUtil.makeSelect(getUserDB(), strSQL, intStartCnt, intSelectLimitCnt);
if(logger.isDebugEnabled()) logger.debug("part sql called : " + strSQL);
resultSet = _runSQLSelect(preparedStatement, reqQuery.getStatementParameter());
// }
}
queryResultDAO = new QueryExecuteResultDTO(getUserDB(), reqQuery.getSql(), true, resultSet, intSelectLimitCnt, intStartCnt);
if(resultSet == null) {
if(StringUtils.isEmpty(StringUtils.deleteWhitespace(tadpole_system_message))) {
tadpole_system_message = CMD_COMPLETE_MSG;
}
}
queryResultDAO.setQueryMsg(tadpole_system_message);
} catch(Exception e) {
throw e;
} finally {
isCheckRunning = false;
try { if(preparedStatement != null) preparedStatement.close(); } catch(Exception e) {}
try { if(statement != null) statement.close(); } catch(Exception e) {}
try { if(resultSet != null) resultSet.close(); } catch(Exception e) {}
if(reqQuery.isAutoCommit()) {
try { if(javaConn != null) javaConn.close(); } catch(Exception e){}
}
}
return queryResultDAO;
}
/**
* prepared statement 로 실행한다.
*
* @param preparedStatement
* @param statementParameter
* @return
*/
private ResultSet _runSQLSelect(final PreparedStatement preparedStatement, final Object[] statementParameter) throws Exception {
Future<ResultSet> queryFuture = execServiceQuery.submit(new Callable<ResultSet>() {
@Override
public ResultSet call() throws SQLException {
for (int i=1; i<=statementParameter.length; i++) {
preparedStatement.setObject(i, statementParameter[i-1]);
}
return preparedStatement.executeQuery();
}
});
/* SELECT ALRM_DATE 와같은 select다음에 한글 모음이 들어갔을때 아래와 같은 에러가 발생한다.
* Caused by: java.lang.NullPointerException
at oracle.jdbc.driver.T4C8Oall.getNumRows(T4C8Oall.java:973)
*/
return queryFuture.get();
}
/**
* select문을 실행합니다.
*
* @param strSQL
*/
private ResultSet _runSQLSelect(final Statement statement, final String strSQL) throws Exception {
Future<ResultSet> queryFuture = execServiceQuery.submit(new Callable<ResultSet>() {
@Override
public ResultSet call() throws SQLException {
// 오라클인 경우 PL/SQL 실행후 dbms_output 출력 메시지를 결과 메시지에 받아온다.
if(DBGroupDefine.ORACLE_GROUP == getUserDB().getDBGroup()) {
try {
dbmsOutput = new OracleDbmsOutputUtil( statement.getConnection() );
dbmsOutput.enable( 1000000 );
statement.execute(strSQL);
dbmsOutput.show();
tadpole_system_message = dbmsOutput.getOutput();
}finally {
try {if(dbmsOutput!=null)dbmsOutput.close();} catch (SQLException e) {}
}
} else {
statement.execute(strSQL);
}
return statement.getResultSet();
}
});
/* SELECT ALRM_DATE 와같은 select다음에 한글 모음이 들어갔을때 아래와 같은 에러가 발생한다.
* Caused by: java.lang.NullPointerException
at oracle.jdbc.driver.T4C8Oall.getNumRows(T4C8Oall.java:973)
*/
return queryFuture.get();
}
/**
* check stop thread
* @author hangum
*
*/
private class CheckStopThread extends Thread {
private Statement stmt = null;
public CheckStopThread(Statement stmt) {
super("CheckStopThread "); //$NON-NLS-1$
this.stmt = stmt;
}
@Override
public void run() {
int i = 0;
try {
while(isCheckRunning) {
// Is user stop?
if(!isUserInterrupt) {
isCheckRunning = false;
stmt.cancel();
try {
if(logger.isDebugEnabled()) logger.debug("********* User stop operation is [statement close] " + stmt.isClosed()); //$NON-NLS-1$
if(!stmt.isClosed()) execServiceQuery.shutdownNow();
} catch(Exception ee) {
logger.error("Execute stop", ee); //$NON-NLS-1$
}
}
if(isCheckRunning) {
if(i>100) i = 0;
final int progressAdd = i++;
btnStopQuery.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
progressBarQuery.setSelection(progressAdd);
}
});
Thread.sleep(20);
}
} // end while
} catch(Exception e) {
logger.error("isCheckThread exception", e); //$NON-NLS-1$
} finally {
isCheckRunning = false;
}
} // end run
} // end method
/**
* error message 추가한다.
*
* @param requestQuery
* @param throwable
* @param msg
*/
public void executeErrorProgress(RequestQuery requestQuery, Throwable throwable, final String msg) {
getRdbResultComposite().resultFolderSel(EditorDefine.RESULT_TAB.TADPOLE_MESSAGE);
getRdbResultComposite().refreshErrorMessageView(requestQuery, throwable, msg);
}
public UserDBDAO getUserDB() {
return getRdbResultComposite().getUserDB();
}
/**
* 에디터를 실행 후에 마지막으로 실행해 주어야 하는 코드.
*/
private void finallyEndExecuteCommand(final List<QueryExecuteResultDTO> listRsDAO) {
controlProgress(false);
if(compositeResult != null) compositeResult.endQuery();
// 확장포인트에 실행결과를 위임합니다.
IMainEditorExtension[] extensions = getRdbResultComposite().getMainEditor().getMainEditorExtions();
if(extensions == null) return;
for (IMainEditorExtension iMainEditorExtension : extensions) {
try {
if(listRsDAO != null && listRsDAO.size() >= 1) {
iMainEditorExtension.queryEndedExecute(listRsDAO.get(0));
}
} catch(Exception e) {
logger.error("sql result extension", e);
}
}
// 주의) 일반적으로는 포커스가 잘 가지만,
// progress bar가 열렸을 경우 포커스가 잃어 버리게 되어 포커스를 주어야 합니다.
getRdbResultComposite().setOrionTextFocus();
}
/**
* control progress
*
* @param isStart
*/
private void controlProgress(final boolean isStart) {
if(isStart) {
isCheckRunning = true;
isUserInterrupt = true;
progressBarQuery.setSelection(0);
// HIVE는 CANCLE 기능이 없습니다.
if(DBGroupDefine.HIVE_GROUP != getUserDB().getDBGroup()) {
btnStopQuery.setEnabled(true);
}
} else {
isCheckRunning = false;
isUserInterrupt = false;
progressBarQuery.setSelection(100);
btnStopQuery.setEnabled(false);
}
}
public void setDbUserRoleType(String userRoleType) {
dbUserRoleType = userRoleType;
}
public String getDbUserRoleType() {
return dbUserRoleType;
}
/**
* 쿼리 결과를 화면에 출력하고 로그를 쌓습니다.
*
* @param reqQuery
* @param listRSDao
* @param listLongHistorySeq
*/
public void executeFinish(final RequestQuery reqQuery, final List<QueryExecuteResultDTO> listRSDao, List<Long> listLongHistorySeq) {
// 결과에 메시지가 있으면 시스템 메시지에 결과 메시지를 출력한다. 시작.
StringBuffer sbMSG = new StringBuffer();
for (QueryExecuteResultDTO queryExecuteResultDTO : listRSDao) {
sbMSG.append(queryExecuteResultDTO.getQueryMsg()).append(PublicTadpoleDefine.LINE_SEPARATOR);
}
getRdbResultComposite().refreshErrorMessageView(reqQuery, null, sbMSG.toString());
// 결과에 메시지가 있으면 시스템 메시지에 결과 메시지를 출력한다. 종료.
if(reqQuery.isStatement()) {
if(reqQuery.getMode() == EditorDefine.QUERY_MODE.EXPLAIN_PLAN) {
getRdbResultComposite().setQueryPlanView(reqQuery, listRSDao.get(0), listLongHistorySeq.get(0));
getRdbResultComposite().resultFolderSel(EditorDefine.RESULT_TAB.QUERY_PLAN);
} else { // table data를 생성한다.
changeResultType(reqQuery, listRSDao, listLongHistorySeq);
}
} else {
if(reqQuery.getSqlType() == SQL_TYPE.DDL) {
String strDefaultMsg = Messages.get().ResultSetComposite_10 + reqQuery.getResultDao().getStrSQLText();
String retMsg = ObjectCompileUtil.validateObject(getUserDB(), reqQuery.getSqlDDLType(), reqQuery.getSqlObjectName());
if(!"".equals(retMsg)) { //$NON-NLS-1$
strDefaultMsg = Messages.get().ObjectEditor_7 + retMsg;
}
getRdbResultComposite().refreshInfoMessageView(reqQuery, strDefaultMsg);
getRdbResultComposite().resultFolderSel(EditorDefine.RESULT_TAB.TADPOLE_MESSAGE);
// explorer viewer를 리프레쉬하여 최신정보가 반영되게한다.
refreshExplorerView(getUserDB(), reqQuery);
} else {
getRdbResultComposite().refreshInfoMessageView(reqQuery, Messages.get().ResultSetComposite_10 + reqQuery.getResultDao().getStrSQLText());
getRdbResultComposite().resultFolderSel(EditorDefine.RESULT_TAB.TADPOLE_MESSAGE);
}
LicenseDAO licenseDAO = LicenseValidator.getLicense();
if(licenseDAO.isEnterprise()) {
if(reqQuery.getQueryStatus() == PublicTadpoleDefine.QUERY_DDL_STATUS.CREATE ||
reqQuery.getQueryStatus() == PublicTadpoleDefine.QUERY_DDL_STATUS.DROP ||
reqQuery.getQueryStatus() == PublicTadpoleDefine.QUERY_DDL_STATUS.ALTER
) {
// working schema_history 에 history 를 남깁니다.
try {
TadpoleSystem_SchemaHistory.save(SessionManager.getUserSeq(),
getUserDB(),
reqQuery.getQueryStatus().name(), //$NON-NLS-1$
reqQuery.getSqlDDLType().name(),
reqQuery.getSqlObjectName(),
reqQuery.getSql());
} catch(Exception e) {
logger.error("save schemahistory", e); //$NON-NLS-1$
}
}
} // 정식 라이선스만 저장.
}
}
/**
* CREATE, DROP, ALTER 문이 실행되어 ExplorerViewer view를 리프레쉬합니다.
*
* @param userDB
* @param reqQuery
*/
protected void refreshExplorerView(final UserDBDAO userDB, final RequestQuery reqQuery) {
rdbResultComposite.getSite().getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
try {
ExplorerViewer ev = (ExplorerViewer)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(ExplorerViewer.ID);
ev.refreshCurrentTab(userDB, reqQuery);
} catch (PartInitException e) {
logger.error("ExplorerView show", e); //$NON-NLS-1$
}
}
});
}
@Override
public void dispose() {
super.dispose();
for (ParameterDialog parameterDialog : listParameterDialog) {
if(parameterDialog != null) parameterDialog.close();
}
}
/**
* 실행중인 쿼리 job을 가져옵니다.
* @return
*/
public Job getJobQueryManager() {
return jobQueryManager;
}
@Override
protected void checkSubclass() {
}
/**
* 에디터의 쿼리 타입을 설정합니다.
*
* @param isSelect
*/
public void setSelect(boolean isSelect) {
this.isSelect = isSelect;
}
/**
* 에디터의 쿼리 타입을 설정합니다.
*
* @return
*/
public boolean isSelect() {
return isSelect;
}
}