package org.n3r.eql.map; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.Getter; import lombok.Setter; import lombok.SneakyThrows; import lombok.val; import org.n3r.eql.config.EqlConfigDecorator; import org.n3r.eql.param.EqlParamPlaceholder; import org.n3r.eql.param.EqlParamsParser; import org.n3r.eql.param.PlaceholderType; import org.n3r.eql.parser.EqlBlock; import org.n3r.eql.util.*; import org.slf4j.Logger; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; public class EqlRun implements Cloneable { public List<Pair<Integer, Object>> realParams = Lists.<Pair<Integer, Object>>newArrayList(); @Setter List<Object> boundParams; @Getter @Setter Connection connection; @Getter String evalSql; @Setter String evalSqlTemplate; @Setter @Getter EqlDynamic evalEqlDynamic; @Setter @Getter boolean iterateOption; @Setter @Getter String tagSqlId; @Setter @Getter boolean forEvaluate; @Getter Map<Object, Map<String, Object>> cachedProperties = Maps.newHashMap(); private String traceParams; public void addRealParam(int index, Object value) { realParams.add(Pair.of(index, value)); } List<Pair<Integer, Integer>> outParameters = Lists.<Pair<Integer, Integer>>newArrayList(); public void registerOutParameter(int index, int type) { outParameters.add(Pair.of(index, type)); } public void bindParamsForEvaluation(String sqlClassPath) { createEvalSql(-1, sqlClassPath, eqlConfig, tagSqlId, boundParams.toString()); } @SneakyThrows public void bindParams(PreparedStatement ps, String sqlClassPath) { for (Pair<Integer, Object> param : realParams) { ps.setObject(param._1, param._2); } for (Pair<Integer, Integer> out : outParameters) { ((CallableStatement) ps).registerOutParameter(out._1, out._2); } createEvalSql(-1, sqlClassPath, eqlConfig, tagSqlId, boundParams.toString()); } @SneakyThrows public void bindBatchParams(PreparedStatement ps, int index, String sqlClassPath) { for (Pair<Integer, Object> param : realParams) { ps.setObject(param._1, ((Object[]) param._2)[index]); } createEvalSql(index, sqlClassPath, eqlConfig, tagSqlId, batchParamsString(boundParams, index)); } private void createEvalSql(int index, String sqlClassPath, EqlConfigDecorator eqlConfig, String tagSqlId, String msg) { boolean hasBoundParams = boundParams != null && boundParams.size() > 0; if (hasBoundParams) { Logger log = Logs.createLogger(eqlConfig, sqlClassPath, getSqlId(), tagSqlId, "params"); log.debug(msg); } if (hasBoundParams) { Logger evalLog = Logs.createLogger(eqlConfig, sqlClassPath, getSqlId(), tagSqlId, "eval"); /* if (isForEvaluate() || evalLog.isDebugEnabled()) */ this.evalSql = parseEvalSql(index); evalLog.debug(this.evalSql); } else { this.evalSql = evalSqlTemplate; } this.traceParams = msg; } private String batchParamsString(List<Object> boundParams, int index) { ArrayList<Object> bounds = new ArrayList<Object>(); for (Object object : boundParams) { bounds.add(((Object[]) object)[index]); } return bounds.toString(); } public String getEvalSqlTemplate() { return evalSqlTemplate; } private String parseEvalSql(int batchIndex) { val eval = new StringBuilder(); int startPos = 0; int index = -1; int size = boundParams.size(); int evalSqlLength = evalSqlTemplate.length(); val simpleDateFormat = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss"); while (startPos < evalSqlLength) { String placeholder = S.wrap(++index, EqlParamsParser.SUB); int pos = evalSqlTemplate.indexOf(placeholder, startPos); if (pos < 0) break; eval.append(evalSqlTemplate.substring(startPos, pos)); if (index < size) { Object boundParam = boundParams.get(index); if (batchIndex >= 0) { boundParam = ((Object[]) boundParam)[batchIndex]; } val evalBoundParam = createEvalBoundParam(simpleDateFormat, boundParam); eval.append(evalBoundParam); } else { eval.append('?'); } startPos = pos + placeholder.length(); } eval.append(evalSqlTemplate.substring(startPos)); return eval.toString(); } private String createEvalBoundParam(SimpleDateFormat simpleDateFormat, Object boundParam) { if (boundParam == null) return "NULL"; if (boundParam instanceof Boolean) return (Boolean) boundParam ? "1" : "0"; if (boundParam instanceof Number) return boundParam.toString(); if (boundParam instanceof Date) return '\'' + simpleDateFormat.format((Date) boundParam) + '\''; if (boundParam instanceof byte[]) return '\'' + Hex.encode((byte[]) boundParam) + '\''; return '\'' + S.escapeSingleQuotes(boundParam.toString()) + '\''; } @Getter String runSql; @Getter String printSql; @Setter @Getter Object result; @Setter @Getter EqlConfigDecorator eqlConfig; @Setter @Getter EqlBlock eqlBlock; @Setter @Getter int placeholderNum; @Getter EqlParamPlaceholder[] placeHolders; @Setter @Getter PlaceholderType placeHolderType; @Setter @Getter PlaceholderType placeHolderOutType; @Setter @Getter EqlType sqlType; @Setter @Getter boolean lastSelectSql; @Setter @Getter boolean willReturnOnlyOneRow; @Getter Object[] extraBindParams; @Setter @Getter EqlDynamic eqlDynamic; @Getter int outCount; @Setter @Getter Map<String, Object> executionContext; @Setter @Getter Object[] params; @Setter @Getter Object[] dynamics; @Setter @Getter Object paramBean; @Override @SneakyThrows public EqlRun clone() { return (EqlRun) super.clone(); } private Object getDynamicsBean() { return dynamics == null || dynamics.length == 0 ? null : dynamics[0]; } public void setRunSql(String runSql) { this.runSql = runSql; printSql = runSql.replaceAll("\\r?\\n", " "); } public String getSqlId() { if (S.isNotBlank(tagSqlId)) return tagSqlId; return eqlBlock != null ? eqlBlock.getSqlId() : "auto"; } public void setPlaceHolders(EqlParamPlaceholder[] placeHolders) { this.placeHolders = placeHolders; outCount = 0; for (EqlParamPlaceholder placeHolder : placeHolders) if (placeHolder.getInOut() != EqlParamPlaceholder.InOut.IN) ++outCount; } public EqlParamPlaceholder getPlaceHolder(int index) { return index < placeHolders.length ? placeHolders[index] : null; } public void setExtraBindParams(Object... extraBindParams) { this.extraBindParams = extraBindParams; } public Map<String, Object> getMergedParamProperties() { return P.mergeProperties(executionContext, getParamBean()); } public Map<String, Object> getMergedParamPropertiesWith(Object element) { return P.mergeProperties(executionContext, element); } public Object getIterateParams() { return ((Object[]) ((Map) getParamBean()).get("_params"))[0]; } public Map<String, Object> getMergedDynamicsProperties() { return P.mergeProperties(executionContext, getDynamicsBean()); } public void traceResult(Object execRet) { BlackcatUtils.trace(getSqlId(), printSql, traceParams, evalSql, execRet); } }