package io.github.lucaseasedup.logit.storage; import java.sql.ResultSet; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import java.util.Map; public final class SqlUtils { private SqlUtils() { } public static String escapeQuotes( String string, String quote, boolean escapeBackslashes ) { if (string == null || quote == null) throw new IllegalArgumentException(); string = string.replace(quote, quote + quote); if (escapeBackslashes) { string = string.replace("\\", "\\\\"); } return string; } public static boolean resolveSelector(Selector selector, StorageEntry entry) { if (selector == null || entry == null) throw new IllegalArgumentException(); if (selector instanceof SelectorConstant) { return ((SelectorConstant) selector).getValue(); } else if (selector instanceof SelectorNegation) { return !resolveSelector( ((SelectorNegation) selector).getOperand(), entry ); } else if (selector instanceof SelectorBinary) { SelectorBinary selectorBinary = (SelectorBinary) selector; switch (selectorBinary.getRelation()) { case AND: return resolveSelector(selectorBinary.getLeftOperand(), entry) && resolveSelector(selectorBinary.getRightOperand(), entry); case OR: return resolveSelector(selectorBinary.getLeftOperand(), entry) || resolveSelector(selectorBinary.getRightOperand(), entry); default: throw new IllegalArgumentException( "Unsupported relation: " + selectorBinary.getRelation() ); } } else if (selector instanceof SelectorCondition) { SelectorCondition selectorCondition = (SelectorCondition) selector; String key = selectorCondition.getKey(); String operandValue = selectorCondition.getValue(); String actualValue = entry.get(key); switch (selectorCondition.getRelation()) { case EQUALS: if (actualValue == null) { return operandValue == null; } else { return actualValue.equals(operandValue); } case LESS_THAN: try { long actualLong = Long.parseLong(actualValue); long operandLong = Long.parseLong(operandValue); return actualLong < operandLong; } catch (NumberFormatException ex) { return false; } case GREATER_THAN: try { long actualLong = Long.parseLong(actualValue); long operandLong = Long.parseLong(operandValue); return actualLong > operandLong; } catch (NumberFormatException ex) { return false; } case STARTS_WITH: return actualValue.startsWith(operandValue); case ENDS_WITH: return actualValue.endsWith(operandValue); case CONTAINS: return actualValue.contains(operandValue); default: throw new IllegalArgumentException( "Unsupported relation: " + selectorCondition.getRelation() ); } } else { throw new IllegalArgumentException( "Unsupported selector: " + selector.getClass().getName() ); } } public static List<StorageEntry> copyResultSet(ResultSet rs) throws SQLException { if (rs == null) throw new IllegalArgumentException(); List<StorageEntry> entries = new LinkedList<>(); if (rs.isBeforeFirst()) { while (rs.next()) { StorageEntry.Builder entryBuilder = new StorageEntry.Builder(); for (int i = 1, n = rs.getMetaData().getColumnCount(); i <= n; i++) { entryBuilder.put( rs.getMetaData().getColumnLabel(i), rs.getString(i) ); } entries.add(entryBuilder.build()); } rs.close(); } return entries; } public static String translateSelector( Selector selector, String columnQuote, String valueQuote ) { if (selector == null || columnQuote == null || valueQuote == null) throw new IllegalArgumentException(); if (selector instanceof SelectorConstant) { SelectorConstant selectorConstant = (SelectorConstant) selector; return (selectorConstant.getValue()) ? "1 = 1" : "1 = 0"; } else if (selector instanceof SelectorNegation) { SelectorNegation selectorNegation = (SelectorNegation) selector; String translatedSelector = translateSelector( selectorNegation.getOperand(), columnQuote, valueQuote ); return "NOT (" + translatedSelector + ")"; } else if (selector instanceof SelectorBinary) { SelectorBinary selectorBinary = (SelectorBinary) selector; StringBuilder sb = new StringBuilder(); sb.append("("); sb.append(translateSelector( selectorBinary.getLeftOperand(), columnQuote, valueQuote )); sb.append(") "); switch (selectorBinary.getRelation()) { case AND: sb.append("AND"); break; case OR: sb.append("OR"); break; default: throw new IllegalArgumentException( "Unsupported relation: " + selectorBinary.getRelation() ); } sb.append(" ("); sb.append(translateSelector( selectorBinary.getRightOperand(), columnQuote, valueQuote )); sb.append(")"); return sb.toString(); } else if (selector instanceof SelectorCondition) { SelectorCondition selectorCondition = (SelectorCondition) selector; StringBuilder sb = new StringBuilder(); sb.append("("); sb.append(columnQuote); sb.append(escapeQuotes( selectorCondition.getKey(), columnQuote, true )); sb.append(columnQuote); sb.append(") "); switch (selectorCondition.getRelation()) { case EQUALS: sb.append("="); break; case LESS_THAN: sb.append("<"); break; case GREATER_THAN: sb.append(">"); break; case STARTS_WITH: case ENDS_WITH: case CONTAINS: sb.append("LIKE"); break; default: throw new IllegalArgumentException( "Unsupported relation: " + selectorCondition.getRelation() ); } sb.append(" ("); sb.append(valueQuote); if (selectorCondition.getRelation() == Infix.ENDS_WITH || selectorCondition.getRelation() == Infix.CONTAINS) { sb.append("%"); } sb.append(escapeQuotes( selectorCondition.getValue(), valueQuote, true )); if (selectorCondition.getRelation() == Infix.STARTS_WITH || selectorCondition.getRelation() == Infix.CONTAINS) { sb.append("%"); } sb.append(valueQuote); sb.append(")"); return sb.toString(); } else { throw new IllegalArgumentException( "Unsupported selector: " + selector.getClass().getName() ); } } public static String encodeType(DataType type) { if (type == null) throw new IllegalArgumentException(); switch (type) { case INTEGER: return "INTEGER"; case REAL: return "REAL"; case TINYTEXT: return "VARCHAR(255)"; case MEDIUMTEXT: return "VARCHAR(1023)"; case LONGTEXT: return "VARCHAR(10119)"; case TEXT: return "TEXT"; default: throw new IllegalArgumentException("Unknown type: " + type); } } public static DataType decodeType(String type) { if (type == null) throw new IllegalArgumentException(); type = type.toUpperCase(); if (type.startsWith("INT") || type.startsWith("TINYINT") || type.startsWith("SMALLINT") || type.startsWith("MEDIUMINT") || type.startsWith("BIGINT")) { return DataType.INTEGER; } else if (type.startsWith("REAL") || type.startsWith("DOUBLE") || type.startsWith("FLOAT")) { return DataType.REAL; } else if (type.startsWith("VARCHAR") || type.startsWith("TEXT") || type.startsWith("CLOB") || type.startsWith("CHAR") || type.startsWith("VARYING CHARACTER") || type.startsWith("NVARCHAR") || type.startsWith("LONGVARCHAR") || type.startsWith("NCHAR") || type.startsWith("NTEXT") || type.startsWith("LONGTEXT") || type.startsWith("MEDIUMTEXT") || type.startsWith("NCLOB") || type.startsWith("TINYTEXT")) { return DataType.TEXT; } else { throw new IllegalArgumentException("Unsupported type: " + type); } } public static String translateKeyList(List<String> keys, String columnQuote) { if (keys == null || columnQuote == null) throw new IllegalArgumentException(); StringBuilder sb = new StringBuilder(); for (String key : keys) { if (sb.length() > 0) { sb.append(", "); } sb.append(columnQuote); sb.append(escapeQuotes(key, columnQuote, true)); sb.append(columnQuote); } return sb.toString(); } public static String translateKeyTypeList( UnitKeys keys, String primaryKey, String columnQuote ) { if (keys == null || columnQuote == null) throw new IllegalArgumentException(); if (primaryKey != null && !keys.containsKey(primaryKey)) { throw new IllegalArgumentException( "Cannot create index on a non-existing key" ); } StringBuilder sb = new StringBuilder(); for (Map.Entry<String, DataType> e : keys.entrySet()) { if (sb.length() > 0) { sb.append(", "); } sb.append(columnQuote); sb.append(escapeQuotes(e.getKey(), columnQuote, true)); sb.append(columnQuote); sb.append(" "); sb.append(encodeType(e.getValue())); sb.append(" NOT NULL"); if (primaryKey != null && primaryKey.equals(e.getKey())) { sb.append(" PRIMARY KEY"); } } return sb.toString(); } public static String translateEntryNames( StorageEntry entry, String columnQuote ) { if (entry == null || columnQuote == null) throw new IllegalArgumentException(); StringBuilder sb = new StringBuilder(); for (StorageDatum datum : entry) { if (sb.length() > 0) { sb.append(", "); } sb.append(columnQuote); sb.append(escapeQuotes(datum.getKey(), columnQuote, true)); sb.append(columnQuote); } return sb.toString(); } public static String translateEntryValues( StorageEntry entry, String valueQuote ) { if (entry == null || valueQuote == null) throw new IllegalArgumentException(); StringBuilder sb = new StringBuilder(); for (StorageDatum datum : entry) { if (sb.length() > 0) { sb.append(", "); } sb.append(valueQuote); sb.append(escapeQuotes(datum.getValue(), valueQuote, true)); sb.append(valueQuote); } return sb.toString(); } public static String translateEntrySubset( StorageEntry entrySubset, String columnQuote, String valueQuote ) { if (entrySubset == null || columnQuote == null || valueQuote == null) throw new IllegalArgumentException(); StringBuilder sb = new StringBuilder(); for (StorageDatum datum : entrySubset) { if (sb.length() > 0) { sb.append(", "); } sb.append(columnQuote); sb.append(escapeQuotes(datum.getKey(), columnQuote, true)); sb.append(columnQuote); sb.append(" = "); sb.append(valueQuote); sb.append(escapeQuotes(datum.getValue(), valueQuote, true)); sb.append(valueQuote); } return sb.toString(); } }