AdbcStatusCode AdbcSqliteBinderBindNext()

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;
}