|
19 | 19 | import java.time.LocalDateTime; |
20 | 20 | import java.time.temporal.ChronoUnit; |
21 | 21 |
|
| 22 | +import java.sql.Connection; |
| 23 | +import java.sql.DriverManager; |
| 24 | +import java.sql.PreparedStatement; |
| 25 | +import java.sql.ResultSet; |
| 26 | +import java.sql.SQLException; |
| 27 | +import java.util.Map; |
| 28 | +import java.util.HashMap; |
| 29 | + |
22 | 30 | @SuppressWarnings("ucd") // ignore unused code detector warnings, because this class uses reflection |
23 | 31 | public class CompiletimeNatives extends ReflectionBasedNativeProvider implements NativesProvider { |
24 | 32 | private final boolean isProd; |
@@ -180,4 +188,179 @@ public ILconstString getBuildDate() { |
180 | 188 | public ILconstBool isProductionBuild() { |
181 | 189 | return isProd ? ILconstBool.TRUE : ILconstBool.FALSE; |
182 | 190 | } |
| 191 | + |
| 192 | + private int sqliteHandleCounter = 0; |
| 193 | + private final Map<Integer, Connection> sqliteConnections = new HashMap<>(); |
| 194 | + private final Map<Integer, PreparedStatement> sqliteStatements = new HashMap<>(); |
| 195 | + private final Map<Integer, ResultSet> sqliteResultSets = new HashMap<>(); |
| 196 | + |
| 197 | + public ILconstInt sqlite_open(ILconstString path) { |
| 198 | + try { |
| 199 | + Connection conn = DriverManager.getConnection("jdbc:sqlite:" + path.getVal()); |
| 200 | + int handle = ++sqliteHandleCounter; |
| 201 | + sqliteConnections.put(handle, conn); |
| 202 | + return new ILconstInt(handle); |
| 203 | + } catch (SQLException e) { |
| 204 | + throw new InterpreterException("Failed to open SQLite database " + path.getVal() + ": " + e.getMessage()); |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + public ILconstInt sqlite_prepare(ILconstInt connection, ILconstString query) { |
| 209 | + Connection conn = sqliteConnections.get(connection.getVal()); |
| 210 | + if (conn == null) throw new InterpreterException("Invalid SQLite connection handle: " + connection.getVal()); |
| 211 | + try { |
| 212 | + PreparedStatement stmt = conn.prepareStatement(query.getVal()); |
| 213 | + int handle = ++sqliteHandleCounter; |
| 214 | + sqliteStatements.put(handle, stmt); |
| 215 | + return new ILconstInt(handle); |
| 216 | + } catch (SQLException e) { |
| 217 | + throw new InterpreterException("Failed to prepare SQLite statement: " + e.getMessage()); |
| 218 | + } |
| 219 | + } |
| 220 | + |
| 221 | + public void sqlite_bind_int(ILconstInt statement, ILconstInt index, ILconstInt value) { |
| 222 | + PreparedStatement stmt = sqliteStatements.get(statement.getVal()); |
| 223 | + if (stmt == null) throw new InterpreterException("Invalid SQLite statement handle: " + statement.getVal()); |
| 224 | + try { |
| 225 | + stmt.setInt(index.getVal(), value.getVal()); |
| 226 | + } catch (SQLException e) { |
| 227 | + throw new InterpreterException("Failed to bind int: " + e.getMessage()); |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + public void sqlite_bind_real(ILconstInt statement, ILconstInt index, ILconstReal value) { |
| 232 | + PreparedStatement stmt = sqliteStatements.get(statement.getVal()); |
| 233 | + if (stmt == null) throw new InterpreterException("Invalid SQLite statement handle: " + statement.getVal()); |
| 234 | + try { |
| 235 | + stmt.setDouble(index.getVal(), (double) value.getVal()); |
| 236 | + } catch (SQLException e) { |
| 237 | + throw new InterpreterException("Failed to bind real: " + e.getMessage()); |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | + public void sqlite_bind_string(ILconstInt statement, ILconstInt index, ILconstString value) { |
| 242 | + PreparedStatement stmt = sqliteStatements.get(statement.getVal()); |
| 243 | + if (stmt == null) throw new InterpreterException("Invalid SQLite statement handle: " + statement.getVal()); |
| 244 | + try { |
| 245 | + stmt.setString(index.getVal(), value.getVal()); |
| 246 | + } catch (SQLException e) { |
| 247 | + throw new InterpreterException("Failed to bind string: " + e.getMessage()); |
| 248 | + } |
| 249 | + } |
| 250 | + |
| 251 | + public ILconstBool sqlite_step(ILconstInt statement) { |
| 252 | + PreparedStatement stmt = sqliteStatements.get(statement.getVal()); |
| 253 | + if (stmt == null) throw new InterpreterException("Invalid SQLite statement handle: " + statement.getVal()); |
| 254 | + try { |
| 255 | + ResultSet rs = sqliteResultSets.get(statement.getVal()); |
| 256 | + if (rs == null) { |
| 257 | + boolean hasResultSet = stmt.execute(); |
| 258 | + if (hasResultSet) { |
| 259 | + rs = stmt.getResultSet(); |
| 260 | + sqliteResultSets.put(statement.getVal(), rs); |
| 261 | + boolean hasRow = rs.next(); |
| 262 | + return hasRow ? ILconstBool.TRUE : ILconstBool.FALSE; |
| 263 | + } else { |
| 264 | + return ILconstBool.FALSE; |
| 265 | + } |
| 266 | + } else { |
| 267 | + boolean hasRow = rs.next(); |
| 268 | + return hasRow ? ILconstBool.TRUE : ILconstBool.FALSE; |
| 269 | + } |
| 270 | + } catch (SQLException e) { |
| 271 | + throw new InterpreterException("Failed to step SQLite statement: " + e.getMessage()); |
| 272 | + } |
| 273 | + } |
| 274 | + |
| 275 | + public ILconstInt sqlite_column_count(ILconstInt statement) { |
| 276 | + try { |
| 277 | + ResultSet rs = sqliteResultSets.get(statement.getVal()); |
| 278 | + if (rs != null) { |
| 279 | + return new ILconstInt(rs.getMetaData().getColumnCount()); |
| 280 | + } |
| 281 | + PreparedStatement stmt = sqliteStatements.get(statement.getVal()); |
| 282 | + if (stmt == null) throw new InterpreterException("Invalid SQLite statement handle: " + statement.getVal()); |
| 283 | + java.sql.ResultSetMetaData meta = stmt.getMetaData(); |
| 284 | + return new ILconstInt(meta == null ? 0 : meta.getColumnCount()); |
| 285 | + } catch (SQLException e) { |
| 286 | + throw new InterpreterException("Failed to get column count: " + e.getMessage()); |
| 287 | + } |
| 288 | + } |
| 289 | + |
| 290 | + public ILconstInt sqlite_column_int(ILconstInt statement, ILconstInt index) { |
| 291 | + ResultSet rs = sqliteResultSets.get(statement.getVal()); |
| 292 | + if (rs == null) throw new InterpreterException("No result set for statement handle: " + statement.getVal()); |
| 293 | + try { |
| 294 | + return new ILconstInt(rs.getInt(index.getVal() + 1)); |
| 295 | + } catch (SQLException e) { |
| 296 | + throw new InterpreterException("Failed to get column int: " + e.getMessage()); |
| 297 | + } |
| 298 | + } |
| 299 | + |
| 300 | + public ILconstReal sqlite_column_real(ILconstInt statement, ILconstInt index) { |
| 301 | + ResultSet rs = sqliteResultSets.get(statement.getVal()); |
| 302 | + if (rs == null) throw new InterpreterException("No result set for statement handle: " + statement.getVal()); |
| 303 | + try { |
| 304 | + return new ILconstReal((float) rs.getDouble(index.getVal() + 1)); |
| 305 | + } catch (SQLException e) { |
| 306 | + throw new InterpreterException("Failed to get column real: " + e.getMessage()); |
| 307 | + } |
| 308 | + } |
| 309 | + |
| 310 | + public ILconstString sqlite_column_string(ILconstInt statement, ILconstInt index) { |
| 311 | + ResultSet rs = sqliteResultSets.get(statement.getVal()); |
| 312 | + if (rs == null) throw new InterpreterException("No result set for statement handle: " + statement.getVal()); |
| 313 | + try { |
| 314 | + String val = rs.getString(index.getVal() + 1); |
| 315 | + return new ILconstString(val == null ? "" : val); |
| 316 | + } catch (SQLException e) { |
| 317 | + throw new InterpreterException("Failed to get column string: " + e.getMessage()); |
| 318 | + } |
| 319 | + } |
| 320 | + |
| 321 | + public void sqlite_reset(ILconstInt statement) { |
| 322 | + PreparedStatement stmt = sqliteStatements.get(statement.getVal()); |
| 323 | + if (stmt != null) { |
| 324 | + try { |
| 325 | + ResultSet rs = sqliteResultSets.remove(statement.getVal()); |
| 326 | + if (rs != null) rs.close(); |
| 327 | + } catch (SQLException e) { |
| 328 | + // Ignore |
| 329 | + } |
| 330 | + } |
| 331 | + } |
| 332 | + |
| 333 | + public void sqlite_finalize(ILconstInt statement) { |
| 334 | + PreparedStatement stmt = sqliteStatements.remove(statement.getVal()); |
| 335 | + if (stmt != null) { |
| 336 | + try { |
| 337 | + ResultSet rs = sqliteResultSets.remove(statement.getVal()); |
| 338 | + if (rs != null) rs.close(); |
| 339 | + stmt.close(); |
| 340 | + } catch (SQLException e) { |
| 341 | + // Ignore |
| 342 | + } |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + public void sqlite_close(ILconstInt connection) { |
| 347 | + Connection conn = sqliteConnections.remove(connection.getVal()); |
| 348 | + if (conn != null) { |
| 349 | + try { |
| 350 | + conn.close(); |
| 351 | + } catch (SQLException e) { |
| 352 | + // Ignore |
| 353 | + } |
| 354 | + } |
| 355 | + } |
| 356 | + |
| 357 | + public void sqlite_exec(ILconstInt connection, ILconstString query) { |
| 358 | + Connection conn = sqliteConnections.get(connection.getVal()); |
| 359 | + if (conn == null) throw new InterpreterException("Invalid SQLite connection handle: " + connection.getVal()); |
| 360 | + try (java.sql.Statement stmt = conn.createStatement()) { |
| 361 | + stmt.execute(query.getVal()); |
| 362 | + } catch (SQLException e) { |
| 363 | + throw new InterpreterException("Failed to exec SQLite query: " + e.getMessage()); |
| 364 | + } |
| 365 | + } |
183 | 366 | } |
0 commit comments