/*
 * Copyright (c) 2011-2014 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.jdbcclient.impl.actions;

import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonArray;
import io.vertx.ext.jdbc.impl.actions.JDBCStatementHelper;
import io.vertx.ext.sql.SQLOptions;
import io.vertx.jdbcclient.SqlOutParam;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.Tuple;
import io.vertx.sqlclient.impl.command.ExtendedQueryCommand;

import java.sql.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.stream.Collector;

Author:Paulo Lopes
/** * @author <a href="mailto:pmlopes@gmail.com">Paulo Lopes</a> */
public class JDBCPreparedBatch<C, R> extends JDBCQueryAction<C, R> { private final ExtendedQueryCommand<R> query; private final List<Tuple> listParams; public JDBCPreparedBatch(JDBCStatementHelper helper, SQLOptions options, ExtendedQueryCommand<R> query, Collector<Row, C, R> collector, List<Tuple> listParams) { super(helper, options, collector); this.query = query; this.listParams = listParams; } @Override public JDBCResponse<R> execute(Connection conn) throws SQLException { try (PreparedStatement ps = prepare(conn)) { for (Tuple params : listParams) { fillStatement(ps, conn, params); ps.addBatch(); } return decode(ps, ps.executeBatch(), true); } } private PreparedStatement prepare(Connection conn) throws SQLException { final String sql = query.sql(); boolean autoGeneratedKeys = options == null || options.isAutoGeneratedKeys(); boolean autoGeneratedIndexes = options != null && options.getAutoGeneratedKeysIndexes() != null; if (autoGeneratedKeys && !autoGeneratedIndexes) { return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); } else if (autoGeneratedIndexes) { // convert json array to int or string array JsonArray indexes = options.getAutoGeneratedKeysIndexes(); try { if (indexes.getValue(0) instanceof Number) { int[] keys = new int[indexes.size()]; for (int i = 0; i < keys.length; i++) { keys[i] = indexes.getInteger(i); } return conn.prepareStatement(sql, keys); } else if (indexes.getValue(0) instanceof String) { String[] keys = new String[indexes.size()]; for (int i = 0; i < keys.length; i++) { keys[i] = indexes.getString(i); } return conn.prepareStatement(sql, keys); } else { throw new SQLException("Invalid type of index, only [int, String] allowed"); } } catch (RuntimeException e) { // any exception due to type conversion throw new SQLException(e); } } else { return conn.prepareStatement(sql); } } private void fillStatement(PreparedStatement ps, Connection conn, Tuple params) throws SQLException { for (int i = 0; i < params.size(); i++) { // we must convert types (to comply to JDBC) Object value = adaptType(conn, params.getValue(i)); if (value instanceof SqlOutParam) { throw new SQLException("{out} parameters are not supported in batch mode"); } else { ps.setObject(i + 1, value); } } } private Object adaptType(Connection conn, Object value) throws SQLException { // we must convert types (to comply to JDBC) if (value instanceof LocalTime) { // -> java.sql.Time LocalTime time = (LocalTime) value; return Time.valueOf(time); } else if (value instanceof LocalDate) { // -> java.sql.Date LocalDate date = (LocalDate) value; return Date.valueOf(date); } else if (value instanceof Instant) { // -> java.sql.Timestamp Instant timestamp = (Instant) value; return Timestamp.from(timestamp); } else if (value instanceof Buffer) { // -> java.sql.Blob Buffer blob = (Buffer) value; return conn.createBlob().setBytes(0, blob.getBytes()); } return value; } }