How to ccall struct array from C?

I would like read call directly duckdb c-api, but I have some difficulties with structs. I try to do the following c-code in Julia.

#include "duckdb.h"
#include <stdio.h>

int main() {
	duckdb_database db = NULL;
	duckdb_connection con = NULL;
	duckdb_result result;

	if (duckdb_open(NULL, &db) == DuckDBError) {
		fprintf(stderr, "Failed to open database\n");
		goto cleanup;
	}
	if (duckdb_connect(db, &con) == DuckDBError) {
		fprintf(stderr, "Failed to open connection\n");
		goto cleanup;
	}
	if (duckdb_query(con, "CREATE TABLE integers(i INTEGER, j INTEGER);", NULL) == DuckDBError) {
		fprintf(stderr, "Failed to query database\n");
		goto cleanup;
	}
	if (duckdb_query(con, "INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);", NULL) == DuckDBError) {
		fprintf(stderr, "Failed to query database\n");
		goto cleanup;
	}
	if (duckdb_query(con, "SELECT * FROM integers", &result) == DuckDBError) {
		fprintf(stderr, "Failed to query database\n");
		goto cleanup;
	}
	// print the names of the result
	for (size_t i = 0; i < result.column_count; i++) {
		printf("%s ", result.columns[i].name);
	}
	printf("\n");
	// print the data of the result
	for (size_t row_idx = 0; row_idx < result.row_count; row_idx++) {
		for (size_t col_idx = 0; col_idx < result.column_count; col_idx++) {
			char *val = duckdb_value_varchar(&result, col_idx, row_idx);
			printf("%s ", val);
			duckdb_free(val);
		}
		printf("\n");
	}
	// duckdb_print_result(result);
cleanup:
	duckdb_destroy_result(&result);
	duckdb_disconnect(&con);
	duckdb_close(&db);
}

I’m able to log in and read the first element but I don’t know how to read array of duckdb_columns

libduckdb="/home/kimmo/duckdb/build/release/src/libduckdb.so"
handle = Ref{Ptr{Cvoid}}();
ccall( (:duckdb_open,libduckdb),Cint,(Ptr{UInt8},Ptr{Cvoid}),":memory:",handle)
con = Ref{Ptr{Cvoid}}();
ccall( (:duckdb_connect,libduckdb),Cint,(Ptr{Cvoid},Ptr{Cvoid}),handle[],con)

struct duckdb_type
	duck_type::Ptr{Cint}
end

struct duckdb_column
	data::Ptr{Cvoid}
	nullmask::Ptr{Cint}
	type::Ptr{duckdb_type}
	name::Ptr{UInt8}
	internal_data::Ptr{Cvoid}
end

struct duckdb_result
	column_count::Ptr{UInt64}
	row_count::Ptr{UInt64}
	rows_changed::Ptr{UInt64}
	columns::Ptr{duckdb_column}
	error_message::Ptr{UInt8}
	internal_data::Ptr{Cvoid}
end

ccall( (:duckdb_query,libduckdb),Cint,(Ptr{Cvoid},Ptr{UInt8},Ptr{Cvoid}),con[],"CREATE TABLE integers(icol INTEGER, jcol INTEGER);",C_NULL)
ccall( (:duckdb_query,libduckdb),Cint,(Ptr{Cvoid},Ptr{UInt8},Ptr{Cvoid}),con[],"INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);",C_NULL)

res = Ref{duckdb_result}();
ccall( (:duckdb_query,libduckdb),Cint,(Ptr{Cvoid},Ptr{UInt8},Ptr{Cvoid}),con[],"SELECT * FROM integers",res)

cols=unsafe_load(res[].columns);
unsafe_string(cols.name)

Are you looking for unsafe_wrap?

help?> unsafe_wrap
search: unsafe_wrap unsafe_write

  unsafe_wrap(Array, pointer::Ptr{T}, dims; own = false)

  Wrap a Julia Array object around the data at the address given by pointer, without making a
  copy. The pointer element type T determines the array element type. dims is either an integer
  (for a 1d array) or a tuple of the array dimensions. own optionally specifies whether Julia
  should take ownership of the memory, calling free on the pointer when the array is no longer
  referenced.

  This function is labeled "unsafe" because it will crash if pointer is not a valid memory address
  to data of the requested length.
1 Like

Thank you very much. Now I’m able to read basic duckdb c-api elements so I can continue my little exercise. I would like to use duckdb with Julia without any extra depencies.

Solution was quite simple:
cols=unsafe_wrap(Array,res[].columns,Int64(res[].column_count))

I would to use duckdb with Julia without any extra depencies.

You might be interested in Clang.jl which can auto-generate those structures and function wrappers for you. By setting use_julia_native_enum_type = true in the toml file, the generated code is self-contained Julia code without any dependency.

1 Like

Thank you very much. I have used Julia just a couple of months so I have many things to learn. But I looked Clang.jl day before yesterday and now I will look at it again.