001package org.hl7.fhir.r5.utils.sql; 002 003import java.sql.Connection; 004import java.sql.PreparedStatement; 005import java.sql.SQLType; 006import java.util.List; 007 008import org.hl7.fhir.exceptions.FHIRException; 009import org.hl7.fhir.r5.model.Base; 010import org.hl7.fhir.r5.utils.sql.Validator.TrueFalseOrUnknown; 011import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 012 013public class StorageSqlite3 implements Storage { 014 015 public static class SQLiteStore extends Store { 016 private PreparedStatement p; 017 018 protected SQLiteStore(String name, PreparedStatement p) { 019 super(name); 020 this.p = p; 021 } 022 023 public PreparedStatement getP() { 024 return p; 025 } 026 027 } 028 029 private Connection conn; 030 private int nextKey = 0; 031 032 public StorageSqlite3(Connection conn) { 033 super(); 034 this.conn = conn; 035 } 036 037 @Override 038 public Store createStore(String name, List<Column> columns) { 039 try { 040 CommaSeparatedStringBuilder fields = new CommaSeparatedStringBuilder(", "); 041 CommaSeparatedStringBuilder values = new CommaSeparatedStringBuilder(", "); 042 StringBuilder b = new StringBuilder(); 043 b.append("Create Table "+name+" ( "); 044 b.append("ViewRowKey integer NOT NULL"); 045 for (Column column : columns) { 046 b.append(", "+column.getName()+" "+sqliteType(column.getKind())+" NULL"); // index columns are always nullable 047 fields.append(column.getName()); 048 values.append("?"); 049 } 050 b.append(", PRIMARY KEY (ViewRowKey))\r\n"); 051 conn.createStatement().execute(b.toString()); 052 053 String isql = "Insert into "+name+" (ViewRowKey, "+fields.toString()+") values (?, "+values.toString()+")"; 054 PreparedStatement psql = conn.prepareStatement(isql); 055 return new SQLiteStore(name, psql); 056 } catch (Exception e) { 057 throw new FHIRException(e); 058 } 059 } 060 061 private String sqliteType(ColumnKind type) { 062 switch (type) { 063 case DateTime: return "Text"; 064 case Decimal: return "Real"; 065 case Integer: return "Integer"; 066 case String: return "Text"; 067 case Time: return "Text"; 068 case Binary: return "Text"; 069 case Boolean: return "Integer"; 070 case Complex: throw new FHIRException("SQLite runner does not handle complexes"); 071 } 072 return null; 073 } 074 075 @Override 076 public void addRow(Store store, List<Cell> cells) { 077 try { 078 SQLiteStore sqls = (SQLiteStore) store; 079 PreparedStatement p = sqls.getP(); 080 p.setInt(1, ++nextKey); 081 for (int i = 0; i < cells.size(); i++) { 082 Cell c = cells.get(i); 083 switch (c.getColumn().getKind()) { 084 case Null: 085 p.setNull(i+2, java.sql.Types.NVARCHAR); 086 case Binary: 087 p.setBytes(i+2, c.getValues().size() == 0 ? null : c.getValues().get(0).getValueBinary()); 088 break; 089 case Boolean: 090 p.setBoolean(i+2, c.getValues().size() == 0 ? false : c.getValues().get(0).getValueBoolean().booleanValue()); 091 break; 092 case DateTime: 093 p.setDate(i+2, c.getValues().size() == 0 ? null : new java.sql.Date(c.getValues().get(0).getValueDate().getTime())); 094 break; 095 case Decimal: 096 p.setString(i+2, c.getValues().size() == 0 ? null : c.getValues().get(0).getValueString()); 097 break; 098 case Integer: 099 p.setInt(i+2, c.getValues().size() == 0 ? 0 : c.getValues().get(0).getValueInt().intValue()); 100 break; 101 case String: 102 p.setString(i+2, c.getValues().size() == 0 ? null : c.getValues().get(0).getValueString()); 103 break; 104 case Time: 105 p.setString(i+2, c.getValues().size() == 0 ? null : c.getValues().get(0).getValueString()); 106 break; 107 case Complex: throw new FHIRException("SQLite runner does not handle complexes"); 108 } 109 } 110 p.execute(); 111 } catch (Exception e) { 112 throw new FHIRException(e); 113 } 114 115 } 116 117 @Override 118 public void finish(Store store) { 119 // nothing 120 } 121 122 @Override 123 public TrueFalseOrUnknown supportsArrays() { 124 return TrueFalseOrUnknown.FALSE; 125 } 126 127 @Override 128 public TrueFalseOrUnknown supportsComplexTypes() { 129 return TrueFalseOrUnknown.FALSE; 130 } 131 132 @Override 133 public TrueFalseOrUnknown needsName() { 134 return TrueFalseOrUnknown.TRUE; 135 } 136 137 @Override 138 public String getKeyForSourceResource(Base res) { 139 throw new Error("Key management for resources isn't decided yet"); 140 } 141 142 @Override 143 public String getKeyForTargetResource(Base res) { 144 throw new Error("Key management for resources isn't decided yet"); 145 } 146}