Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Licensed to Julian Hyde under one or more
    * contributor license agreements.  See the NOTICE file distributed with
    * this work for additional information regarding copyright ownership.
    * The ASF licenses this file to you 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 net.hydromatic.quidem;
  
  
  import java.io.*;
  import java.sql.*;
  import java.util.*;

Runs a SQL script.
  
  public class Quidem {
    private static final Ordering<String[]> ORDERING =
        Ordering.natural().nullsLast().lexicographical().onResultOf(
            new Function<String[], Iterable<Comparable>>() {
              public Iterable<Comparableapply(String[] input) {
                return Arrays.<Comparable>asList(input);
              }
            });
  
    public static final boolean DEBUG =
      "true".equals(System.getProperties().getProperty("quidem.debug"));
  
    private static final String[] USAGE_LINES = {
      "Usage: quidem argument... inFile outFile",
      "",
      "Arguments:",
      "  --help",
      "           Print usage",
      "  --db name url user password",
      "           Add a database to the connection factory",
      "  --var name value",
      "           Assign a value to a variable",
      "  --factory className",
      "           Define a factory class"
    };

  
Default value for setStackLimit(int).
  
    private static final int DEFAULT_MAX_STACK_LENGTH = 16384;

  
The empty environment. Returns null for all variables.
  
    public static final Function<StringObjectEMPTY_ENV =
        new Function<StringObject>() {
          public Object apply(String name) {
            return null;
          }
        };
  
    private BufferedReader reader;
    private Writer writer;
    private PrintWriter printWriter;
    private final Map<PropertyObjectmap = new HashMap<PropertyObject>();
  
Result set from SQL statement just executed.
  
    private ResultSet resultSet;
  
Whether to sort result set before printing.
  
    private boolean sort;
    private final List<Stringlines = new ArrayList<String>();
    private String pushedLine;
    private final StringBuilder buf = new StringBuilder();
    private Connection connection;
    private boolean execute = true;
    private boolean skip = false;
    private int stackLimit = ;
    private final Function<StringObjectenv;

  
Creates a Quidem interpreter with an empty environment.
  
    public Quidem(BufferedReader readerWriter writer) {
      this(readerwriter);
    }

  
Creates a Quidem interpreter.
  
    public Quidem(BufferedReader readerWriter writer,
        Function<StringObjectenv) {
      this. = reader;
      this. = writer;
      this. = env;
    }
  
   public static void main(String[] args) {
     final PrintWriter pw = new PrintWriter(.);
     try {
       main2(Arrays.asList(args), pw);
       pw.flush();
     } catch (Throwable e) {
       pw.flush();
       e.printStackTrace();
       System.exit(1);
     }
   }
 
   public static void main2(List<StringargsPrintWriter out)
       throws Exception {
     final List<ConnectionFactoryfactories = Lists.newArrayList();
     final Map<StringStringenvMap = Maps.newLinkedHashMap();
     int i;
     for (i = 0; i < args.size();) {
       String arg = args.get(i);
       if (arg.equals("--help")) {
         usage(outnull);
         return;
       }
       if (arg.equals("--db")) {
         if (i + 4 >= args.size()) {
           usage(out"Insufficient arguments for --db");
           return;
         }
         final String name = args.get(i + 1);
         final String url = args.get(i + 2);
         final String user = args.get(i + 3);
         final String password = args.get(i + 4);
         factories.add(new SimpleConnectionFactory(nameurluserpassword));
         i += 5;
         continue;
       }
       if (arg.equals("--var")) {
         if (i + 3 >= args.size()) {
           usage(out"Insufficient arguments for --var");
           return;
         }
         final String name = args.get(i + 1);
         final String value = args.get(i + 2);
         envMap.put(namevalue);
         i += 3;
         continue;
       }
       if (arg.equals("--factory")) {
         if (i + 1 >= args.size()) {
           usage(out"Insufficient arguments for --factory");
         }
         final String className = args.get(i + 1);
         final Class<?> factoryClass;
         try {
           factoryClass = Class.forName(className);
         } catch (ClassNotFoundException e) {
           usage(out"Factory class " + className + " not found");
           return;
         }
         ConnectionFactory factory;
         try {
           factory = (ConnectionFactoryfactoryClass.newInstance();
         } catch (InstantiationException e) {
           usage(out"Error instantiating factory class " + className);
           return;
         } catch (IllegalAccessException e) {
           usage(out"Error instantiating factory class " + className);
           return;
         } catch (ClassCastException e) {
           usage(out"Error instantiating factory class " + className);
           return;
         }
         factories.add(factory);
         i += 2;
         continue;
       }
       break;
     }
     if (i + 2 > args.size()) {
       usage(out"Insufficient arguments: need inFile and outFile");
       return;
     }
     final File inFile = new File(args.get(i));
     final File outFile = new File(args.get(i + 1));
     final Reader reader;
     try {
       reader = new LineNumberReader(new FileReader(inFile));
     } catch (FileNotFoundException e) {
       throw new RuntimeException("Error opening input " + inFilee);
     }
     final Writer writer;
     try {
       writer = new FileWriter(outFile);
     } catch (IOException e) {
       throw new RuntimeException("Error opening output " + outFilee);
     }
     factories.add(new UnsupportedConnectionFactory());
 
     final Function<StringObjectenv = new Function<StringObject>() {
       public Object apply(String input) {
         return envMap.get(input);
       }
     };
     final Quidem quidem = new Quidem(new BufferedReader(reader), writerenv);
 
     quidem.execute(new ChainingConnectionFactory(factories));
     reader.close();
     writer.close();
   }
 
   private static void usage(PrintWriter outString error) {
     if (error != null) {
       out.println(error);
       out.println();
     }
     for (String line : ) {
       out.println(line);
     }
   }
 
   private void close() throws SQLException {
     if ( != null) {
       Connection c = ;
        = null;
       c.close();
     }
   }
 
   public void execute(ConnectionFactory connectionFactory) {
     this. = connectionFactory;
     this. = new PrintWriter();
     try {
       Command command = new Parser().parse();
       try {
         command.execute();
         close();
       } catch (Exception e) {
         throw new RuntimeException(
             "Error while executing command " + commande);
       } catch (AssertionError e) {
         throw new RuntimeException(
             "Error while executing command " + commande);
       }
     } finally {
       .flush();
       try {
         close();
       } catch (SQLException e) {
         // ignore
       }
     }
   }
 
   Command of(List<Commandcommands) {
     return commands.size() == 1
         ? commands.get(0)
         : new CompositeCommand(ImmutableList.copyOf(commands));
   }
 
   private static String pad(String sint widthboolean right) {
     if (s == null) {
       s = "";
     }
     final int x = width - s.length();
     if (x <= 0) {
       return s;
     }
     final StringBuilder buf = new StringBuilder();
     if (right) {
       buf.append(chars(' 'x)).append(s);
     } else {
       buf.append(s).append(chars(' 'x));
     }
     return buf.toString();
   }
 
   private static CharSequence chars(final char cfinal int length) {
     return new CharSequence() {
       @Override public String toString() {
         final char[] chars = new char[length];
         Arrays.fill(charsc);
         return new String(chars);
       }
 
       public int length() {
         return length;
       }
 
       public char charAt(int index) {
         return c;
       }
 
       public CharSequence subSequence(int startint end) {
         return Quidem.chars(cend - start);
       }
     };
   }

  
Sets the maximum number of characters of an error stack to be printed.

If negative, does not limit the stack size.

The default is DEFAULT_MAX_STACK_LENGTH.

Useful because it prevents diff from running out of memory if the error stack is very large. It is preferable to produce a result where you can see the first N characters of each stack trace than to produce no result at all.

Parameters:
stackLimit Maximum number of characters to print of each stack trace
 
   public void setStackLimit(int stackLimit) {
     this. = stackLimit;
   }
 
   private boolean getBoolean(String variableName) {
     if (variableName.equals("true")) {
       return true;
     }
     if (variableName.equals("false")) {
       return false;
     }
     final Object value = .apply(variableName);
     return value instanceof Boolean && (Booleanvalue
         || value != null && value.toString().equalsIgnoreCase("true");
   }

  
Returns whether a SQL query is likely to produce results always in the same order.

If Quidem believes that the order is deterministic, it does not sort the rows before comparing them.

The result is just a guess. Quidem does not understand the finer points of SQL semantics.

Parameters:
sql SQL query
Returns:
Whether the order is likely to be deterministic
 
   public boolean isProbablyDeterministic(String sql) {
     final String upperSql = sql.toUpperCase();
     if (!upperSql.contains("ORDER BY")) {
       return false;
     }
     final int i = upperSql.lastIndexOf("ORDER BY");
     final String tail = upperSql.substring(i);
     final int closeCount = tail.length() - tail.replace(")""").length();
     final int openCount = tail.length() - tail.replace("(""").length();
     if (closeCount > openCount) {
       // The last ORDER BY occurs within parentheses. It is either in a
       // sub-query or in a windowed aggregate. Neither of these make the
       // ordering deterministic.
       return false;
     }
     return true;
   }

  
Connection factory that recognizes a single name.
 
   private static class SimpleConnectionFactory implements ConnectionFactory {
     private final String name;
     private final String url;
     private final String user;
     private final String password;
 
     public SimpleConnectionFactory(String nameString urlString user,
         String password) {
       this. = name;
       this. = url;
       this. = user;
       this. = password;
     }
 
     @Override public Connection connect(String namethrows Exception {
       if (name.equals(this.)) {
         return DriverManager.getConnection();
       }
       return null;
     }
   }

  
Connection factory that says all databases are unknown.
 
   private static class UnsupportedConnectionFactory
       implements ConnectionFactory {
     public Connection connect(String name) {
       throw new RuntimeException("Unknown database: " + name);
     }
   }

  
Connection factory that tries several factories, returning a connection from the first that is able to connect.
 
   private static class ChainingConnectionFactory implements ConnectionFactory {
     private final List<ConnectionFactoryfactories;
 
     public ChainingConnectionFactory(List<ConnectionFactoryfactories) {
       this. = ImmutableList.copyOf(factories);
     }
 
     @Override public Connection connect(String namethrows Exception {
       for (ConnectionFactory factory : ) {
         Connection c = factory.connect(name);
         if (c != null) {
           return c;
         }
       }
       return null;
     }
   }

  
Parser.
 
   private class Parser {
     final List<Commandcommands = new ArrayList<Command>();
 
     Command parse() {
       for (;;) {
         Command command;
         try {
           command = nextCommand();
         } catch (IOException e) {
           throw new RuntimeException("Error while reading next command"e);
         }
         if (command == null) {
           break;
         }
         .add(command);
       }
       return of();
     }
 
     private Command nextCommand() throws IOException {
       .clear();
       ImmutableList<Stringcontent = ImmutableList.of();
       for (;;) {
         String line = nextLine();
         if (line == null) {
           return null;
         }
         if (line.startsWith("#") || line.isEmpty()) {
           return new CommentCommand();
         }
         if (line.startsWith("!")) {
           line = line.substring(1);
           while (line.startsWith(" ")) {
             line = line.substring(1);
           }
           if (line.startsWith("use")) {
             String[] parts = line.split(" ");
             return new UseCommand(parts[1]);
           }
           if (line.startsWith("ok")) {
             SqlCommand command = previousSqlCommand();
             return new CheckResultCommand(commandcontent);
           }
           if (line.startsWith("plan")) {
             SqlCommand command = previousSqlCommand();
             return new ExplainCommand(commandcontent);
           }
           if (line.startsWith("error")) {
             SqlCommand command = previousSqlCommand();
             return new ErrorCommand(commandcontent);
           }
           if (line.startsWith("skip")) {
             return new SkipCommand();
           }
           if (line.startsWith("set outputformat")) {
             String[] parts = line.split(" ");
             final OutputFormat outputFormat =
                 OutputFormat.valueOf(parts[2].toUpperCase());
             return new SetCommand(.outputFormat);
           }
           if (line.matches("if \\([A-Za-z-][A-Za-z_0-9]*\\) \\{")) {
             List<StringifLines = ImmutableList.copyOf();
             .clear();
             Command command = new Parser().parse();
             String variable =
                 line.substring("if (" .length(),
                     line.length() - ") {" .length());
             return new IfCommand(ifLinescommandvariable);
           }
           if (line.equals("}")) {
             return null;
           }
           throw new RuntimeException("Unknown command: " + line);
         }
         .setLength(0);
         boolean last = false;
         for (;;) {
           if (line.endsWith(";")) {
             last = true;
             line = line.substring(0, line.length() - 1);
           }
           .append(line).append("\n");
           if (last) {
             break;
           }
           line = nextLine();
           if (line == null) {
             throw new RuntimeException(
                 "end of file reached before end of SQL command");
           }
           if (line.startsWith("!") || line.startsWith("#")) {
             pushLine();
             break;
           }
         }
         content = ImmutableList.copyOf();
         .clear();
         if (last) {
           String sql = .toString();
           return new SqlCommand(contentsqlnull);
         }
       }
     }
 
     private SqlCommand previousSqlCommand() {
       for (int i = .size() - 1; i >= 0; i--) {
         Command command = .get(i);
         if (command instanceof SqlCommand) {
           return (SqlCommandcommand;
         }
       }
       throw new AssertionError("no previous SQL command");
     }
 
     private void pushLine() {
       if ( != null) {
         throw new AssertionError("cannot push two lines");
       }
       if (.size() == 0) {
         throw new AssertionError("no line has been read");
       }
        = .get(.size() - 1);
       .remove(.size() - 1);
     }
 
     private String nextLine() throws IOException {
       String line;
       if ( != null) {
         line = ;
          = null;
       } else {
         line = .readLine();
         if (line == null) {
           return null;
         }
       }
       .add(line);
       return line;
     }
   }

  
Schemes for converting the output of a SQL statement into text.
 
   enum OutputFormat {
     CSV {
       @Override public void format(ResultSet resultSet,
           List<StringheaderLinesList<StringbodyLines,
           List<StringfooterLinesQuidem runthrows Exception {
         final ResultSetMetaData metaData = resultSet.getMetaData();
         final int n = metaData.getColumnCount();
         final StringBuilder buf = new StringBuilder();
         for (int i = 0; i < ni++) {
           if (i > 0) {
             buf.append(", ");
           }
           buf.append(metaData.getColumnLabel(i + 1));
         }
         headerLines.add(buf.toString());
         buf.setLength(0);
         final List<Stringlines = Lists.newArrayList();
         while (resultSet.next()) {
           for (int i = 0; i < ni++) {
             if (i > 0) {
               buf.append(", ");
             }
             buf.append(resultSet.getString(i + 1));
           }
           lines.add(buf.toString());
           buf.setLength(0);
         }
         if (run.sort) {
           Collections.sort(lines);
         }
         bodyLines.addAll(lines);
       }
     },
 
     // Example:
     //
     //  ename | deptno | gender | first_value
     // -------+--------+--------+-------------
     //  Jane  |     10 | F      | Jane
     //  Bob   |     10 | M      | Jane
     // (2 rows)
     PSQL {
       @Override public void format(ResultSet resultSet,
           List<StringheaderLinesList<StringbodyLines,
           List<StringfooterLinesQuidem runthrows Exception {
         Quidem.format(
             resultSetheaderLinesbodyLinesfooterLinesrun.sortfalse);
       }
     },
 
     // Example:
     //
     // +-------+--------+--------+-------------+
     // | ename | deptno | gender | first_value |
     // +-------+--------+--------+-------------+
     // | Jane  |     10 | F      | Jane        |
     // | Bob   |     10 | M      | Jane        |
     // +-------+--------+--------+-------------+
     // (2 rows)
     MYSQL {
       @Override public void format(ResultSet resultSet,
           List<StringheaderLinesList<StringbodyLines,
           List<StringfooterLinesQuidem runthrows Exception {
         Quidem.format(
             resultSetheaderLinesbodyLinesfooterLinesrun.sorttrue);
       }
     };
 
     public abstract void format(ResultSet resultSetList<StringheaderLines,
         List<StringbodyLinesList<StringfooterLinesQuidem run)
         throws Exception;
   }
 
   private static void format(ResultSet resultSetList<StringheaderLines,
       List<StringbodyLinesList<StringfooterLinesboolean sort,
       boolean mysqlthrows SQLException {
     final ResultSetMetaData metaData = resultSet.getMetaData();
     final int n = metaData.getColumnCount();
     final int[] widths = new int[n];
     final List<String[]> rows = new ArrayList<String[]>();
     final boolean[] rights = new boolean[n];
 
     for (int i = 0; i < ni++) {
       widths[i] = metaData.getColumnLabel(i + 1).length();
     }
     while (resultSet.next()) {
       String[] row = new String[n];
       for (int i = 0; i < ni++) {
         String value = resultSet.getString(i + 1);
         widths[i] = Math.max(widths[i], value == null ? 0 : value.length());
         row[i] = value;
       }
       rows.add(row);
     }
     for (int i = 0; i < widths.lengthi++) {
       switch (metaData.getColumnType(i + 1)) {
       case .:
       case .:
       case .:
       case .:
       case .:
       case .:
       case .:
       case .:
       case .:
         rights[i] = true;
       }
     }
 
     if (sort) {
       Collections.sort(rows);
     }
 
     // Compute "+-----+---+" (if b)
     // or       "-----+---" (if not b)
     final StringBuilder buf = new StringBuilder();
     for (int i = 0; i < ni++) {
       buf.append(mysql || i > 0 ? "+" : "");
       buf.append(chars('-'widths[i] + 2));
     }
     buf.append(mysql ? "+" : "");
     String hyphens = flush(buf);
 
     if (mysql) {
       headerLines.add(hyphens);
     }
 
     // Print "| FOO | B |"
     // or    "  FOO | B"
     for (int i = 0; i < ni++) {
       buf.append(i > 0 ? " | " : mysql ? "| " : " ");
       final String label = metaData.getColumnLabel(i + 1);
       buf.append(mysql || i < n - 1 ? pad(labelwidths[i], false) : label);
     }
     buf.append(mysql ? " |" : "");
     headerLines.add(flush(buf));
     headerLines.add(hyphens);
     for (String[] row : rows) {
       for (int i = 0; i < ni++) {
         buf.append(i > 0 ? " | " : mysql ? "| " : " ");
         // don't pad the last field if it is left-justified
         final String s = !mysql && i == n - 1 && !rights[i]
             ? row[i]
             : pad(row[i], widths[i], rights[i]);
         buf.append(s);
       }
       buf.append(mysql ? " |" : "");
       bodyLines.add(flush(buf));
     }
     if (mysql) {
       footerLines.add(hyphens);
     }
     footerLines.add(
         rows.size() == 1 ? "(1 row)" : "(" + rows.size() + " rows)");
     footerLines.add("");
   }

  
Returns the contents of a StringBuilder and clears it for the next use.
 
   private static String flush(StringBuilder buf) {
     final String s = buf.toString();
     buf.setLength(0);
     return s;
   }
 
   private String stack(Throwable e) {
     final StringWriter buf = new StringWriter();
     stack(ebuf);
     return buf.toString();
   }
 
   private void stack(Throwable eWriter w) {
     if ( >= 0) {
       w = new LimitWriter(w);
     }
     final PrintWriter pw;
     if (w instanceof PrintWriter) {
       pw = (PrintWriterw;
     } else {
       pw = new PrintWriter(w);
     }
     e.printStackTrace(pw);
     if ( >= 0) {
       ((LimitWriterw).ellipsis(" (stack truncated)\n");
     }
   }

  
Command.
 
   interface Command {
     void execute(boolean executethrows Exception;
   }

  
Base class for implementations of Command.
 
   abstract class AbstractCommand implements Command {
     protected Command echo(Iterable<Stringlines) {
       for (String line : lines) {
         try {
           .println(line);
         } catch (Exception e) {
           throw new RuntimeException("Error while writing output"e);
         }
       }
       return this;
     }
   }

  
Base class for implementations of Command that have one piece of source code.
 
   abstract class SimpleCommand extends AbstractCommand {
     protected final ImmutableList<Stringlines;
 
     public SimpleCommand(List<Stringlines) {
       this. = ImmutableList.copyOf(lines);
     }
   }

  
Command that sets the current connection.
 
   class UseCommand extends SimpleCommand {
     private final String name;
 
     public UseCommand(List<StringlinesString name) {
       super(lines);
       this. = name;
     }
 
     public void execute(boolean executethrows Exception {
       echo();
       if ( != null) {
         .close();
       }
     }
   }

  
Command that executes a SQL statement and checks its result.
 
   class CheckResultCommand extends SimpleCommand {
     private final SqlCommand sqlCommand;
     protected final ImmutableList<Stringoutput;
 
     public CheckResultCommand(List<StringlinesSqlCommand sqlCommand,
         ImmutableList<Stringoutput) {
       super(lines);
       this. = sqlCommand;
       this. = output;
     }
 
     @Override public String toString() {
       return "CheckResultCommand [sql: " + . + "]";
     }
 
     public void execute(boolean executethrows Exception {
       if (execute) {
         if ( == null) {
           throw new RuntimeException("no connection");
         }
         final Statement statement = .createStatement();
         if ( != null) {
           .close();
         }
         try {
           if () {
             ..println("execute: " + this);
           }
            = null;
            = null;
            = statement.executeQuery(.);
           final String sql = .;
            = !isProbablyDeterministic(sql);
         } catch (SQLException e) {
            = e;
         }
         if ( != null) {
           final OutputFormat format =
               (OutputFormat.get(.);
           final List<StringheaderLines = new ArrayList<String>();
           final List<StringbodyLines = new ArrayList<String>();
           final List<StringfooterLines = new ArrayList<String>();
           format.format(headerLinesbodyLinesfooterLines,
               Quidem.this);
 
           // Construct the original body.
           // Strip the header and footer from the actual output.
           // We assume that original and actual header have the same line count.
           // Ditto footers.
           final List<Stringlines = new ArrayList<String>();
           for (String line : headerLines) {
             if (!lines.isEmpty()) {
               lines.remove(0);
             }
           }
           for (String line : footerLines) {
             if (!lines.isEmpty()) {
               lines.remove(lines.size() - 1);
             }
           }
 
           // Print the actual header.
           for (String line : headerLines) {
             .println(line);
           }
           // Print all lines that occurred in the actual output ("bodyLines"),
           // but in their original order ("lines").
           for (String line : lines) {
             if () {
               if (bodyLines.remove(line)) {
                 .println(line);
               }
             } else {
               if (!bodyLines.isEmpty()
                   && bodyLines.get(0).equals(line)) {
                 bodyLines.remove(0);
                 .println(line);
               }
             }
           }
           // Print lines that occurred in the actual output but not original.
           for (String line : bodyLines) {
             .println(line);
           }
           // Print the actual footer.
           for (String line : footerLines) {
             .println(line);
           }
           .close();
         }
 
 
         if ( == null &&  == null) {
           throw new AssertionError("neither resultSet nor exception set");
         }
          = null;
          = null;
       } else {
         echo();
       }
       echo();
     }
 
     protected void checkResultSet(SQLException resultSetException) {
       if (resultSetException != null) {
         stack(resultSetException);
       }
     }
   }

  
Command that executes a SQL statement and checks that it throws a given error.
 
   class ErrorCommand extends CheckResultCommand {
     public ErrorCommand(List<StringlinesSqlCommand sqlCommand,
         ImmutableList<Stringoutput) {
       super(linessqlCommandoutput);
     }
 
     @Override protected void checkResultSet(SQLException resultSetException) {
       if (resultSetException == null) {
         .println("Expected error, but SQL command did not give one");
         return;
       }
       if (!.isEmpty()) {
         final String actual = squash(stack(resultSetException));
         final String expected = squash(concat());
         if (actual.contains(expected)) {
           // They gave an expected error, and the actual error does not match.
           // Print the actual error. This will cause a diff.
           for (String line : ) {
             .println(line);
           }
           return;
         }
       }
       super.checkResultSet(resultSetException);
     }
 
     private String squash(String s) {
       return s.replace("\r\n""\n"// convert line endings to linux
           .replaceAll("[ \t]+"" "// convert tabs & multiple spaces to spaces
           .replaceAll("\n ""\n"// remove spaces at start of lines
           .replaceAll("^ """// or at start of string
           .replaceAll(" \n""\n"// remove spaces at end of lines
           .replaceAll(" $""\n"); // or at end of string
     }
 
     private String concat(List<Stringlines) {
       final StringBuilder buf = new StringBuilder();
       for (String line : lines) {
         buf.append(line).append("\n");
       }
       return buf.toString();
     }
   }

  
Command that prints the plan for the current query.
 
   class ExplainCommand extends SimpleCommand {
     private final SqlCommand sqlCommand;
     private final ImmutableList<Stringcontent;
 
     public ExplainCommand(List<Stringlines,
         SqlCommand sqlCommand,
         ImmutableList<Stringcontent) {
       super(lines);
       this. = sqlCommand;
       this. = content;
     }
 
     @Override public String toString() {
       return "ExplainCommand [sql: " + . + "]";
     }
 
     public void execute(boolean executethrows Exception {
       if (execute) {
         final Statement statement = .createStatement();
         final ResultSet resultSet =
             statement.executeQuery("explain plan for " + .);
         final StringBuffer buf = new StringBuffer();
         while (resultSet.next()) {
           final String line = resultSet.getString(1);
           buf.append(line);
           if (!line.endsWith("\n")) {
             buf.append("\n");
           }
         }
         if (buf.length() == 0) {
           throw new AssertionError("explain returned 0 records");
         }
         .print(buf);
         .flush();
       } else {
         echo();
       }
       echo();
     }
   }

  
Command that executes a SQL statement.
 
   private class SqlCommand extends SimpleCommand {
     private final String sql;
 
     protected SqlCommand(List<StringlinesString sqlList<Stringoutput) {
       super(lines);
       this. = Preconditions.checkNotNull(sql);
     }
 
     public void execute(boolean executethrows Exception {
       echo();
     }
   }

  
Creates a connection for a given name. Kind of a directory service. Caller must close the connection.
  public interface ConnectionFactory {
    Connection connect(String namethrows Exception;
  }

  
Property whose value may be set.
  enum Property {
    OUTPUTFORMAT
  }

  
Command that defines the current SQL statement.

  class SetCommand extends SimpleCommand {
    private final Property property;
    private final Object value;
    public SetCommand(List<StringlinesProperty propertyObject value) {
      super(lines);
      this. = property;
      this. = value;
    }
    public void execute(boolean executethrows Exception {
      echo();
      .put();
    }
  }

  
Command that executes a comment. (Does nothing.)
  class CommentCommand extends SimpleCommand {
    public CommentCommand(List<Stringlines) {
      super(lines);
    }
    public void execute(boolean executethrows Exception {
      echo();
    }
  }

  
Command that disables execution of a block.
  class IfCommand extends AbstractCommand {
    private final List<StringifLines;
    private final List<StringendLines;
    private final Command command;
    private final String variable;
    public IfCommand(List<StringifLinesList<StringendLines,
        Command commandString variable) {
      this. = variable;
      this. = ImmutableList.copyOf(ifLines);
      this. = ImmutableList.copyOf(endLines);
      this. = command;
    }
    public void execute(boolean executethrows Exception {
      echo();
      // Switch to a mode where we don't execute, just echo.
      boolean oldExecute = Quidem.this.;
      boolean newExecute;
      if () {
        // If "skip" is set, stay in current (disabled) mode.
        newExecute = oldExecute;
      } else {
        // If "enable" is true, stay in the current mode.
        newExecute = getBoolean();
      }
      .execute(newExecute);
      echo();
    }
  }

  
Command that switches to a mode where we skip executing the rest of the input. The input is still printed.
  class SkipCommand extends SimpleCommand {
    public SkipCommand(List<Stringlines) {
      super(lines);
    }
    public void execute(boolean executethrows Exception {
      echo();
      // Switch to a mode where we don't execute, just echo.
      // Set "skip" so we don't leave that mode.
       = true;
      Quidem.this. = false;
    }
  }

  
Command that executes a comment. (Does nothing.)
  class CompositeCommand extends AbstractCommand {
    private final List<Commandcommands;
    public CompositeCommand(List<Commandcommands) {
      this. = commands;
    }
    public void execute(boolean executethrows Exception {
      // We handle all RuntimeExceptions, all Exceptions, and a limited number
      // of Errors. If we don't understand an Error (e.g. OutOfMemoryError)
      // then we print it out, then abort.
      for (Command command : ) {
        boolean abort = false;
        Throwable e = null;
        try {
          command.execute(execute && Quidem.this.);