並行性與執行緒安全

一般來說,物件允許多個執行緒的序列化存取:一個執行緒可以進行呼叫,一旦完成,另一個執行緒可以進行呼叫。它們不允許多個執行緒的並行存取。

稍微相關的是從單個執行緒或多個執行緒重疊/並行執行多步驟操作的問題。例如,可以從同一個 AdbcConnection 建立兩個 AdbcStatement 物件

struct AdbcStatement stmt1;
struct AdbcStatement stmt2;

struct ArrowArrayStream out1;
struct ArrowArrayStream out2;

/* Ignoring error handling for brevity */
AdbcStatementNew(&conn, &stmt1, NULL);
AdbcStatementNew(&conn, &stmt2, NULL);
AdbcStatementSetSqlQuery(&stmt1, "SELECT * FROM a", NULL);
AdbcStatementSetSqlQuery(&stmt2, "SELECT * FROM b", NULL);

AdbcStatementExecuteQuery(&stmt1, &out1, NULL, NULL);
AdbcStatementExecuteQuery(&stmt2, &out2, NULL, NULL);
/* What happens to the result set of stmt1? */

如果客戶端應用程式在 stmt1 上呼叫 AdbcStatementExecuteQuery(),然後在 stmt2 上呼叫,而沒有讀取 stmt1 的結果集,會發生什麼事?一些現有的客戶端函式庫/協議,例如 libpq,不支援從單個連線並行執行查詢。因此,驅動程式必須 1) 在第一次 Execute 期間將所有結果緩衝到記憶體中(或以其他方式允許程式繼續讀取第一個結果集),2) 在第二次 Execute 時發出錯誤,或 3) 在第二次 Execute 時使第一個語句的結果集無效。

在這種情況下,ADBC 允許驅動程式選擇 1) 或 2)。如果可能且合理,驅動程式應允許並行執行,無論是因為底層協議是為此設計的,還是通過緩衝結果集。但是,如果無法支援,則允許驅動程式報錯。

另一個用例是擁有單個語句,但多次執行它並並行讀取結果集。例如,客戶端可能希望使用預備語句來執行此操作

/* Ignoring error handling for brevity */
struct AdbcStatement stmt;
AdbcStatementNew(&conn, &stmt, NULL);
AdbcStatementSetSqlQuery(&stmt, "SELECT * FROM a WHERE foo > ?", NULL);
AdbcStatementPrepare(&stmt, NULL);

struct ArrowArrayStream stream;
AdbcStatementBind(&stmt, &array1, &schema, NULL);
AdbcStatementExecuteQuery(&stmt, &stream, NULL, NULL);
/* Spawn a thread to process `stream` */

struct ArrowArrayStream stream2;
AdbcStatementBind(&stmt, &array2, &schema, NULL);
AdbcStatementExecuteQuery(&stmt, &stream2, NULL, NULL);
/* What happens to `stream` here? */

ADBC 選擇不允許這樣做(具體來說:第二次呼叫 Execute 必須使第一次呼叫的結果集無效),這與現有的 API 一致,這些 API 通常不支援以這種方式「重疊」使用單個預備語句。