in c/driver/sqlite/statement_reader.c [273:453]
AdbcStatusCode AdbcSqliteBinderBindNext(struct AdbcSqliteBinder* binder, sqlite3* conn,
sqlite3_stmt* stmt, char* finished,
struct AdbcError* error) {
struct ArrowError arrow_error = {0};
int status = 0;
while (!binder->array.release || binder->next_row >= binder->array.length) {
if (binder->array.release) {
ArrowArrayViewReset(&binder->batch);
binder->array.release(&binder->array);
status =
ArrowArrayViewInitFromSchema(&binder->batch, &binder->schema, &arrow_error);
if (status != 0) {
SetError(error, "Failed to initialize array view: (%d) %s: %s", status,
strerror(status), arrow_error.message);
return ADBC_STATUS_INTERNAL;
}
}
status = binder->params.get_next(&binder->params, &binder->array);
if (status != 0) {
const char* message = binder->params.get_last_error(&binder->params);
if (!message) message = "(unknown error)";
SetError(error, "Failed to get next parameter batch: (%d) %s: %s", status,
strerror(status), message);
return ADBC_STATUS_IO;
}
if (!binder->array.release) {
*finished = 1;
AdbcSqliteBinderRelease(binder);
return ADBC_STATUS_OK;
}
status = ArrowArrayViewSetArray(&binder->batch, &binder->array, &arrow_error);
if (status != 0) {
SetError(error, "Failed to initialize array view: (%d) %s: %s", status,
strerror(status), arrow_error.message);
return ADBC_STATUS_INTERNAL;
}
binder->next_row = 0;
}
if (sqlite3_reset(stmt) != SQLITE_OK) {
SetError(error, "Failed to reset statement: %s", sqlite3_errmsg(conn));
return ADBC_STATUS_INTERNAL;
}
if (sqlite3_clear_bindings(stmt) != SQLITE_OK) {
SetError(error, "Failed to clear statement bindings: %s", sqlite3_errmsg(conn));
return ADBC_STATUS_INTERNAL;
}
for (int col = 0; col < binder->schema.n_children; col++) {
if (ArrowArrayViewIsNull(binder->batch.children[col], binder->next_row)) {
status = sqlite3_bind_null(stmt, col + 1);
} else {
switch (binder->types[col]) {
case NANOARROW_TYPE_BINARY:
case NANOARROW_TYPE_LARGE_BINARY:
case NANOARROW_TYPE_FIXED_SIZE_BINARY:
case NANOARROW_TYPE_BINARY_VIEW: {
struct ArrowBufferView value =
ArrowArrayViewGetBytesUnsafe(binder->batch.children[col], binder->next_row);
status = sqlite3_bind_blob(stmt, col + 1, value.data.as_char,
(int)value.size_bytes, SQLITE_STATIC);
break;
}
case NANOARROW_TYPE_BOOL:
case NANOARROW_TYPE_UINT8:
case NANOARROW_TYPE_UINT16:
case NANOARROW_TYPE_UINT32:
case NANOARROW_TYPE_UINT64: {
uint64_t value =
ArrowArrayViewGetUIntUnsafe(binder->batch.children[col], binder->next_row);
if (value > INT64_MAX) {
SetError(error,
"Column %d has unsigned integer value %" PRIu64
"out of range of int64_t",
col, value);
return ADBC_STATUS_INVALID_ARGUMENT;
}
status = sqlite3_bind_int64(stmt, col + 1, (int64_t)value);
break;
}
case NANOARROW_TYPE_INT8:
case NANOARROW_TYPE_INT16:
case NANOARROW_TYPE_INT32:
case NANOARROW_TYPE_INT64: {
int64_t value =
ArrowArrayViewGetIntUnsafe(binder->batch.children[col], binder->next_row);
status = sqlite3_bind_int64(stmt, col + 1, value);
break;
}
case NANOARROW_TYPE_HALF_FLOAT:
case NANOARROW_TYPE_FLOAT:
case NANOARROW_TYPE_DOUBLE: {
double value = ArrowArrayViewGetDoubleUnsafe(binder->batch.children[col],
binder->next_row);
status = sqlite3_bind_double(stmt, col + 1, value);
break;
}
case NANOARROW_TYPE_STRING:
case NANOARROW_TYPE_LARGE_STRING:
case NANOARROW_TYPE_STRING_VIEW: {
struct ArrowBufferView value =
ArrowArrayViewGetBytesUnsafe(binder->batch.children[col], binder->next_row);
status = sqlite3_bind_text(stmt, col + 1, value.data.as_char,
(int)value.size_bytes, SQLITE_STATIC);
break;
}
case NANOARROW_TYPE_DICTIONARY: {
int64_t value_index =
ArrowArrayViewGetIntUnsafe(binder->batch.children[col], binder->next_row);
if (ArrowArrayViewIsNull(binder->batch.children[col]->dictionary,
value_index)) {
status = sqlite3_bind_null(stmt, col + 1);
} else {
struct ArrowBufferView value = ArrowArrayViewGetBytesUnsafe(
binder->batch.children[col]->dictionary, value_index);
status = sqlite3_bind_text(stmt, col + 1, value.data.as_char,
(int)value.size_bytes, SQLITE_STATIC);
}
break;
}
case NANOARROW_TYPE_DATE32: {
int64_t value =
ArrowArrayViewGetIntUnsafe(binder->batch.children[col], binder->next_row);
char* tsstr;
if ((value > INT32_MAX) || (value < INT32_MIN)) {
SetError(error,
"Column %d has value %" PRId64
" which exceeds the expected range "
"for an Arrow DATE32 type",
col, value);
return ADBC_STATUS_INVALID_DATA;
}
RAISE_ADBC(ArrowDate32ToIsoString((int32_t)value, &tsstr, error));
// SQLITE_TRANSIENT ensures the value is copied during bind
status = sqlite3_bind_text(stmt, col + 1, tsstr, (int)strlen(tsstr),
SQLITE_TRANSIENT);
free(tsstr);
break;
}
case NANOARROW_TYPE_TIMESTAMP: {
struct ArrowSchemaView bind_schema_view;
RAISE_NA(ArrowSchemaViewInit(&bind_schema_view, binder->schema.children[col],
&arrow_error));
enum ArrowTimeUnit unit = bind_schema_view.time_unit;
int64_t value =
ArrowArrayViewGetIntUnsafe(binder->batch.children[col], binder->next_row);
char* tsstr;
RAISE_ADBC(ArrowTimestampToIsoString(value, unit, &tsstr, error));
// SQLITE_TRANSIENT ensures the value is copied during bind
status = sqlite3_bind_text(stmt, col + 1, tsstr, (int)strlen(tsstr),
SQLITE_TRANSIENT);
free((char*)tsstr);
break;
}
default:
SetError(error, "Column %d has unsupported type %s", col,
ArrowTypeString(binder->types[col]));
return ADBC_STATUS_NOT_IMPLEMENTED;
}
}
if (status != SQLITE_OK) {
SetError(error, "Failed to clear statement bindings: %s", sqlite3_errmsg(conn));
return ADBC_STATUS_INTERNAL;
}
}
binder->next_row++;
*finished = 0;
return ADBC_STATUS_OK;
}