2929import java .util .HashMap ;
3030import java .util .List ;
3131import java .util .Map ;
32+ import java .util .concurrent .locks .Lock ;
33+ import java .util .concurrent .locks .ReentrantLock ;
3234import java .util .stream .Collectors ;
3335import java .util .stream .Stream ;
3436import javax .annotation .Nonnull ;
37+ import javax .annotation .concurrent .GuardedBy ;
3538import javax .sql .DataSource ;
3639import org .slf4j .Logger ;
3740import org .slf4j .LoggerFactory ;
@@ -54,6 +57,8 @@ public class JDBCVectorStoreQueryProvider
5457 private final String collectionsTable ;
5558 private final String prefixForCollectionTables ;
5659
60+ private final Object dbCreationLock = new Object ();
61+
5762 @ SuppressFBWarnings ("EI_EXPOSE_REP2" ) // DataSource is not exposed
5863 protected JDBCVectorStoreQueryProvider (
5964 @ Nonnull DataSource dataSource ,
@@ -89,12 +94,13 @@ protected JDBCVectorStoreQueryProvider(
8994
9095 /**
9196 * Creates a new instance of the JDBCVectorStoreQueryProvider class.
92- * @param dataSource the data source
93- * @param collectionsTable the collections table
97+ *
98+ * @param dataSource the data source
99+ * @param collectionsTable the collections table
94100 * @param prefixForCollectionTables the prefix for collection tables
95- * @param supportedKeyTypes the supported key types
96- * @param supportedDataTypes the supported data types
97- * @param supportedVectorTypes the supported vector types
101+ * @param supportedKeyTypes the supported key types
102+ * @param supportedDataTypes the supported data types
103+ * @param supportedVectorTypes the supported vector types
98104 */
99105 public JDBCVectorStoreQueryProvider (
100106 @ SuppressFBWarnings ("EI_EXPOSE_REP2" ) @ Nonnull DataSource dataSource ,
@@ -276,48 +282,57 @@ public boolean collectionExists(String collectionName) {
276282 */
277283 @ Override
278284 @ SuppressFBWarnings ("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" )
285+ @ GuardedBy ("dbCreationLock" )
279286 // SQL query is generated dynamically with valid identifiers
280287 public void createCollection (String collectionName ,
281288 VectorStoreRecordDefinition recordDefinition ) {
282289
283- // No approximate search is supported in JDBCVectorStoreQueryProvider
284- if (recordDefinition .getVectorFields ().stream ()
285- .anyMatch (
286- field -> field .getIndexKind () != null && field .getIndexKind () != IndexKind .FLAT
287- && field .getIndexKind () != IndexKind .UNDEFINED )) {
288- LOGGER
289- .warn (String .format ("Indexes are not supported in %s. Ignoring indexKind property." ,
290- this .getClass ().getName ()));
291- }
292-
293- String createStorageTable = formatQuery ("CREATE TABLE IF NOT EXISTS %s ("
294- + "%s VARCHAR(255) PRIMARY KEY, "
295- + "%s, "
296- + "%s);" ,
297- getCollectionTableName (collectionName ),
298- getKeyColumnName (recordDefinition .getKeyField ()),
299- getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getDataFields ()),
300- getSupportedDataTypes ()),
301- getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getVectorFields ()),
302- getSupportedVectorTypes ()));
290+ synchronized (dbCreationLock ) {
291+ // No approximate search is supported in JDBCVectorStoreQueryProvider
292+ if (recordDefinition .getVectorFields ().stream ()
293+ .anyMatch (
294+ field -> field .getIndexKind () != null && field .getIndexKind () != IndexKind .FLAT
295+ && field .getIndexKind () != IndexKind .UNDEFINED )) {
296+ LOGGER
297+ .warn (String .format (
298+ "Indexes are not supported in %s. Ignoring indexKind property." ,
299+ this .getClass ().getName ()));
300+ }
303301
304- String insertCollectionQuery = formatQuery ("INSERT INTO %s (collectionId) VALUES (?)" ,
305- validateSQLidentifier (collectionsTable ));
302+ String createStorageTable = formatQuery ("CREATE TABLE IF NOT EXISTS %s ("
303+ + "%s VARCHAR(255) PRIMARY KEY, "
304+ + "%s, "
305+ + "%s);" ,
306+ getCollectionTableName (collectionName ),
307+ getKeyColumnName (recordDefinition .getKeyField ()),
308+ getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getDataFields ()),
309+ getSupportedDataTypes ()),
310+ getColumnNamesAndTypes (new ArrayList <>(recordDefinition .getVectorFields ()),
311+ getSupportedVectorTypes ()));
312+
313+ String insertCollectionQuery = this .getInsertCollectionQuery (collectionsTable );
314+
315+ try (Connection connection = dataSource .getConnection ();
316+ PreparedStatement createTable = connection .prepareStatement (createStorageTable )) {
317+ createTable .execute ();
318+ } catch (SQLException e ) {
319+ throw new SKException ("Failed to create collection" , e );
320+ }
306321
307- try (Connection connection = dataSource .getConnection ();
308- PreparedStatement createTable = connection .prepareStatement (createStorageTable )) {
309- createTable .execute ();
310- } catch (SQLException e ) {
311- throw new SKException ("Failed to create collection" , e );
322+ try (Connection connection = dataSource .getConnection ();
323+ PreparedStatement insert = connection .prepareStatement (insertCollectionQuery )) {
324+ insert .setObject (1 , collectionName );
325+ insert .execute ();
326+ } catch (SQLException e ) {
327+ throw new SKException ("Failed to insert collection" , e );
328+ }
312329 }
330+ }
313331
314- try (Connection connection = dataSource .getConnection ();
315- PreparedStatement insert = connection .prepareStatement (insertCollectionQuery )) {
316- insert .setObject (1 , collectionName );
317- insert .execute ();
318- } catch (SQLException e ) {
319- throw new SKException ("Failed to insert collection" , e );
320- }
332+ protected String getInsertCollectionQuery (String collectionsTable ) {
333+ return formatQuery (
334+ "INSERT IGNORE INTO %s (collectionId) VALUES (?)" ,
335+ validateSQLidentifier (collectionsTable ));
321336 }
322337
323338 /**
@@ -327,26 +342,29 @@ public void createCollection(String collectionName,
327342 * @throws SKException if an error occurs while deleting the collection
328343 */
329344 @ Override
345+ @ GuardedBy ("dbCreationLock" )
330346 public void deleteCollection (String collectionName ) {
331- String deleteCollectionOperation = formatQuery ("DELETE FROM %s WHERE collectionId = ?" ,
332- validateSQLidentifier (collectionsTable ));
333- String dropTableOperation = formatQuery ("DROP TABLE %s" ,
334- getCollectionTableName (collectionName ));
335-
336- try (Connection connection = dataSource .getConnection ();
337- PreparedStatement deleteCollection = connection
338- .prepareStatement (deleteCollectionOperation )) {
339- deleteCollection .setObject (1 , collectionName );
340- deleteCollection .execute ();
341- } catch (SQLException e ) {
342- throw new SKException ("Failed to delete collection" , e );
343- }
347+ synchronized (dbCreationLock ) {
348+ String deleteCollectionOperation = formatQuery ("DELETE FROM %s WHERE collectionId = ?" ,
349+ validateSQLidentifier (collectionsTable ));
350+ String dropTableOperation = formatQuery ("DROP TABLE %s" ,
351+ getCollectionTableName (collectionName ));
352+
353+ try (Connection connection = dataSource .getConnection ();
354+ PreparedStatement deleteCollection = connection
355+ .prepareStatement (deleteCollectionOperation )) {
356+ deleteCollection .setObject (1 , collectionName );
357+ deleteCollection .execute ();
358+ } catch (SQLException e ) {
359+ throw new SKException ("Failed to delete collection" , e );
360+ }
344361
345- try (Connection connection = dataSource .getConnection ();
346- PreparedStatement dropTable = connection .prepareStatement (dropTableOperation )) {
347- dropTable .execute ();
348- } catch (SQLException e ) {
349- throw new SKException ("Failed to drop table" , e );
362+ try (Connection connection = dataSource .getConnection ();
363+ PreparedStatement dropTable = connection .prepareStatement (dropTableOperation )) {
364+ dropTable .execute ();
365+ } catch (SQLException e ) {
366+ throw new SKException ("Failed to drop table" , e );
367+ }
350368 }
351369 }
352370
@@ -518,8 +536,8 @@ protected <Record> List<Record> getRecordsWithFilter(String collectionName,
518536 *
519537 * @param <Record> the record type
520538 * @param collectionName the collection name
521- * @param vector the vector to search with
522- * @param options the search options
539+ * @param vector the vector to search with
540+ * @param options the search options
523541 * @param recordDefinition the record definition
524542 * @param mapper the mapper, responsible for mapping the result set to the record
525543 * type.
@@ -622,8 +640,8 @@ public String getFilter(VectorSearchFilter filter,
622640 }
623641
624642 /**
625- * Gets the filter parameters for the given vector search filter to associate with the filter string
626- * generated by the getFilter method.
643+ * Gets the filter parameters for the given vector search filter to associate with the filter
644+ * string generated by the getFilter method.
627645 *
628646 * @param filter The filter to get the filter parameters for.
629647 * @return The filter parameters.
0 commit comments