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