14 ベクトルの操作

Oracle Databaseリリース23ai以降、表の列はベクトルとして宣言できます。ベクトルは、1つ以上の数値(整数または小数)の配列です。

ベクトル・データは機械学習に使用できます。Oracle AI (Artificial Intelligence) Vector Searchでは、キーワードではなくセマンティクスに基づいてデータを問い合せることができます。Oracle AI Vector Searchの詳細は、『Oracle AI Vector Searchユーザーズ・ガイド』を参照してください。

14.1 ベクトル用のJDBC APIおよび型

JDBCドライバでは、SQLデータ型がjava.SQL.SQLTypeインタフェースのインスタンスとして表されます。Oracle Databaseのデータ型ごとに、Oracle JDBCドライバではSQLTypeのインスタンスがoracle.JDBC.OracleTypeのメンバーとして宣言されます。

14.1.1 Vector用のJDBCの型

この項では、ベクトルをサポートするためにoracle.jdbc.OracleType列挙に追加されているSQLTypeの新しいインスタンスについて説明します。これらのインスタンスは、VECTORデータ型を表します。

ノート:

VECTORデータ型との間で変換できるJavaオブジェクト型は、各型のJava APIリファレンス・ドキュメントで指定されています。
VECTOR
OracleType.VECTORは、任意の型のベクトル、つまりアスタリスク(*)ワイルドカードを持つ型を表します。
VECTOR_INT8
OracleType.VECTOR_INT8は、INT8値のVectorを表します。
VECTOR_FLOAT32
OracleType.VECTOR_FLOAT32は、FLOAT32値のVectorを表します。
VECTOR_FLOAT64
OracleType.VECTOR_FLOAT64は、FLOAT64値のVectorを表します。

PreparedStatementにVector型パラメータがある場合は、これらの型コードを使用する必要があります。この型は、setObject(int, Object, SQLType)メソッドまたはsetObject(int, Object, int)メソッドの引数として指定できます。setObject(int, Object)メソッドをコールしてVectorパラメータを設定することはできません

14.1.2 ベクトル用のJDBCインタフェース

この項では、ベクトルをサポートするために追加または更新されたJDBCインタフェースについて説明します。

14.1.2.1 VectorMetaDataインタフェース

新しいインタフェースoracle.jdbc.VectorMetaDataには、ベクトルの列またはパラメータのメタデータが格納されます。

14.1.2.2 DatabaseMetaDataインタフェース

JDBCドライバは、表およびストアド・プロシージャのメタデータをjava.sql.DatabaseMetaDataインタフェースのインスタンスとして表します。

14.1.2.3 OracleResultSetMetaDataおよびOracleParameterMetaDataインタフェース

JDBCドライバは、列およびパラメータのメタデータをそれぞれjava.sql.ResultSetMetaDataおよびjava.sql.ParameterMetaDataインタフェースのインスタンスとして表します。Oracle JDBCドライバは、ResultSetMetaDataおよびParameterMetaDataインタフェースをそれぞれOracleResultSetMetaDataおよびOracleParameterMetaDataインタフェースで拡張します。

14.1.2.4 SparseArrayインタフェース

疎ベクトルは、通常は多数の次元を持つが、ゼロ以外の次元値はほとんどないベクトルです。JDBCアプリケーションの場合、Javaオブジェクトと疎ベクトルの間で変換を行う必要があります。Javaには疎データを表す組込みオブジェクトがないため、この目的のためにSparseArrayインタフェースが使用されます。

SparseArrayインタフェースには、ゼロ以外の値を格納するためにdoublefloatbytebooleanなどの特定のJava数値型を使用する次のサブインタフェースが含まれています:

  • SparseDoubleArray
  • SparseFloatArray
  • SparseArray
  • SparseDoubleArray

これらのサブインタフェースは、PreparedStatement.setObject(int, Object)メソッドのバインド値およびResultSet.getObject(int, Class)メソッドの戻り値として使用できます。

14.1.3 Vector用のJDBCメソッド

この項では、ベクトルをサポートするために追加または更新されたJDBCメソッドについて説明します。また、ベクトル・データに関する標準のJDBCメソッドの動作についても説明します。

getVectorMetaDataメソッド

getVectorMetaDataというメソッドが、OracleResultSetMetaDataおよびOracleParameterMetaDataインタフェースに追加されています。このメソッドは、ベクトルの列またはパラメータに対してoracle.jdbc.VectorMetaDataインタフェースのインスタンスを返します。これにより、アプリケーションが実行時にベクトル・データのサイズおよび型を識別できます。このメソッドは、ベクトルではない列またはパラメータに対してnullを返します。

getLengthメソッド

getLengthというメソッドは、ベクトルの列またはパラメータの値の数を返します。たとえば:

  • VECTOR(3, INT8)として宣言された列に対して、メソッドは3を返します。
  • 可変長(つまり、長さとしてアスタリスク(*)が指定されている)のベクトル列またはパラメータに対して、メソッドは-1を返します。たとえば、VECTOR(*, INT8)の場合などです。

getArrayClassメソッド

getArrayClassというメソッドは、配列オブジェクトのクラスを返します。このクラスは、ベクトルの列またはパラメータの変換に使用できます。たとえば、

  • 任意の型のベクトルである列またはパラメータの場合は、double[].classが返されます。たとえば、VECTOR(*, *)の場合などです。
  • INT8値のベクトルである列またはパラメータの場合は、byte[].classが返されます。
  • FLOAT32値のベクトルである列またはパラメータの場合は、float[].classが返されます。
  • FLOAT64値のベクトルである列またはパラメータの場合は、double[].classが返されます。

getTypeメソッド

getTypeメソッドは、ベクトルの列またはパラメータに対して次のOracleTypesのいずれかを返します。

  • 任意の型のベクトル(つまり、VECTOR(<length>,*)型)である列またはパラメータの場合は、OracleTypes.VECTORが返されます
  • INT8値のベクトルである列またはパラメータの場合は、OracleTypes.VECTOR_INT8が返されます。
  • FLOAT32値のベクトルである列またはパラメータの場合は、OracleTypes.VECTOR_FLOAT32が返されます。
  • FLOAT64値のベクトルである列またはパラメータの場合は、OracleTypes.VECTOR_FLOAT64が返されます。

getColumnsメソッド

getColumnsメソッドは、次の方法でベクトル列を取り出します。

  • getColumnsメソッドは、oracle.jdbc.OracleTypes.VECTOR (-105)int値をベクトル列のDATA_TYPEとして返します。
  • getColumnsメソッドは、"VECTOR"の文字列値をベクトル列のTYPE_NAMEとして返します。

getObject(int)またはgetObject(String)メソッド

java.sql.ResultSetインタフェースのgetObject(int)またはgetObject(String)メソッドをコールする場合、VECTORデータ型のデフォルトのマッピングはありません。ただし、oracle.jdbc.vectorDefaultGetObjectType接続プロパティを使用してデフォルトのマッピングを選択できます。

getPrecisionおよびgetScaleメソッド

getPrecisionおよびgetScaleメソッドは、ベクトルの列またはパラメータに対して0を返します。JDBC 4.3仕様では、VECTORデータ型に対するこれらのメソッドの正しい動作は定義されていません。戻り値0は、精度とスケールがデータ型に適用されないことを示します。他のすべてのメソッドは、JDBC 4.3仕様で指定されているとおりに動作します。

14.2 CallableStatementを使用したSQLからJavaへの変換

JDBCドライバは、プロシージャ型SQLコールをjava.sql.CallableStatementインタフェースのインスタンスとして表します。インタフェースは、outパラメータのSQL型を指定するregisterOutParameterメソッドを定義します。対応するgetObjectメソッドが定義され、登録されたSQL型がJavaオブジェクトに変換されます。

この項では、ベクトルをJavaオブジェクトに変換する際のgetObjectメソッドの変換を示しています。

ノート:

この項では、Class引数を受け入れるgetObjectメソッドの動作は示していません。これらのメソッドの動作は、「CallableStatmentおよびResultSetを使用したSQLからJavaへの変換」の項で示します。これらのメソッドは、registerOutParameterメソッドに登録されたSQL型の影響を受けません。

この項では、ベクトルで可能な拡大および縮小変換の動作について説明します。

OracleType.VECTOR登録

registerOutParameterメソッドは、OracleType.VECTORおよびOracleTypes.VECTORを認識します。この登録では、Class引数が指定されていない場合、getObjectメソッドによって次の変換が実行されます。

  • INT8値のベクトルはbyte[]に変換されます
  • FLOAT32値のベクトルはfloat[]に変換されます
  • FLOAT64値のベクトルはdouble[]に変換されます

OracleType.VECTOR_INT8登録

registerOutParameterメソッドは、OracleType.VECTOR_INT8およびOracleTypes.VECTOR_INT8を認識します。この登録では、Class引数が指定されていない場合、getObjectメソッドによって次の変換が実行されます。

  • INT8値のベクトルはbyte[]に変換されます。この場合、その他の変換は発生しません。
  • FLOAT32値のベクトルはbyte[]に変換され、byteからfloatへの拡大変換が発生します。

OracleType.VECTOR_FLOAT32登録

registerOutParameterメソッドは、OracleType.VECTOR_FLOAT32およびOracleTypes.VECTOR_FLOAT31を認識します。この登録では、Class引数が指定されていない場合、getObjectメソッドによって次の変換が実行されます。

  • INT8値のベクトルはfloat[]に変換され、byteからfloatへの拡大変換が発生します。
  • FLOAT32値のベクトルはfloat[]に変換されます。この場合、その他の変換は発生しません。
  • FLOAT64値のベクトルはfloat[]に変換され、doubleからfloatへの縮小変換が発生します。

OracleType.VECTOR_FLOAT64登録

registerOutParameterメソッドは、OracleType.VECTOR_FLOAT64およびOracleTypes.VECTOR_FLOAT64を認識します。この登録では、Class引数が指定されていない場合、getObjectメソッドによって次の変換が実行されます。

  • INT8値のベクトルはdouble[]に変換され、byteからdoubleへの拡大変換が発生します。
  • FLOAT32値のベクトルはdouble[]に変換され、floatからdoubleへの拡大変換が発生します。
  • FLOAT64値のベクトルはdouble[]に変換されます。この場合、その他の変換は発生しません。

14.3 CallableStatmentおよびResultSetを使用したSQLからJavaへの変換

JDBCドライバは、outパラメータおよび列の値をそれぞれjava.sql.CallableStatementおよびjava.sql.ResultSetインタフェースのインスタンスとして表します。どちらのインタフェースも、SQL型をJavaオブジェクトに変換するgetObjectメソッドを定義します。

この項では、ベクトルのパラメータおよび列をJavaオブジェクトに変換する際のgetObjectメソッドの動作について説明します。

この項では、ベクトルで可能な拡大および縮小変換の動作について説明します。

デフォルトの変換

ResultSetインタフェースのgetObject(int)およびgetObject(String)メソッドは、ベクトル列の変換をサポートしていません。JDBC 4.3仕様では、どのクラスのJavaオブジェクトもベクトルのデフォルトの変換として指定されていません。

CallableStatementインタフェースのgetObject(int)およびgetObject(String)メソッドは、ベクトル列の変換をサポートしています。詳細は、「CallableStatementを使用したSQLからJavaへの変換」を参照してください。

boolean[]変換

getObjectメソッドは、boolean[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはboolean[]に変換されます。0の値はfalseに変換され、0以外の値はtrueに変換されます。
  • FLOAT32値のベクトルはboolean[]に変換されます。0.0の値はfalseに変換され、0.0以外の値はtrueに変換されます。
  • FLOAT64値のベクトルはboolean[]に変換されます。0.0の値はfalseに変換され、0.0以外の値はtrueに変換されます。

byte[]変換

getObjectメソッドは、byte[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはbyte[]に変換されます。この場合、その他の変換は実行されません。
  • FLOAT32値のベクトルはbyte[]に変換され、floatからbyteへの縮小変換が発生します。
  • FLOAT64値のベクトルはbyte[]に変換され、doubleからbyteへの縮小変換が発生します。

short[]変換

getObjectメソッドは、short[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはshort[]に変換され、byteからshortへの拡大変換が発生します。
  • FLOAT32値のベクトルはshort[]に変換され、floatからshortへの縮小変換が発生します。
  • FLOAT64値のベクトルはshort[]に変換され、doubleからshortへの縮小変換が発生します。

int[]変換

getObjectメソッドは、int[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはint[]に変換され、byteからintへの拡大変換が発生します。
  • FLOAT32値のベクトルはint[]に変換され、floatからintへの縮小変換が発生します。
  • FLOAT64値のベクトルはbyte[]に変換され、doubleからintへの縮小変換が発生します。

long[]変換

getObjectメソッドは、long[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはlong[]に変換され、byteからlongへの拡大変換が発生します。
  • FLOAT32値のベクトルはlong[]に変換され、floatからlongへの縮小変換が発生します。
  • FLOAT64値のベクトルはlong[]に変換され、doubleからlongへの縮小変換が発生します。

float[]変換

getObjectメソッドは、float[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはfloat[]に変換され、byteからintへの拡大変換が発生します。
  • FLOAT32値のベクトルはfloat[]に変換されます。この場合、その他の変換は実行されません。
  • FLOAT64値のベクトルはfloat[]に変換され、doubleからfloatへの縮小変換が発生します。

double[]変換

getObjectメソッドは、double[].classをターゲットJava型として認識します。次の変換が実行されます。

  • INT8値のベクトルはdouble[]に変換され、byteからdoubleへの拡大変換が発生します。
  • FLOAT32値のベクトルはdouble[]に変換され、floatからdoubleへの拡大変換が発生します。
  • FLOAT64値のベクトルはdouble[]に変換され、doubleからfloatへの縮小変換が発生します。

14.4 PreparedStatementおよびCallableStatementを使用したJavaからSQLへの変換

JDBCドライバは、SQLコマンドをjava.sql.PreparedStatementおよびjava.sql.CallableStatementインタフェースのインスタンスとして表します。これらのインタフェースは、JavaオブジェクトをSQL型に変換するsetObjectメソッドを定義します。この項では、Javaオブジェクトをベクトルに変換する際のsetObjectメソッドの動作について説明します。

この項では、ベクトルで可能な拡大および縮小変換の動作について説明します。

デフォルトの変換

setObject(int, Object)メソッドでは、ベクトルへの変換はサポートされていません。JDBC 4.3仕様では、どのクラスのJavaオブジェクトについても、ベクトルがデフォルトの変換として指定されていません。

OracleType.VECTOR変換

setObjectメソッドは、OracleType.VECTORおよびOracleTypes.VECTORをターゲットSQL型として認識します。この型では、次の変換がサポートされています。

  • byte[]INT8値のベクトルに変換されます
  • float[]FLOAT32値のベクトルに変換されます
  • double[]FLOAT64値のベクトルに変換されます。

OracleType.VECTOR_INT8変換

setObjectメソッドは、OracleType.VECTOR_INT8およびOracleTypes.VECTOR_INT8をターゲットSQL型として認識します。このターゲットSQL型では、次の変換がサポートされています。

  • boolean[]INT8値のベクトルに変換されますfalseのブール値は0に変換され、trueのブール値は1に変換されます。
  • byte[]INT8値のベクトルに変換されます。この場合、その他の変換は発生しません。
  • short[]INT8値のベクトルに変換され、shortからbyteへの縮小変換が発生します。
  • int[]INT8値のベクトルに変換され、intからbyteへの縮小変換が発生します。
  • long[]INT8値のベクトルに変換され、longからbyteへの縮小変換が発生します。
  • float[]INT8値のベクトルに変換され、floatからbyteへの縮小変換が発生します。
  • double[]INT8値のベクトルに変換され、doubleからbyteへの縮小変換が発生します。

OracleType.VECTOR_FLOAT32変換

setObjectメソッドは、OracleType.VECTOR_FLOAT32およびOracleTypes.VECTOR_FLOAT32をターゲットSQL型として認識します。このターゲットSQL型では、次の変換がサポートされています。

  • boolean[]FLOAT32値のベクトルに変換されますfalseのブール値は0.0に変換され、trueのブール値は1.0に変換されます。
  • byte[]FLOAT32値のベクトルに変換され、byteからfloatへの拡大変換が発生します。
  • short[]FLOAT32値のベクトルに変換され、shortからfloatへの拡大変換が発生します。
  • int[]FLOAT32値のベクトルに変換され、intからfloatへの拡大変換が発生します。
  • long[]FLOAT32値のベクトルに変換され、longからfloatへの拡大変換が発生します。
  • float[]FLOAT32値のベクトルに変換されます。この場合、その他の変換は発生しません。
  • double[]FLOAT32値のベクトルに変換され、doubleからfloatへの縮小変換が発生します。

OracleType.VECTOR_FLOAT64変換

setObjectメソッドは、OracleType.VECTOR_FLOAT64およびOracleTypes.VECTOR_FLOAT64をターゲットSQL型として認識します。このターゲットSQL型では、次の変換がサポートされています。

  • boolean[]FLOAT64値のベクトルに変換されます。ここで、falseのブール値は0.0に変換され、trueのブール値は1.0に変換されます。
  • byte[]FLOAT64値のベクトルに変換され、byteからdoubleへの拡大変換が発生します。
  • short[]FLOAT64値のベクトルに変換され、shortからdoubleへの拡大変換が発生します。
  • int[]FLOAT64値のベクトルに変換され、intからdoubleへの拡大変換が発生します。
  • long[]FLOAT64値のベクトルに変換され、longからdoubleへの拡大変換が発生します。
  • float[]FLOAT64値のベクトルに変換され、floatからdoubleへの拡大変換が発生します。
  • double[]FLOAT64値のベクトルに変換されます。この場合、その他の変換は発生しません。

14.5 VECTOR Datumクラス

oracle.sqlパッケージは、各Oracle SQLデータ型を表すサブクラスを持つDatumクラスを定義します。たとえば、oracle.sql.NUMBERサブクラスはNUMBERデータ型の値を表し、oracle.sql.TIMESTAMPTIMESTAMPデータ型の値を表します。

ベクトル列の値を表す新しいサブクラスoracle.sql.VECTORDatumクラスに追加されています。VECTORクラスは、JavaオブジェクトとOracleのベクトルのバイナリ・エンコーディング間の変換をサポートします。

Javaオブジェクトからの変換

VECTORクラスは、ベクトルのインスタンスを作成するファクトリ・メソッドを定義します。これらのファクトリ・メソッドは、次の方法でJavaオブジェクトをベクトルのバイナリ・エンコーディングに変換します。

  • ofFloat64Values(Object)メソッドは、JavaオブジェクトをFLOAT64値のベクトルに変換します。
  • ofFloat32Values(Object)メソッドは、JavaオブジェクトをFLOAT32値のベクトルに変換します。
  • ofInt8Values(Object)メソッドは、JavaオブジェクトをINT8値のベクトルに変換します。

Javaオブジェクトへの変換

VECTORクラスは、ベクトルのJavaオブジェクト表現を返すインスタンス・メソッドを定義します。これらのインスタンス・メソッドは、次の方法でベクトルのバイナリ・エンコーディングをJavaオブジェクトに変換します。

  • toBooleanArray()メソッドは、ベクトルをboolean値の配列に変換します
  • toByteArray()メソッドは、ベクトルをbyte値の配列に変換します
  • toShortArray()メソッドは、ベクトルをshort値の配列に変換します
  • toIntArray()メソッドは、ベクトルをint値の配列に変換します
  • toLongArray()メソッドは、ベクトルをlong値の配列に変換します
  • toFloatArray()メソッドは、ベクトルをfloat値の配列に変換します
  • toDoubleArray()メソッドは、ベクトルをdouble値の配列に変換します

14.6 以前のJDBCドライバとの下位互換性

以前のリリースのOracle JDBCは、Oracle Database 23aiに接続できます。これらのJDBCビルドには、VECTORデータ型に対する組込みサポートはありませんが、VARCHARおよびCLOBデータ型がサポートされているため、アプリケーションではこれらの型をベクトル列に対するDML操作および問合せ操作に使用できます。

JDBCでは、VARCHARを使用したStringの変換、CLOBを使用したjava.sql.Clobの変換がサポートされています。これらの変換は、Oracle Database 19c、21cおよび23aiリリースで一貫した動作になっています。

次のコード例にこれらの変換を示します。ここでは、Stringおよびjava.sql.ClobPreparedStatement.setObjectメソッドに渡されます。CLOBからStringへの変換は、String.classメソッドでResultSet.getObject(int, Class)メソッドをコールすることで示されます。

import oracle.jdbc.OracleStatement;
 
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Arrays;
import java.util.Random;
 
/**
 * This example can be run with 19.x releases of Oracle JDBC. It uses String to
 * represent VECTOR data, which may be suitable for database tools.
 */
public class VectorStringTest {
 
  public static void main(String[] args) throws SQLException {
 
    try (
      Connection connection =
        DriverManager.getConnection("jdbc:oracle:thin:@test");
      Table table = new Table(connection);
      PreparedStatement insert = connection.prepareStatement(
        "INSERT INTO vector_test(id, value) VALUES (?, ?)");
      PreparedStatement query = connection.prepareStatement(
        "SELECT id, value FROM vector_test ORDER BY id")) {
 
      // Toy example to show the VARCHAR literal syntax of a VECTOR: A comma
      // separated numbers enclosed in square brackets.
      String vectorLiteral = "[0.1, 0.2, 0.3]";
      insert.setString(1, "0");
      insert.setString(2, vectorLiteral);
      System.out.println("Inserting VECTOR (VARCHAR):\n\t" + vectorLiteral);
      insert.executeUpdate();
 
      // Generate a Vector of 256 dimensions, each having many decimal point
      // digits. Arrays.toString(double[]) conveniently generates the Vector
      // literal syntax, so it may be used to convert the Vector to String.
      double[] vector = getVector(256);
      String vectorString = Arrays.toString(vector);
      insert.setObject(1, "1");
      insert.setObject(2, vectorString);
      System.out.println("Inserting VECTOR (VARCHAR):\n\t" + vectorString);
      insert.executeUpdate();
 
      // If the String is longer than 32k characters, then it must be converted
      // to a CLOB (32k is the maximum length of a VARCHAR).
      // This example results in:
      //   ORA-42552: VECTOR() library processing error 'LVECTOR_ERR_INPUT_NAN_OR_INF' in 'qvcCons:lvector_from_oratext'.
      // The 2048 length is commented out for this reason. A 256 length is used
      // just to demonstrate the conversion to CLOB.
      // double[] largeVector = getVector(2048);
      double[] largeVector = getVector(256);
      Clob vectorClob = connection.createClob();
      try {
        String largeVectorString = Arrays.toString(largeVector);
        vectorClob.setString(1L, largeVectorString);
        insert.setString(1, "2");
        insert.setObject(2, vectorClob);
        System.out.println("Inserting VECTOR (CLOB):\n\t" + largeVectorString);
        insert.executeUpdate();
      }
      finally {
        vectorClob.free();
      }
 
      // Query the VECTOR column. For a 19c JDBC client, the database sends the
      // VECTOR as a CLOB. For a 23ai JDBC client, it sends it as the VECTOR binary
      // encoding. When the getString method has JDBC convert the VECTOR, both
      // client versions should return the same text value.
      try (ResultSet resultSet = query.executeQuery()) {
        ResultSetMetaData metaData = resultSet.getMetaData();
        while (resultSet.next()) {
          System.out.println("Queried VECTOR:");
          System.out.printf(
            "\t%s (%s) : %s%n",
            metaData.getColumnName(1),
            metaData.getColumnTypeName(1),
            resultSet.getString(1));
          System.out.printf(
            "\t%s (%s) : %s%n",
            metaData.getColumnName(2),
            metaData.getColumnTypeName(2),
            resultSet.getString(2));
        }
      }
 
      // Applications can request that the database always sends the VECTOR
      // as a CLOB. The defineColumnType method is used to specify the CLOB
      // type.
      System.out.println("\nQuerying VECTOR as CLOB");
      query.unwrap(OracleStatement.class)
        .defineColumnType(2, Types.CLOB);
      try (ResultSet resultSet = query.executeQuery()) {
        ResultSetMetaData metaData = resultSet.getMetaData();
        while (resultSet.next()) {
          System.out.println("Queried VECTOR:");
          System.out.printf(
            "\t%s (%s) : %s%n",
            metaData.getColumnName(1),
            metaData.getColumnTypeName(1),
            resultSet.getString(1));
          System.out.printf(
            "\t%s (%s) : %s%n",
            metaData.getColumnName(2),
            metaData.getColumnTypeName(2),
            resultSet.getString(2));
        }
      }
 
    }
  }
 
  static double[] getVector(int length) {
    return new Random(0).doubles()
      .limit(length)
      .toArray();
  }
 
  static class Table implements AutoCloseable {
 
    private final Connection connection;
 
    Table(Connection connection) throws SQLException {
      try (Statement statement = connection.createStatement()) {
        statement.addBatch("DROP TABLE IF EXISTS vector_test");
        statement.addBatch(
          "CREATE TABLE vector_test" +
            " (id NUMBER PRIMARY KEY, value VECTOR(*,*))");
        statement.executeBatch();
      }
      this.connection = connection;
    }
 
    @Override
    public void close() throws SQLException {
      try (Statement statement = connection.createStatement()) {
        statement.execute("DROP TABLE IF EXISTS vector_test");
      }
    }
  }
}