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}