blob: 7579234914af50c0c7a0bd12d531a04e72fbaaa9 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::ffi::{c_char, c_void};
use std::ptr;
use std::sync::Arc;
use arrow_array::ffi::{from_ffi, FFI_ArrowArray, FFI_ArrowSchema};
use arrow_array::{Array, RecordBatch, RecordBatchOptions, StructArray};
use arrow_schema::{DataType as ArrowDataType, Schema as ArrowSchema};
use paimon::table::Table;
use crate::error::{check_non_null, paimon_error, validate_cstr, PaimonErrorCode};
use crate::result::{
paimon_result_prepare_commit, paimon_result_table_commit, paimon_result_table_write,
paimon_result_write_builder,
};
use crate::runtime;
use crate::types::*;
// ======================= WriteBuilder ===============================
unsafe fn new_write_builder(
table: *const paimon_table,
commit_user: Option<String>,
) -> paimon_result_write_builder {
if let Err(e) = check_non_null(table, "table") {
return paimon_result_write_builder {
write_builder: ptr::null_mut(),
error: e,
};
}
let table_ref = &*((*table).inner as *const Table);
let builder = table_ref.new_write_builder();
let commit_user = match commit_user {
Some(commit_user) => match builder.with_commit_user(commit_user) {
Ok(builder) => builder.commit_user().to_string(),
Err(e) => {
return paimon_result_write_builder {
write_builder: ptr::null_mut(),
error: paimon_error::from_paimon(e),
}
}
},
None => builder.commit_user().to_string(),
};
let state = WriteBuilderState {
table: table_ref.clone(),
commit_user,
overwrite: false,
};
let inner = Box::into_raw(Box::new(state)) as *mut c_void;
paimon_result_write_builder {
write_builder: Box::into_raw(Box::new(paimon_write_builder { inner })),
error: ptr::null_mut(),
}
}
/// Create a new WriteBuilder from a Table.
///
/// The returned WriteBuilder holds a shared `commit_user` (UUID) that will be
/// used by both `new_write()` and `new_commit()` for duplicate-commit detection.
///
/// # Safety
/// `table` must be a valid pointer from `paimon_catalog_get_table`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_new_write_builder(
table: *const paimon_table,
) -> paimon_result_write_builder {
new_write_builder(table, None)
}
/// Create a WriteBuilder with a caller-provided stable commit identity.
///
/// Writers whose messages are merged into one logical commit must use the
/// same `commit_user`.
///
/// # Safety
/// `table` must be a valid table pointer. `commit_user` must be a valid UTF-8
/// C string and a safe file-name segment.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_new_write_builder_with_commit_user(
table: *const paimon_table,
commit_user: *const c_char,
) -> paimon_result_write_builder {
let commit_user = match validate_cstr(commit_user, "commit_user") {
Ok(commit_user) => commit_user,
Err(error) => {
return paimon_result_write_builder {
write_builder: ptr::null_mut(),
error,
}
}
};
new_write_builder(table, Some(commit_user))
}
/// Free a paimon_write_builder.
///
/// # Safety
/// Only call with a write_builder returned from `paimon_table_new_write_builder`.
#[no_mangle]
pub unsafe extern "C" fn paimon_write_builder_free(wb: *mut paimon_write_builder) {
if !wb.is_null() {
let wrapper = Box::from_raw(wb);
if !wrapper.inner.is_null() {
drop(Box::from_raw(wrapper.inner as *mut WriteBuilderState));
}
}
}
/// Enable overwrite mode for the WriteBuilder.
///
/// In overwrite mode, a subsequent `paimon_table_commit_overwrite` will replace
/// the data in the written partitions rather than appending.
///
/// # Safety
/// `wb` must be a valid pointer from `paimon_table_new_write_builder`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_write_builder_with_overwrite(
wb: *mut paimon_write_builder,
) -> *mut paimon_error {
if let Err(e) = check_non_null(wb, "wb") {
return e;
}
let state = &mut *((*wb).inner as *mut WriteBuilderState);
state.overwrite = true;
ptr::null_mut()
}
// ======================= TableWrite ===============================
fn invalid_input(message: impl Into<String>) -> *mut paimon_error {
paimon_error::new(PaimonErrorCode::InvalidInput, message.into())
}
fn validate_batch_schema(
input: &RecordBatch,
target: &ArrowSchema,
) -> Result<(), *mut paimon_error> {
let input_schema = input.schema();
if input_schema.fields().len() != target.fields().len() {
return Err(invalid_input(format!(
"Input schema is not consistent with the table schema. input: {input_schema:?}, table: {target:?}"
)));
}
for (index, (input_field, target_field)) in input_schema
.fields()
.iter()
.zip(target.fields().iter())
.enumerate()
{
if input_field.name() != target_field.name()
|| input_field.data_type() != target_field.data_type()
{
return Err(invalid_input(format!(
"Input schema is not consistent with the table schema. input: {input_schema:?}, table: {target:?}"
)));
}
if !target_field.is_nullable() && input.column(index).null_count() != 0 {
return Err(invalid_input(format!(
"Column '{}' is NOT NULL but the Arrow batch contains {} null value(s)",
target_field.name(),
input.column(index).null_count()
)));
}
}
Ok(())
}
unsafe fn import_record_batch(
array: *mut c_void,
schema: *mut c_void,
) -> Result<RecordBatch, *mut paimon_error> {
// Arrow's from_raw implements the C Data Interface move operation: it
// replaces the caller-owned struct with an empty/released value.
let ffi_array = FFI_ArrowArray::from_raw(array as *mut FFI_ArrowArray);
let ffi_schema = FFI_ArrowSchema::from_raw(schema as *mut FFI_ArrowSchema);
let data = match from_ffi(ffi_array, &ffi_schema) {
Ok(data) => data,
Err(e) => {
drop(ffi_schema);
return Err(invalid_input(format!(
"Failed to import Arrow record batch: {e}"
)));
}
};
drop(ffi_schema);
if !matches!(data.data_type(), ArrowDataType::Struct(_)) {
return Err(invalid_input(format!(
"Arrow record batch root must be Struct, got {:?}",
data.data_type()
)));
}
let struct_array = StructArray::from(data);
if struct_array.null_count() != 0 {
return Err(invalid_input(
"Arrow record batch root Struct must not contain nulls",
));
}
let row_count = struct_array.len();
let (fields, columns, _) = struct_array.into_parts();
let schema = Arc::new(ArrowSchema::new(fields));
RecordBatch::try_new_with_options(
schema,
columns,
&RecordBatchOptions::new().with_row_count(Some(row_count)),
)
.map_err(|e| invalid_input(format!("Failed to construct Arrow record batch: {e}")))
}
/// Create a new TableWrite from the WriteBuilder.
///
/// The returned TableWrite accumulates Arrow batches until `prepare_commit` is called.
///
/// # Safety
/// `wb` must be a valid pointer from `paimon_table_new_write_builder`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_write_builder_new_write(
wb: *const paimon_write_builder,
) -> paimon_result_table_write {
if let Err(e) = check_non_null(wb, "wb") {
return paimon_result_table_write {
write: ptr::null_mut(),
error: e,
};
}
let state = &*((*wb).inner as *const WriteBuilderState);
let mut builder = match state
.table
.new_write_builder()
.with_commit_user(state.commit_user.clone())
{
Ok(b) => b,
Err(e) => {
return paimon_result_table_write {
write: ptr::null_mut(),
error: paimon_error::from_paimon(e),
}
}
};
if state.overwrite {
builder = builder.with_overwrite();
}
let tw = match builder.new_write() {
Ok(w) => w,
Err(e) => {
return paimon_result_table_write {
write: ptr::null_mut(),
error: paimon_error::from_paimon(e),
}
}
};
let target_schema =
match paimon::arrow::build_target_arrow_schema(state.table.schema().fields()) {
Ok(schema) => schema,
Err(e) => {
return paimon_result_table_write {
write: ptr::null_mut(),
error: paimon_error::from_paimon(e),
}
}
};
let table_write = TableWriteState {
write: tw,
target_schema,
table_location: state.table.location().to_string(),
commit_user: state.commit_user.clone(),
};
let inner = Box::into_raw(Box::new(table_write)) as *mut c_void;
paimon_result_table_write {
write: Box::into_raw(Box::new(paimon_table_write { inner })),
error: ptr::null_mut(),
}
}
/// Free a paimon_table_write.
///
/// Dropping a TableWrite before calling `prepare_commit` discards any
/// uncommitted data.
///
/// # Safety
/// Only call with a write returned from `paimon_write_builder_new_write`.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_write_free(tw: *mut paimon_table_write) {
if !tw.is_null() {
let wrapper = Box::from_raw(tw);
if !wrapper.inner.is_null() {
drop(Box::from_raw(wrapper.inner as *mut TableWriteState));
}
}
}
/// Write a single Arrow record batch into the table's writers.
///
/// The Arrow data is imported via the Arrow C Data Interface. `array` and
/// `schema` must point to valid `ArrowArray` and `ArrowSchema` structs
/// filled by the caller. Ownership is transferred — the caller must not
/// release the structs after this call.
///
/// # Safety
/// `tw` must be a valid pointer from `paimon_write_builder_new_write`, or null (returns error).
/// `array` and `schema` must be valid pointers to initialized ArrowArray /
/// ArrowSchema structs, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_write_write_arrow_batch(
tw: *mut paimon_table_write,
array: *mut c_void,
schema: *mut c_void,
) -> *mut paimon_error {
if let Err(e) = check_non_null(tw, "tw") {
return e;
}
if let Err(e) = check_non_null(array, "array") {
return e;
}
if let Err(e) = check_non_null(schema, "schema") {
return e;
}
let table_write = &mut *((*tw).inner as *mut TableWriteState);
let batch = match import_record_batch(array, schema) {
Ok(batch) => batch,
Err(error) => return error,
};
if let Err(error) = validate_batch_schema(&batch, &table_write.target_schema) {
return error;
}
match runtime().block_on(table_write.write.write_arrow_batch(&batch)) {
Ok(()) => ptr::null_mut(),
Err(e) => paimon_error::from_paimon(e),
}
}
/// Close file writers and produce CommitMessages.
///
/// Consumes the open file writers (they are flushed and closed). After this
/// call, the TableWrite can be reused — `write_arrow_batch` may be called
/// again to start a new round of writes.
///
/// The returned `paimon_commit_messages` must be passed to a
/// `paimon_table_commit_*` function and then freed with
/// `paimon_commit_messages_free`.
///
/// # Safety
/// `tw` must be a valid pointer from `paimon_write_builder_new_write`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_write_prepare_commit(
tw: *mut paimon_table_write,
) -> paimon_result_prepare_commit {
if let Err(e) = check_non_null(tw, "tw") {
return paimon_result_prepare_commit {
messages: ptr::null_mut(),
error: e,
};
}
let table_write = &mut *((*tw).inner as *mut TableWriteState);
match runtime().block_on(table_write.write.prepare_commit()) {
Ok(messages) => {
let messages = CommitMessagesState {
messages,
table_location: table_write.table_location.clone(),
commit_user: table_write.commit_user.clone(),
};
let inner = Box::into_raw(Box::new(messages)) as *mut c_void;
paimon_result_prepare_commit {
messages: Box::into_raw(Box::new(paimon_commit_messages { inner })),
error: ptr::null_mut(),
}
}
Err(e) => paimon_result_prepare_commit {
messages: ptr::null_mut(),
error: paimon_error::from_paimon(e),
},
}
}
// ======================= TableCommit ===============================
/// Create a new TableCommit from the WriteBuilder.
///
/// The committer shares the same `commit_user` as the writer, which is
/// required for duplicate-commit detection.
///
/// # Safety
/// `wb` must be a valid pointer from `paimon_table_new_write_builder`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_write_builder_new_commit(
wb: *const paimon_write_builder,
) -> paimon_result_table_commit {
if let Err(e) = check_non_null(wb, "wb") {
return paimon_result_table_commit {
commit: ptr::null_mut(),
error: e,
};
}
let state = &*((*wb).inner as *const WriteBuilderState);
let builder = match state
.table
.new_write_builder()
.with_commit_user(state.commit_user.clone())
{
Ok(b) => b,
Err(e) => {
return paimon_result_table_commit {
commit: ptr::null_mut(),
error: paimon_error::from_paimon(e),
}
}
};
let tc = match builder.try_new_commit() {
Ok(c) => c,
Err(e) => {
return paimon_result_table_commit {
commit: ptr::null_mut(),
error: paimon_error::from_paimon(e),
}
}
};
let table_commit = TableCommitState {
commit: tc,
table_location: state.table.location().to_string(),
commit_user: state.commit_user.clone(),
};
let inner = Box::into_raw(Box::new(table_commit)) as *mut c_void;
paimon_result_table_commit {
commit: Box::into_raw(Box::new(paimon_table_commit { inner })),
error: ptr::null_mut(),
}
}
/// Free a paimon_table_commit.
///
/// # Safety
/// Only call with a commit returned from `paimon_write_builder_new_commit`.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_free(tc: *mut paimon_table_commit) {
if !tc.is_null() {
let wrapper = Box::from_raw(tc);
if !wrapper.inner.is_null() {
drop(Box::from_raw(wrapper.inner as *mut TableCommitState));
}
}
}
// ======================= CommitMessages ===============================
/// Free a paimon_commit_messages.
///
/// # Safety
/// Only call with messages returned from `paimon_table_write_prepare_commit`.
#[no_mangle]
pub unsafe extern "C" fn paimon_commit_messages_free(msgs: *mut paimon_commit_messages) {
if !msgs.is_null() {
let wrapper = Box::from_raw(msgs);
if !wrapper.inner.is_null() {
drop(Box::from_raw(wrapper.inner as *mut CommitMessagesState));
}
}
}
/// Merge `source` messages into `target` for one logical commit.
///
/// Both handles retain ownership and must be freed separately. They must have
/// been prepared for the same table and `commit_user`.
///
/// # Safety
/// `target` and `source` must be distinct valid commit-message handles.
#[no_mangle]
pub unsafe extern "C" fn paimon_commit_messages_merge(
target: *mut paimon_commit_messages,
source: *const paimon_commit_messages,
) -> *mut paimon_error {
if let Err(error) = check_non_null(target, "target") {
return error;
}
if let Err(error) = check_non_null(source, "source") {
return error;
}
if ptr::eq(target, source.cast_mut()) {
return invalid_input("target and source commit messages must be distinct handles");
}
let target = &mut *((*target).inner as *mut CommitMessagesState);
let source = &*((*source).inner as *const CommitMessagesState);
if target.table_location != source.table_location || target.commit_user != source.commit_user {
return invalid_input(
"commit messages can only be merged when table and commit_user both match",
);
}
target.messages.extend(source.messages.clone());
ptr::null_mut()
}
// ======================= Commit operations ===============================
fn validate_commit_context(
commit: &TableCommitState,
messages: &CommitMessagesState,
) -> Result<(), *mut paimon_error> {
if commit.table_location != messages.table_location {
return Err(invalid_input(format!(
"commit messages were prepared for a different table (message table '{}', committer table '{}')",
messages.table_location, commit.table_location
)));
}
if commit.commit_user != messages.commit_user {
return Err(invalid_input(
"commit messages were prepared with a different commit_user",
));
}
Ok(())
}
/// Commit the given messages in APPEND mode.
///
/// Empty messages is a no-op success.
/// The caller retains ownership of `msgs`; it may retry after an error and
/// must eventually release the handle with `paimon_commit_messages_free`.
///
/// # Safety
/// `tc` must be a valid pointer from `paimon_write_builder_new_commit`, or null (returns error).
/// `msgs` must be a valid pointer from `paimon_table_write_prepare_commit`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_commit(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
) -> *mut paimon_error {
paimon_table_commit_commit_with_identifier(tc, msgs, i64::MAX)
}
/// Commit the given messages with a caller-provided identifier.
///
/// Identifiers must increase monotonically for a `commit_user`. All messages
/// for one identifier must be merged and submitted in a single call.
/// This operation does not filter previously committed identifiers. Use
/// `paimon_table_commit_filter_and_commit_with_identifier` when retrying an
/// uncertain result.
/// The caller retains ownership of `msgs` and must free it explicitly.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_commit_with_identifier(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
commit_identifier: i64,
) -> *mut paimon_error {
if let Err(e) = check_non_null(tc, "tc") {
return e;
}
if let Err(e) = check_non_null(msgs, "msgs") {
return e;
}
let table_commit = &*((*tc).inner as *const TableCommitState);
let messages = &*((*msgs).inner as *const CommitMessagesState);
if let Err(error) = validate_commit_context(table_commit, messages) {
return error;
}
match runtime().block_on(
table_commit
.commit
.commit_with_identifier(messages.messages.clone(), commit_identifier),
) {
Ok(()) => ptr::null_mut(),
Err(e) => paimon_error::from_paimon(e),
}
}
/// Filter a previously committed identifier, then commit if it is new.
///
/// Identifiers must increase monotonically for a `commit_user`. Use this only
/// to retry the same uncertain result after all writer messages for the
/// logical commit have been merged.
/// The caller retains ownership of `msgs` and must free it explicitly.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_filter_and_commit_with_identifier(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
commit_identifier: i64,
) -> *mut paimon_error {
if let Err(e) = check_non_null(tc, "tc") {
return e;
}
if let Err(e) = check_non_null(msgs, "msgs") {
return e;
}
let table_commit = &*((*tc).inner as *const TableCommitState);
let messages = &*((*msgs).inner as *const CommitMessagesState);
if let Err(error) = validate_commit_context(table_commit, messages) {
return error;
}
match runtime().block_on(
table_commit
.commit
.filter_and_commit_with_identifier(messages.messages.clone(), commit_identifier),
) {
Ok(()) => ptr::null_mut(),
Err(e) => paimon_error::from_paimon(e),
}
}
/// Commit in OVERWRITE mode, replacing data in the written partitions.
///
/// `static_partitions` is currently passed as `None` (overwrite all
/// partitions that were written to).
/// The caller retains ownership of `msgs`; it may retry after an error and
/// must eventually release the handle with `paimon_commit_messages_free`.
///
/// # Safety
/// `tc` must be a valid pointer from `paimon_write_builder_new_commit`, or null (returns error).
/// `msgs` must be a valid pointer from `paimon_table_write_prepare_commit`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_overwrite(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
) -> *mut paimon_error {
paimon_table_commit_overwrite_impl(tc, msgs, None)
}
/// Overwrite with a caller-provided stable commit identifier.
///
/// The caller retains ownership of `msgs` and must free it explicitly.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_overwrite_with_identifier(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
commit_identifier: i64,
) -> *mut paimon_error {
paimon_table_commit_overwrite_impl(tc, msgs, Some(commit_identifier))
}
unsafe fn paimon_table_commit_overwrite_impl(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
commit_identifier: Option<i64>,
) -> *mut paimon_error {
if let Err(e) = check_non_null(tc, "tc") {
return e;
}
if let Err(e) = check_non_null(msgs, "msgs") {
return e;
}
let table_commit = &*((*tc).inner as *const TableCommitState);
let messages = &*((*msgs).inner as *const CommitMessagesState);
if let Err(error) = validate_commit_context(table_commit, messages) {
return error;
}
let result = match commit_identifier {
Some(commit_identifier) => {
runtime().block_on(table_commit.commit.overwrite_with_identifier(
messages.messages.clone(),
None,
commit_identifier,
))
}
None => runtime().block_on(
table_commit
.commit
.overwrite(messages.messages.clone(), None),
),
};
match result {
Ok(()) => ptr::null_mut(),
Err(e) => paimon_error::from_paimon(e),
}
}
/// Truncate the entire table — removes all data.
///
/// This is an OVERWRITE with zero new files. The table's latest snapshot
/// will have no data.
///
/// # Safety
/// `tc` must be a valid pointer from `paimon_write_builder_new_commit`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_truncate_table(
tc: *const paimon_table_commit,
) -> *mut paimon_error {
paimon_table_commit_truncate_table_impl(tc, None)
}
/// Truncate the table with a caller-provided stable commit identifier.
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_truncate_table_with_identifier(
tc: *const paimon_table_commit,
commit_identifier: i64,
) -> *mut paimon_error {
paimon_table_commit_truncate_table_impl(tc, Some(commit_identifier))
}
unsafe fn paimon_table_commit_truncate_table_impl(
tc: *const paimon_table_commit,
commit_identifier: Option<i64>,
) -> *mut paimon_error {
if let Err(e) = check_non_null(tc, "tc") {
return e;
}
let table_commit = &*((*tc).inner as *const TableCommitState);
let result = match commit_identifier {
Some(commit_identifier) => runtime().block_on(
table_commit
.commit
.truncate_table_with_identifier(commit_identifier),
),
None => runtime().block_on(table_commit.commit.truncate_table()),
};
match result {
Ok(()) => ptr::null_mut(),
Err(e) => paimon_error::from_paimon(e),
}
}
/// Abort a prepared commit, cleaning up written data files.
///
/// This is a best-effort cleanup — it attempts to delete new data, changelog,
/// and index files produced by the writer. Storage deletion errors are ignored
/// so cleanup does not mask an earlier write or commit failure.
/// The caller retains ownership of `msgs` and must free it explicitly.
///
/// # Safety
/// `tc` must be a valid pointer from `paimon_write_builder_new_commit`, or null (returns error).
/// `msgs` must be a valid pointer from `paimon_table_write_prepare_commit`, or null (returns error).
#[no_mangle]
pub unsafe extern "C" fn paimon_table_commit_abort(
tc: *const paimon_table_commit,
msgs: *mut paimon_commit_messages,
) -> *mut paimon_error {
if let Err(e) = check_non_null(tc, "tc") {
return e;
}
if let Err(e) = check_non_null(msgs, "msgs") {
return e;
}
let table_commit = &*((*tc).inner as *const TableCommitState);
let messages = &*((*msgs).inner as *const CommitMessagesState);
if let Err(error) = validate_commit_context(table_commit, messages) {
return error;
}
match runtime().block_on(table_commit.commit.abort(&messages.messages)) {
Ok(()) => ptr::null_mut(),
Err(e) => paimon_error::from_paimon(e),
}
}
// --- C ABI signature guards -------------------------------------------------
const _: unsafe extern "C" fn(*const paimon_table) -> paimon_result_write_builder =
paimon_table_new_write_builder;
const _: unsafe extern "C" fn(*const paimon_table, *const c_char) -> paimon_result_write_builder =
paimon_table_new_write_builder_with_commit_user;
const _: unsafe extern "C" fn(*const paimon_write_builder) -> paimon_result_table_write =
paimon_write_builder_new_write;
const _: unsafe extern "C" fn(*const paimon_write_builder) -> paimon_result_table_commit =
paimon_write_builder_new_commit;
const _: unsafe extern "C" fn(*mut paimon_table_write) -> paimon_result_prepare_commit =
paimon_table_write_prepare_commit;
const _: unsafe extern "C" fn(
*mut paimon_commit_messages,
*const paimon_commit_messages,
) -> *mut paimon_error = paimon_commit_messages_merge;
const _: unsafe extern "C" fn(
*mut paimon_table_write,
*mut c_void,
*mut c_void,
) -> *mut paimon_error = paimon_table_write_write_arrow_batch;
const _: unsafe extern "C" fn(
*const paimon_table_commit,
*mut paimon_commit_messages,
) -> *mut paimon_error = paimon_table_commit_commit;
const _: unsafe extern "C" fn(
*const paimon_table_commit,
*mut paimon_commit_messages,
i64,
) -> *mut paimon_error = paimon_table_commit_commit_with_identifier;
const _: unsafe extern "C" fn(
*const paimon_table_commit,
*mut paimon_commit_messages,
i64,
) -> *mut paimon_error = paimon_table_commit_filter_and_commit_with_identifier;
const _: unsafe extern "C" fn(
*const paimon_table_commit,
*mut paimon_commit_messages,
) -> *mut paimon_error = paimon_table_commit_overwrite;
const _: unsafe extern "C" fn(
*const paimon_table_commit,
*mut paimon_commit_messages,
i64,
) -> *mut paimon_error = paimon_table_commit_overwrite_with_identifier;
const _: unsafe extern "C" fn(*const paimon_table_commit) -> *mut paimon_error =
paimon_table_commit_truncate_table;
const _: unsafe extern "C" fn(*const paimon_table_commit, i64) -> *mut paimon_error =
paimon_table_commit_truncate_table_with_identifier;
const _: unsafe extern "C" fn(
*const paimon_table_commit,
*mut paimon_commit_messages,
) -> *mut paimon_error = paimon_table_commit_abort;