diff --git a/jdbc-driver/src/main/java/acolyte/jdbc/Defaults.java b/jdbc-driver/src/main/java/acolyte/jdbc/Defaults.java index 33c8c4c4..ca332e0a 100644 --- a/jdbc-driver/src/main/java/acolyte/jdbc/Defaults.java +++ b/jdbc-driver/src/main/java/acolyte/jdbc/Defaults.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.sql.Timestamp; +import java.sql.Array; import java.sql.Types; import java.sql.Date; import java.sql.Time; @@ -57,6 +58,7 @@ final class Defaults { // JDBC type mappings final HashMap mappings = new HashMap(); + mappings.put(Types.ARRAY, Array.class.getName()); mappings.put(Types.BIGINT, Long.class.getName()); mappings.put(Types.BIT, Boolean.class.getName()); mappings.put(Types.BOOLEAN, Boolean.class.getName()); @@ -98,6 +100,7 @@ final class Defaults { // JDBC type names final HashMap names = new HashMap(); + names.put(Types.ARRAY, "ARRAY"); names.put(Types.BIGINT, "BIGINT"); names.put(Types.BIT, "BOOL"); names.put(Types.BOOLEAN, "BOOL"); @@ -134,6 +137,7 @@ final class Defaults { // JDBC type signs final HashMap signs = new HashMap(); + signs.put(Types.ARRAY, Boolean.FALSE); signs.put(Types.BIGINT, Boolean.TRUE); signs.put(Types.BIT, Boolean.FALSE); signs.put(Types.BOOLEAN, Boolean.FALSE); @@ -162,6 +166,7 @@ final class Defaults { final HashMap precisions = new HashMap(); + precisions.put(Types.ARRAY, 0); precisions.put(Types.BIGINT, 64); precisions.put(Types.BIT, 1); precisions.put(Types.BOOLEAN, 1); @@ -189,6 +194,7 @@ final class Defaults { // JDBC type scales final HashMap scales = new HashMap(); + scales.put(Types.ARRAY, 0); scales.put(Types.BIGINT, 0); scales.put(Types.BIT, 0); scales.put(Types.BOOLEAN, 0); diff --git a/jdbc-driver/src/main/java/acolyte/jdbc/ParameterMetaData.java b/jdbc-driver/src/main/java/acolyte/jdbc/ParameterMetaData.java index 6fa4371d..4a9b74ca 100644 --- a/jdbc-driver/src/main/java/acolyte/jdbc/ParameterMetaData.java +++ b/jdbc-driver/src/main/java/acolyte/jdbc/ParameterMetaData.java @@ -298,6 +298,11 @@ public static ParameterDef Decimal(final BigDecimal bd) { return Scaled(Types.DECIMAL, bd.scale()); } // end of Decimal + /** + * Array definition + */ + public static final ParameterDef Array = Default(Types.ARRAY); + /** * String definition */ diff --git a/jdbc-driver/src/main/java/acolyte/jdbc/PreparedStatement.java b/jdbc-driver/src/main/java/acolyte/jdbc/PreparedStatement.java index 3742fe39..cfea5bf0 100644 --- a/jdbc-driver/src/main/java/acolyte/jdbc/PreparedStatement.java +++ b/jdbc-driver/src/main/java/acolyte/jdbc/PreparedStatement.java @@ -583,7 +583,7 @@ public void setClob(final int parameterIndex, final Clob clob) public void setArray(final int parameterIndex, final Array x) throws SQLException { - throw new SQLFeatureNotSupportedException(); + setParam(parameterIndex, acolyte.jdbc.ParameterMetaData.Array, x); } // end of setArray /** @@ -974,6 +974,7 @@ private void setParam(final int index, */ private String normalizeClassName(final Class c) { if (Blob.class.isAssignableFrom(c)) return "java.sql.Blob"; + else if (Array.class.isAssignableFrom(c)) return "java.sql.Array"; return c.getName(); } // end of normalizeClassName diff --git a/jdbc-driver/src/test/scala/acolyte/jdbc/ParameterMetaDataSpec.scala b/jdbc-driver/src/test/scala/acolyte/jdbc/ParameterMetaDataSpec.scala index 84269903..69281bab 100644 --- a/jdbc-driver/src/test/scala/acolyte/jdbc/ParameterMetaDataSpec.scala +++ b/jdbc-driver/src/test/scala/acolyte/jdbc/ParameterMetaDataSpec.scala @@ -14,6 +14,7 @@ import org.specs2.mutable.Specification import acolyte.jdbc.ParameterMetaData.{ ParameterDef ⇒ Param, Binary, + Array => ArrayP, Blob ⇒ BlobP, Bool ⇒ BoolP, Byte ⇒ ByteP, @@ -227,6 +228,12 @@ object ParameterMetaDataSpec } } + "Array parameter" should { + "be default one" in { + ArrayP aka "array parameter" mustEqual DefaultP(Types.ARRAY) + } + } + "Binary parameter" should { "be default one" in { Binary aka "binary parameter" mustEqual DefaultP(Types.BINARY) diff --git a/jdbc-driver/src/test/scala/acolyte/jdbc/PreparedStatementSpec.scala b/jdbc-driver/src/test/scala/acolyte/jdbc/PreparedStatementSpec.scala index 6fe278d5..f064f4ae 100644 --- a/jdbc-driver/src/test/scala/acolyte/jdbc/PreparedStatementSpec.scala +++ b/jdbc-driver/src/test/scala/acolyte/jdbc/PreparedStatementSpec.scala @@ -16,6 +16,8 @@ import org.specs2.mutable.Specification import org.apache.commons.io.IOUtils.contentEquals +import scala.collection.JavaConversions + import acolyte.jdbc.StatementHandler.Parameter import acolyte.jdbc.test.{ EmptyConnectionHandler, Params } @@ -61,8 +63,6 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { aka("setter") must throwA[SQLFeatureNotSupportedException]). and(statement().setClob(0, null.asInstanceOf[java.sql.Clob]). aka("setter") must throwA[SQLFeatureNotSupportedException]). - and(statement().setArray(0, null). - aka("setter") must throwA[SQLFeatureNotSupportedException]). and(statement().setURL(0, null). aka("setter") must throwA[SQLFeatureNotSupportedException]). and(statement().setRowId(0, null). @@ -321,6 +321,63 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters { } } + "Array" should { + val stringArray = ImmutableArray.getInstance(classOf[String], + JavaConversions.seqAsJavaList(List("A", "B"))) + + "be set as first parameter" in { + lazy val s = statement() + s.setArray(1, stringArray) + + lazy val m = s.getParameterMetaData + + (m.getParameterCount aka "count" mustEqual 1). + and(m.getParameterType(1) aka "SQL type" mustEqual Types.ARRAY) + + } + + "be set as first object with SQL type" in { + lazy val s = statement() + s.setObject(1, stringArray, Types.ARRAY) + + lazy val m = s.getParameterMetaData + + (m.getParameterCount aka "count" mustEqual 1). + and(m.getParameterType(1) aka "SQL type" mustEqual Types.ARRAY) + + } + + "be set as first object with SQL type and scale" in { + lazy val s = statement() + s.setObject(1, stringArray, Types.ARRAY, 1) + + lazy val m = s.getParameterMetaData + + (m.getParameterCount aka "count" mustEqual 1). + and(m.getParameterType(1) aka "SQL type" mustEqual Types.ARRAY) + + } + + "be set as first object without SQL type" in { + lazy val s = statement() + s.setObject(1, stringArray) + + lazy val m = s.getParameterMetaData + + (m.getParameterCount aka "count" mustEqual 1). + and(m.getParameterType(1) aka "SQL type" mustEqual Types.ARRAY) + + } + + "be properly prepared" in { + (executeUpdate("TEST ?, y", Types.ARRAY, stringArray). + aka("SQL update") mustEqual ("TEST ?, y" -> stringArray)). + and(executeQuery("SELECT ? WHERE true", Types.ARRAY, stringArray). + aka("SQL query") mustEqual ("SELECT ? WHERE true" -> stringArray)) + + } + } + "Binary Large Object" should { "be set as first parameter" in { lazy val s = statement() @@ -1679,9 +1736,19 @@ sealed trait StatementParam[A] { sealed trait Setters { import java.math.BigDecimal import java.util.Calendar - import java.sql.{ Date, Time, Timestamp } + import java.sql.{ Array => SqlArray, Date, Time, Timestamp } import org.apache.commons.lang3.tuple.ImmutablePair + implicit def StmtArray[A <: SqlArray]: StatementParam[A] = + new StatementParam[A] { + def set(s: PreparedStatement, i: Int, p: A, t: Int) = { + s.setArray(i, p) + s + } + + def get(p: Parameter): A = p.right.asInstanceOf[A] + } + implicit def StmtBytes: StatementParam[Array[Byte]] = new StatementParam[Array[Byte]] { def set(s: PreparedStatement, i: Int, p: Array[Byte], t: Int) = { diff --git a/project/Acolyte.scala b/project/Acolyte.scala index 1d0384cb..52211012 100644 --- a/project/Acolyte.scala +++ b/project/Acolyte.scala @@ -10,7 +10,7 @@ object Acolyte extends Build with Dependencies aggregate(scalacPlugin, reactiveMongo, jdbcDriver, jdbcScala, studio). settings( organization in ThisBuild := "org.eu.acolyte", - version in ThisBuild := "1.0.27", + version in ThisBuild := "1.0.28", javaOptions in ThisBuild ++= Seq("-source", "1.6", "-target", "1.6"), scalaVersion in ThisBuild := "2.10.4", crossScalaVersions in ThisBuild := Seq("2.10.4", "2.11.2"),