package com.google.sitebricks.persist.sql;

import com.google.common.collect.ImmutableMap;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A simplified API to execute SQL statements against. This is what the
 * EntityStore itself uses to manage the SQL interface. Use this as a low-level,
 * implementation-specific API, as needed.
 *
 * @author dhanji@gmail.com (Dhanji R. Prasanna)
 */
public class Sql {
  static final Pattern NAMED_ARG_PATTERN = Pattern.compile("(@[\\w_]+)",
      Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
  private final Connection connection;

  public Sql(Connection connection) {
    this.connection = connection;
  }

  public void execute(String sql) {
    query(sql);
  }

  public void execute(String sql, Map<String, Object> params) {
    query(sql, params);
  }

  public ResultSet query(String sql) {
    return query(sql, ImmutableMap.<String, Object>of());
  }

  public ResultSet query(String sql, Map<String, Object> params) {
    try {
      Matcher matcher = Sql.NAMED_ARG_PATTERN.matcher(sql);

      Map<Integer, Object> positionalParams = toPositionalMap(params, matcher);

      matcher.reset();
      sql = matcher.replaceAll("?");

      PreparedStatement statement = connection.prepareStatement(sql);

      for (int i = 1; i <= positionalParams.size(); i++) {
        statement.setObject(i, positionalParams.get(i));
      }

      if (statement.execute())
        return statement.getResultSet();
      else {
        statement.close();
        return null;
      }

    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static Map<Integer, Object> toPositionalMap(Map<String, Object> params, Matcher matcher) {
    boolean find = matcher.find();
    Map<Integer, Object> positionalParams = new HashMap<Integer, Object>();
    int index = 1;
    while (find) {
      Object value = params.get(matcher.group().substring(1));
      if (value == null) {
        throw new IllegalArgumentException("Named parameter map for SQL statement did" +
            " not contain required parameter: " + matcher.group());
      }

      positionalParams.put(index, value);
      find = matcher.find(matcher.end());
      index++;
    }
    return positionalParams;
  }

  public boolean tableExists(String name) {
    try {
      return connection.getMetaData().getTables(null, null, name.toUpperCase(), null).next();
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

  public List<Map<String, Object>> list(String sql) {
    return list(sql, ImmutableMap.<String, Object>of());
  }

  public List<Map<String, Object>> list(String sql, Map<String, Object> params) {
    ResultSet resultSet = query(sql, params);

    try {
      ResultSetMetaData metaData = resultSet.getMetaData();
      List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
      int count = metaData.getColumnCount();

      while (resultSet.next()) {
        Map<String, Object> row = new HashMap<String, Object>();
        for (int i = 1; i <= count; i++) {
          String column = metaData.getColumnName(i).toLowerCase();

          row.put(column, resultSet.getObject(column));
        }

        list.add(row);
      }

      return list;
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      try {
        resultSet.close();
      } catch (SQLException e) {
        // This is bad! Log it everywhere possible.
        e.printStackTrace(System.err);
        LoggerFactory.getLogger(SqlPersister.class).error("Unable to release SQL connection", e);
      }
    }
  }

  /**
   * Returns the current underlying JDBC connection.
   */
  public Connection connection() {
    return connection;
  }
}

@@ -2587,24 +2587,223 @@
 %7B%0A    try %7B%0A
+      boolean exists = connection.getMetaData().getTables(null, null, name, null).next();%0A%0A      // Try upper-case object name in case there is some provider-specific weirdness.%0A      if (!exists)%0A  
       return
@@ -2880,24 +2880,58 @@
 ll).next();%0A
+      else%0A        return exists;%0A
     %7D catch 

Make SQL module tableExists tolerant to some case changes