001package edu.pdx.cs.joy.jdbc; 002 003import java.io.File; 004import java.sql.*; 005 006/** 007 * A command-line program that executes SQL statements against an H2 database. 008 * Supports all CRUD operations (INSERT, SELECT, UPDATE, DELETE) and displays 009 * results in a human-readable format. 010 */ 011public class ExecuteH2DatabaseStatement { 012 013 /** 014 * Main method that takes a database file path and SQL statement as arguments. 015 * 016 * @param args command line arguments: args[0] = database file path, args[1] = SQL statement 017 */ 018 public static void main(String[] args) { 019 if (args.length < 2) { 020 System.err.println("Missing required arguments"); 021 System.err.println("Usage: java ExecuteH2DatabaseStatement <database-file-path> <sql-statement>"); 022 System.err.println(); 023 System.err.println("Examples:"); 024 System.err.println(" SELECT: java ExecuteH2DatabaseStatement mydb.db \"SELECT * FROM users\""); 025 System.err.println(" INSERT: java ExecuteH2DatabaseStatement mydb.db \"INSERT INTO users (name) VALUES ('John')\""); 026 System.err.println(" UPDATE: java ExecuteH2DatabaseStatement mydb.db \"UPDATE users SET name='Jane' WHERE id=1\""); 027 System.err.println(" DELETE: java ExecuteH2DatabaseStatement mydb.db \"DELETE FROM users WHERE id=1\""); 028 return; 029 } 030 031 String dbFilePath = args[0]; 032 String sqlStatement = args[1]; 033 034 File dbFile = new File(dbFilePath); 035 System.out.println("Connecting to H2 database: " + dbFile.getAbsolutePath()); 036 System.out.println("Executing SQL: " + sqlStatement); 037 System.out.println(); 038 039 try (Connection connection = H2DatabaseHelper.createFileBasedConnection(dbFile)) { 040 executeStatement(connection, sqlStatement); 041 } catch (SQLException ex) { 042 System.err.println("Error: " + ex.getMessage()); 043 } 044 } 045 046 /** 047 * Executes a SQL statement and displays the results. 048 * 049 * @param connection the database connection 050 * @param sql the SQL statement to execute 051 * @throws SQLException if a database error occurs 052 */ 053 private static void executeStatement(Connection connection, String sql) throws SQLException { 054 try (Statement statement = connection.createStatement()) { 055 // Determine if this is a query (SELECT) or update (INSERT/UPDATE/DELETE) 056 boolean isQuery = statement.execute(sql); 057 058 if (isQuery) { 059 // Handle SELECT queries 060 try (ResultSet resultSet = statement.getResultSet()) { 061 displayResultSet(resultSet); 062 } 063 } else { 064 // Handle INSERT, UPDATE, DELETE 065 int rowsAffected = statement.getUpdateCount(); 066 System.out.println("Statement executed successfully"); 067 System.out.println("Rows affected: " + rowsAffected); 068 } 069 } 070 } 071 072 /** 073 * Displays a ResultSet in a formatted table. 074 * 075 * @param resultSet the result set to display 076 * @throws SQLException if a database error occurs 077 */ 078 private static void displayResultSet(ResultSet resultSet) throws SQLException { 079 ResultSetMetaData metaData = resultSet.getMetaData(); 080 int columnCount = metaData.getColumnCount(); 081 082 // Calculate column widths 083 int[] columnWidths = new int[columnCount]; 084 String[] columnNames = new String[columnCount]; 085 086 for (int i = 1; i <= columnCount; i++) { 087 columnNames[i - 1] = metaData.getColumnLabel(i); 088 columnWidths[i - 1] = Math.max(columnNames[i - 1].length(), 10); 089 } 090 091 // Collect all rows to calculate proper column widths 092 java.util.List<String[]> rows = new java.util.ArrayList<>(); 093 while (resultSet.next()) { 094 String[] row = new String[columnCount]; 095 for (int i = 1; i <= columnCount; i++) { 096 Object value = resultSet.getObject(i); 097 row[i - 1] = value != null ? value.toString() : "NULL"; 098 columnWidths[i - 1] = Math.max(columnWidths[i - 1], row[i - 1].length()); 099 } 100 rows.add(row); 101 } 102 103 // Print header 104 printSeparator(columnWidths); 105 printRow(columnNames, columnWidths); 106 printSeparator(columnWidths); 107 108 // Print data rows 109 if (rows.isEmpty()) { 110 System.out.println("No rows returned"); 111 } else { 112 for (String[] row : rows) { 113 printRow(row, columnWidths); 114 } 115 printSeparator(columnWidths); 116 System.out.println(rows.size() + " row(s) returned"); 117 } 118 } 119 120 /** 121 * Prints a separator line. 122 * 123 * @param columnWidths array of column widths 124 */ 125 private static void printSeparator(int[] columnWidths) { 126 System.out.print("+"); 127 for (int width : columnWidths) { 128 for (int i = 0; i < width + 2; i++) { 129 System.out.print("-"); 130 } 131 System.out.print("+"); 132 } 133 System.out.println(); 134 } 135 136 /** 137 * Prints a data row. 138 * 139 * @param values array of values to print 140 * @param columnWidths array of column widths 141 */ 142 private static void printRow(String[] values, int[] columnWidths) { 143 System.out.print("|"); 144 for (int i = 0; i < values.length; i++) { 145 System.out.print(" "); 146 System.out.print(padRight(values[i], columnWidths[i])); 147 System.out.print(" |"); 148 } 149 System.out.println(); 150 } 151 152 /** 153 * Pads a string to the right with spaces. 154 * 155 * @param str the string to pad 156 * @param length the desired length 157 * @return the padded string 158 */ 159 private static String padRight(String str, int length) { 160 if (str.length() >= length) { 161 return str; 162 } 163 StringBuilder sb = new StringBuilder(str); 164 while (sb.length() < length) { 165 sb.append(' '); 166 } 167 return sb.toString(); 168 } 169}