/* This file is part of VoltDB. * Copyright (C) 2008 Vertica Systems Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.hsqldb; import org.hsqldb.lib.HashMappedList; import org.hsqldb.persist.HsqlProperties; import org.hsqldb.result.Result; import org.hsqldb.result.ResultConstants; import org.hsqldb.result.ResultMetaData; /** * This class is built to create a single in-memory database * which can then be easily manipulated by VoltDB code. * <p> * Primary interaction with HSQLDB in the following ways: * <ul> * <li>Initialize an In-Memory SQL Store</li> * <li>Execute DDL Statements</li> * <li>Dump Serialized Catalog as XML</li> * <li>Compile SQL DML Statements to XML</li> * </ul> */ public class HSQLInterface { /** * The spacer to use for nested XML elements */ public static final String XML_INDENT = " "; /** * Exception subclass that is thrown from <code>getXMLCompiledStatement</code> * and <code>runDDLCommand</code> when a SQL parse error is encountered. * * @see getXMLCompiledStatement * @see runDDLCommand */ public static class HSQLParseException extends Exception { private static final long serialVersionUID = -7341323582748684001L; private String message = null; private Integer lineNo = null; HSQLParseException(String msg) { message = msg; } HSQLParseException(String msg, int lineNo) { message = msg; this.lineNo = lineNo; } public Integer getLineNumber() { return lineNo; } @Override public String getMessage() { return message; } } Session sessionProxy; static int instanceId = 0; private HSQLInterface(Session sessionProxy) { this.sessionProxy = sessionProxy; } public void close() { sessionProxy.close(); DatabaseManager.closeDatabases(Database.CLOSEMODE_IMMEDIATELY); sessionProxy = null; } /** * Load up an HSQLDB in-memory instance. * * @return A newly initialized in-memory HSQLDB instance accessible * through the returned instance of HSQLInterface */ public static HSQLInterface loadHsqldb() { Session sessionProxy = null; String name = "hsqldbinstance-" + String.valueOf(instanceId) + "-" + String.valueOf(System.currentTimeMillis()); instanceId++; HsqlProperties props = new HsqlProperties(); try { sessionProxy = DatabaseManager.newSession(DatabaseURL.S_MEM, name, "SA", "", props, 0); } catch (HsqlException e) { e.printStackTrace(); } // make HSQL case insensitive sessionProxy.executeDirectStatement("SET IGNORECASE TRUE;"); return new HSQLInterface(sessionProxy); } /** * Modify the current schema with a SQL DDL command. * * @param ddl The SQL DDL statement to be run. * @throws HSQLParseException Throws exception if SQL parse error is * encountered. */ public void runDDLCommand(String ddl) throws HSQLParseException { Result result = sessionProxy.executeDirectStatement(ddl); if (result.mode == ResultConstants.ERROR) throw new HSQLParseException(result.getMainString()); } /** * Load a text file full or DDL and run <code>runDDLCommand</code> * on every DDL statment in the file. * * @param path Path to a text file containing semi-colon * delimeted SQL DDL statements. * @throws HSQLParseException throws an exeption if there * is a problem reading, parsing, or running the file. */ public void runDDLFile(String path) throws HSQLParseException { HSQLFileParser.Statement[] stmts; stmts = HSQLFileParser.getStatements(path); for (HSQLFileParser.Statement stmt : stmts) { try { runDDLCommand(stmt.statement); } catch (HSQLParseException e) { e.lineNo = stmt.lineNo; throw e; } } } /** * Compile a SQL statement with parameters into an XML representation.<p> * Any question-marks (?) in the statement will be considered parameters. * * @param sql SQL statement to be compiled against the current schema. * @return XML representation of the compiled statement. * @throws HSQLParseException Throws exception if SQL parse error is * encountered. */ public String getXMLCompiledStatement(String sql) throws HSQLParseException { Statement cs = null; try { cs = sessionProxy.compileStatement(sql); } catch (Throwable t) { //t.printStackTrace(); throw new HSQLParseException(t.getMessage()); } //ResultMetaData rmd = cs.getResultMetaData(); //ResultMetaData pmd = cs.getParametersMetaData(); //Result result = Result.newPrepareResponse(cs.id, cs.type, rmd, pmd); Result result = Result.newPrepareResponse(cs); if (result.mode == ResultConstants.ERROR) throw new HSQLParseException(result.getMainString()); String xml = null; xml = cs.voltGetXML(sessionProxy, HSQLInterface.XML_INDENT); String retval = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; retval += DTDSource.getCompiledStatementDTD(); retval += "<statement>\n" + xml + "\n</statement>\n"; return retval; } /** * Debug-only method that prints out the names of all * tables in the current schema. */ @SuppressWarnings("unused") private void printTables() { String schemaName = null; try { schemaName = sessionProxy.getSchemaName(null); } catch (HsqlException e) { e.printStackTrace(); } SchemaManager schemaManager = sessionProxy.getDatabase().schemaManager; System.out.println("*** Tables For Schema: " + schemaName + " ***"); // load all the tables HashMappedList hsqlTables = schemaManager.getTables(schemaName); for (int i = 0; i < hsqlTables.size(); i++) { Table table = (Table) hsqlTables.get(i); System.out.println(table.getName().name); } } /** * Get an serialized XML representation of the current schema/catalog. * The output will include the DTD * * @return The XML representing a catalog * @throws HSQLParseException */ public String getXMLFromCatalog() throws HSQLParseException { return (getXMLFromCatalog(true)); } /** * Get an serialized XML representation of the current schema/catalog. * * @param include_dtd if true, the output will include the XML's DTD embedded * @return The XML representing the catalog. * @throws HSQLParseException */ public String getXMLFromCatalog(boolean include_dtd) throws HSQLParseException { StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); if (include_dtd) { sb.append(DTDSource.getCatalogDTD()); } sb.append("<databaseschema>\n"); String schemaName = null; try { schemaName = sessionProxy.getSchemaName(null); } catch (HsqlException e) { e.printStackTrace(); } SchemaManager schemaManager = sessionProxy.getDatabase().schemaManager; // load all the tables HashMappedList hsqlTables = schemaManager.getTables(schemaName); for (int i = 0; i < hsqlTables.size(); i++) { Table table = (Table) hsqlTables.get(i); sb.append(table.voltGetXML(sessionProxy, " ")); } sb.append("</databaseschema>\n"); return sb.toString(); } }