franta-hg@16: /** franta-hg@16: * SQL-DK franta-hg@16: * Copyright © 2013 František Kučera (frantovo.cz) franta-hg@16: * franta-hg@16: * This program is free software: you can redistribute it and/or modify franta-hg@16: * it under the terms of the GNU General Public License as published by franta-hg@16: * the Free Software Foundation, either version 3 of the License, or franta-hg@16: * (at your option) any later version. franta-hg@16: * franta-hg@16: * This program is distributed in the hope that it will be useful, franta-hg@16: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@16: * GNU General Public License for more details. franta-hg@16: * franta-hg@16: * You should have received a copy of the GNU General Public License franta-hg@16: * along with this program. If not, see . franta-hg@16: */ franta-hg@1: package info.globalcode.sql.dk; franta-hg@1: franta-hg@49: import static info.globalcode.sql.dk.Functions.notNull; franta-hg@49: import static info.globalcode.sql.dk.Functions.findByName; franta-hg@49: import java.sql.Connection; franta-hg@1: import java.sql.PreparedStatement; franta-hg@34: import java.sql.SQLException; franta-hg@49: import java.util.ArrayList; franta-hg@34: import java.util.List; franta-hg@51: import java.util.logging.Level; franta-hg@51: import java.util.logging.Logger; franta-hg@49: import java.util.regex.Matcher; franta-hg@49: import java.util.regex.Pattern; franta-hg@1: franta-hg@1: /** franta-hg@1: * franta-hg@1: * @author Ing. František Kučera (frantovo.cz) franta-hg@1: */ franta-hg@1: public class SQLCommandNamed extends SQLCommand { franta-hg@1: franta-hg@51: private static final Logger log = Logger.getLogger(SQLCommandNamed.class.getName()); franta-hg@49: private String namePrefix; franta-hg@49: private String nameSuffix; franta-hg@34: private List parameters; franta-hg@49: private List parametersUsed = new ArrayList<>(); franta-hg@49: private StringBuilder updatedQuery; franta-hg@49: private Pattern pattern; franta-hg@34: franta-hg@49: public SQLCommandNamed(String query, List parameters, String namePrefix, String nameSuffix) { franta-hg@37: super(query); franta-hg@49: this.updatedQuery = new StringBuilder(query.length()); franta-hg@34: this.parameters = parameters; franta-hg@49: this.namePrefix = namePrefix; franta-hg@49: this.nameSuffix = nameSuffix; franta-hg@49: } franta-hg@49: franta-hg@49: @Override franta-hg@49: public PreparedStatement prepareStatement(Connection c) throws SQLException { franta-hg@49: buildPattern(); franta-hg@49: placeParametersAndUpdateQuery(); franta-hg@51: logPossiblyMissingParameters(); franta-hg@49: return c.prepareStatement(updatedQuery.toString()); franta-hg@34: } franta-hg@34: franta-hg@1: @Override franta-hg@34: public void parametrize(PreparedStatement ps) throws SQLException { franta-hg@49: int i = 1; franta-hg@49: for (Parameter p : notNull(parametersUsed)) { franta-hg@49: ps.setObject(i++, p.getValue(), p.getType()); franta-hg@49: } franta-hg@49: } franta-hg@49: franta-hg@49: /** franta-hg@49: * Builds a regexp pattern that matches all parameter names (with prefix/suffix) and which has franta-hg@49: * one group: parameter name (without prefix/suffix) franta-hg@49: */ franta-hg@49: private void buildPattern() { franta-hg@49: StringBuilder patternString = new StringBuilder(); franta-hg@49: franta-hg@54: patternString.append(namePrefix); franta-hg@54: patternString.append("(?"); franta-hg@49: for (int i = 0; i < parameters.size(); i++) { franta-hg@51: patternString.append(Pattern.quote(parameters.get(i).getName())); franta-hg@51: if (i < parameters.size() - 1) { franta-hg@49: patternString.append("|"); franta-hg@49: } franta-hg@49: } franta-hg@49: patternString.append(")"); franta-hg@54: patternString.append(nameSuffix); franta-hg@49: franta-hg@49: pattern = Pattern.compile(patternString.toString()); franta-hg@49: } franta-hg@49: franta-hg@49: private void placeParametersAndUpdateQuery() throws SQLException { franta-hg@49: final String originalQuery = getQuery(); franta-hg@49: Matcher m = pattern.matcher(originalQuery); franta-hg@49: franta-hg@49: int lastPosition = 0; franta-hg@49: while (m.find(lastPosition)) { franta-hg@54: String name = m.group("paramName"); franta-hg@49: franta-hg@49: updatedQuery.append(originalQuery.substring(lastPosition, m.start())); franta-hg@49: updatedQuery.append("?"); franta-hg@49: franta-hg@49: parametersUsed.add(findByName(parameters, name)); franta-hg@49: franta-hg@49: lastPosition = m.end(); franta-hg@49: } franta-hg@49: updatedQuery.append(originalQuery.substring(lastPosition, originalQuery.length())); franta-hg@49: franta-hg@49: for (NamedParameter definedParameter : parameters) { franta-hg@49: if (findByName(parametersUsed, definedParameter.getName()) == null) { franta-hg@54: /** franta-hg@54: * User can have predefined set of parameters and use them with different SQL franta-hg@54: * queries that use only subset of these parameters → just warning, not exception. franta-hg@54: */ franta-hg@54: log.log(Level.WARNING, "Parameter „{0}“ is defined but not used in the query: „{1}“", new Object[]{definedParameter.getName(), originalQuery}); franta-hg@49: } franta-hg@49: } franta-hg@1: } franta-hg@34: franta-hg@51: private void logPossiblyMissingParameters() { franta-hg@54: Pattern p = Pattern.compile(namePrefix + "(?.*?)" + nameSuffix); franta-hg@51: Matcher m = p.matcher(updatedQuery); franta-hg@51: int lastPosition = 0; franta-hg@51: while (m.find(lastPosition)) { franta-hg@54: /** franta-hg@54: * We have not parsed and understood the SQL query; the parameter-like looking string franta-hg@54: * could be inside a literal part of the query → just warning, not exception. franta-hg@54: */ franta-hg@54: log.log(Level.WARNING, "Possibly missing parameter „{0}“ in the query: „{1}“", new Object[]{m.group("paramName"), getQuery()}); franta-hg@51: lastPosition = m.end(); franta-hg@51: } franta-hg@51: } franta-hg@51: franta-hg@34: @Override franta-hg@34: public List getParameters() { franta-hg@34: return parameters; franta-hg@34: } franta-hg@1: }