/* * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.susom.database; import java.io.InputStream; import java.io.Reader; import java.util.Arrays; import java.util.Date; import org.slf4j.Logger; import com.github.susom.database.MixedParameterSql.SecretArg; /** * Convenience class to substitute real values into a database query for debugging, logging, etc. * <p/> * WARNING!!! Never execute this SQL without manual inspection because this class does NOTHING * to prevent SQL injection or any other bad things. * * @author garricko */ public class DebugSql { public static final String PARAM_SQL_SEPARATOR = "\tParamSql:\t"; public static String printDebugOnlySqlString(String sql, Object[] args, Options options) { StringBuilder buf = new StringBuilder(); printSql(buf, sql, args, false, true, options); return buf.toString(); } public static void printSql(StringBuilder buf, String sql, Object[] args, Options options) { printSql(buf, sql, args, true, options.isLogParameters(), options); } public static void printSql(StringBuilder buf, String sql, Object[] args, boolean includeExecSql, boolean includeParameters, Options options) { Object[] argsToPrint = args; if (argsToPrint == null) { argsToPrint = new Object[0]; } int batchSize = -1; if (argsToPrint.length > 0 && argsToPrint instanceof Object[][]) { // The arguments provided were from a batch - just use the first set batchSize = argsToPrint.length; argsToPrint = (Object[]) argsToPrint[0]; } String[] sqlParts = sql.split("\\?"); if (sqlParts.length != argsToPrint.length + (sql.endsWith("?") ? 0 : 1)) { buf.append("(wrong # args) query: "); buf.append(sql); if (args != null) { buf.append(" args: "); if (includeParameters) { buf.append(Arrays.toString(argsToPrint)); } else { buf.append(argsToPrint.length); } } } else { if (includeExecSql) { buf.append(removeTabs(sql)); } if (includeParameters && argsToPrint.length > 0) { if (includeExecSql) { buf.append(PARAM_SQL_SEPARATOR); } for (int i = 0; i < argsToPrint.length; i++) { buf.append(removeTabs(sqlParts[i])); Object argToPrint = argsToPrint[i]; if (argToPrint instanceof String) { String argToPrintString = (String) argToPrint; int maxLength = options.maxStringLengthParam(); if (argToPrintString.length() > maxLength && maxLength > 0) { buf.append("'").append(argToPrintString.substring(0, maxLength)).append("...'"); } else { buf.append("'"); buf.append(removeTabs(escapeSingleQuoted(argToPrintString))); buf.append("'"); } } else if (argToPrint instanceof StatementAdaptor.SqlNull || argToPrint == null) { buf.append("null"); } else if (argToPrint instanceof java.sql.Timestamp) { buf.append(options.flavor().dateAsSqlFunction((Date) argToPrint, options.calendarForTimestamps())); } else if (argToPrint instanceof java.sql.Date) { buf.append(options.flavor().localDateAsSqlFunction((Date) argToPrint)); } else if (argToPrint instanceof Number) { buf.append(argToPrint); } else if (argToPrint instanceof Boolean) { buf.append(((Boolean) argToPrint) ? "'Y'" : "'N'"); } else if (argToPrint instanceof SecretArg) { buf.append("<secret>"); } else if (argToPrint instanceof InternalStringReader) { String argToPrintString = ((InternalStringReader) argToPrint).getString(); int maxLength = options.maxStringLengthParam(); if (argToPrintString.length() > maxLength && maxLength > 0) { buf.append("'").append(argToPrintString.substring(0, maxLength)).append("...'"); } else { buf.append("'"); buf.append(removeTabs(escapeSingleQuoted(argToPrintString))); buf.append("'"); } } else if (argToPrint instanceof Reader || argToPrint instanceof InputStream) { buf.append("<").append(argToPrint.getClass().getName()).append(">"); } else if (argToPrint instanceof byte[]) { buf.append("<").append(((byte[]) argToPrint).length).append(" bytes>"); } else { buf.append("<unknown:").append(argToPrint.getClass().getName()).append(">"); } } if (sqlParts.length > argsToPrint.length) { buf.append(sqlParts[sqlParts.length - 1]); } } } if (batchSize != -1) { buf.append(" (first in batch of "); buf.append(batchSize); buf.append(')'); } } private static String removeTabs(String s) { return s == null ? null : s.replace("\t", "<tab>"); } private static String escapeSingleQuoted(String s) { return s == null ? null : s.replace("'", "''"); } public static String exceptionMessage(String sql, Object[] parameters, String errorCode, Options options) { StringBuilder buf = new StringBuilder("Error executing SQL"); if (errorCode != null) { buf.append(" (errorCode=").append(errorCode).append(")"); } if (options.isDetailedExceptions()) { buf.append(": "); DebugSql.printSql(buf, sql, parameters, options); } return buf.toString(); } public static void logSuccess(String sqlType, Logger log, Metric metric, String sql, Object[] args, Options options) { if (log.isDebugEnabled()) { String msg = logMiddle('\t', sqlType, metric, null, sql, args, options); log.debug(msg); } } public static void logWarning(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args, Options options, Throwable t) { if (log.isWarnEnabled()) { String msg = logMiddle(' ', sqlType, metric, errorCode, sql, args, options); log.warn(msg, t); } } public static void logError(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args, Options options, Throwable t) { if (log.isErrorEnabled()) { String msg = logMiddle(' ', sqlType, metric, errorCode, sql, args, options); log.error(msg, t); } } private static String logMiddle(char separator, String sqlType, Metric metric, String errorCode, String sql, Object[] args, Options options) { StringBuilder buf = new StringBuilder(); if (errorCode != null) { buf.append("errorCode=").append(errorCode).append(" "); } buf.append(sqlType).append(": "); metric.printMessage(buf); buf.append(separator); printSql(buf, sql, args, options); return buf.toString(); } }