Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add C API bindings #1471

Merged
merged 1 commit into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/linux-precompiled-bin-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ jobs:
name: libkuzu-${{ github.event.inputs.packageVersion }}-linux-x86_64
path: |
./scripts/pre-compiled-bins/kuzu.h
./scripts/pre-compiled-bins/kuzu.hpp
./scripts/pre-compiled-bins/libkuzu.so
- uses: actions/upload-artifact@v3
with:
name: kuzu_cli-${{ github.event.inputs.packageVersion }}-linux-x86_64
path: ./scripts/pre-compiled-bins/kuzu

- name: Clean up
run: rm -rf ./scripts/pre-compiled-bins/kuzu ./scripts/pre-compiled-bins/headers ./scripts/pre-compiled-bins/libkuzu.so /scripts/pre-compiled-bins/kuzu.h
run: rm -rf ./scripts/pre-compiled-bins/kuzu ./scripts/pre-compiled-bins/headers ./scripts/pre-compiled-bins/libkuzu.so /scripts/pre-compiled-bins/kuzu.h /scripts/pre-compiled-bins/kuzu.hpp
8 changes: 5 additions & 3 deletions .github/workflows/mac-precompiled-bin-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ jobs:
name: libkuzu-${{ github.event.inputs.packageVersion }}-osx-arm64
path: |
./scripts/pre-compiled-bins/kuzu.h
./scripts/pre-compiled-bins/kuzu.hpp
./scripts/pre-compiled-bins/libkuzu.dylib
- uses: actions/upload-artifact@v3
with:
name: kuzu_cli-${{ github.event.inputs.packageVersion }}-osx-arm64
Expand All @@ -51,12 +52,13 @@ jobs:
name: libkuzu-${{ github.event.inputs.packageVersion }}-osx-x86_64
path: |
./scripts/pre-compiled-bins/kuzu.h
./scripts/pre-compiled-bins/kuzu.hpp
./scripts/pre-compiled-bins/libkuzu.dylib
- uses: actions/upload-artifact@v3
with:
name: kuzu_cli-${{ github.event.inputs.packageVersion }}-osx-x86_64
path: ./scripts/pre-compiled-bins/kuzu

- name: Clean up
run: rm -rf ./scripts/pre-compiled-bins/kuzu ./scripts/pre-compiled-bins/headers ./scripts/pre-compiled-bins/libkuzu.dylib ./scripts/pre-compiled-bins/kuzu.h
run: rm -rf ./scripts/pre-compiled-bins/kuzu ./scripts/pre-compiled-bins/headers ./scripts/pre-compiled-bins/libkuzu.dylib ./scripts/pre-compiled-bins/kuzu.h ./scripts/pre-compiled-bins/kuzu.hpp
5 changes: 3 additions & 2 deletions scripts/pre-compiled-bins/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Intro
The script in this directory builds pre-compiled library and CLI and collects header for release. It generates the following files:
- `headers`: the headers for release in a directory;
- `kuzu.h`: the merged header for release;
- `libkuzu.so` / `libkuzu.dylib`: the shared library for C++ API;
- `kuzu.hpp`: the merged C++ header for release;
- `kuzu.h`: the C header for release;
- `libkuzu.so` / `libkuzu.dylib`: the shared library for both C and C++ API;
- `kuzu`: the database shell.
# Usage:
- Install dependency: `pip3 install networkx`.
Expand Down
9 changes: 8 additions & 1 deletion scripts/pre-compiled-bins/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def cleanup():
subprocess.run(['make', 'clean-all'], cwd=workspace_root,
check=True, env=env_vars)
shutil.rmtree(os.path.join(base_dir, "headers"), ignore_errors=True)
for f in ["kuzu", "kuzu.h", "libkuzu.so", "libkuzu.dylib"]:
for f in ["kuzu", "kuzu.hpp", "kuzu.h", "libkuzu.so", "libkuzu.dylib"]:
try:
os.remove(os.path.join(base_dir, f))
except OSError:
Expand Down Expand Up @@ -91,10 +91,17 @@ def collect_and_merge_headers():

def collect_binaries():
logging.info("Collecting binaries...")
c_header_path = os.path.join(workspace_root, "src", "include", "c_api",
"kuzu.h")
so_path = os.path.join(workspace_root, "build",
"release", "src", "libkuzu.so")
dylib_path = os.path.join(workspace_root, "build",
"release", "src", "libkuzu.dylib")
if not os.path.exists(c_header_path):
logging.error("No C header file found")
sys.exit(1)
shutil.copy(c_header_path, os.path.join(base_dir, "kuzu.h"))
logging.info("Copied kuzu.h")
so_exists = os.path.exists(so_path)
dylib_exists = os.path.exists(dylib_path)
if not so_exists and not dylib_exists:
Expand Down
2 changes: 1 addition & 1 deletion scripts/pre-compiled-bins/merge_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
os.path.join(os.path.dirname(__file__), 'headers')
)
OUTPUT_PATH = os.path.realpath(
os.path.join(os.path.dirname(__file__), 'kuzu.h')
os.path.join(os.path.dirname(__file__), 'kuzu.hpp')
)

logging.debug('HEADER_PATH: %s', HEADER_PATH)
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_subdirectory(binder)
add_subdirectory(c_api)
add_subdirectory(catalog)
add_subdirectory(common)
add_subdirectory(expression_evaluator)
Expand Down
14 changes: 14 additions & 0 deletions src/c_api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
add_library(kuzu_c_api
OBJECT
connection.cpp
database.cpp
data_type.cpp
flat_tuple.cpp
prepared_statement.cpp
query_result.cpp
query_summary.cpp
value.cpp)

set(ALL_OBJECT_FILES
${ALL_OBJECT_FILES} $<TARGET_OBJECTS:kuzu_c_api>
PARENT_SCOPE)
137 changes: 137 additions & 0 deletions src/c_api/connection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include "binder/bound_statement_result.h"
#include "c_api/kuzu.h"
#include "common/exception.h"
#include "common/types/value.h"
#include "main/kuzu.h"
#include "planner/logical_plan/logical_plan.h"

using namespace kuzu::common;
using namespace kuzu::main;

kuzu_connection* kuzu_connection_init(kuzu_database* database) {
if (database == nullptr || database->_database == nullptr) {
return nullptr;
}
auto connection = (kuzu_connection*)malloc(sizeof(kuzu_connection));
try {
auto* connection_ptr = new Connection(static_cast<Database*>(database->_database));
connection->_connection = connection_ptr;
} catch (Exception& e) {
free(connection);
return nullptr;
}
return connection;
}

void kuzu_connection_destroy(kuzu_connection* connection) {
if (connection == nullptr) {
return;
}
if (connection->_connection != nullptr) {
delete static_cast<Connection*>(connection->_connection);
}
free(connection);
}

void kuzu_connection_begin_read_only_transaction(kuzu_connection* connection) {
static_cast<Connection*>(connection->_connection)->beginReadOnlyTransaction();
}

void kuzu_connection_begin_write_transaction(kuzu_connection* connection) {
static_cast<Connection*>(connection->_connection)->beginWriteTransaction();
}

void kuzu_connection_commit(kuzu_connection* connection) {
static_cast<Connection*>(connection->_connection)->commit();
}

void kuzu_connection_rollback(kuzu_connection* connection) {
static_cast<Connection*>(connection->_connection)->rollback();
}

void kuzu_connection_set_max_num_thread_for_exec(
kuzu_connection* connection, uint64_t num_threads) {
static_cast<Connection*>(connection->_connection)->setMaxNumThreadForExec(num_threads);
}

uint64_t kuzu_connection_get_max_num_thread_for_exec(kuzu_connection* connection) {
return static_cast<Connection*>(connection->_connection)->getMaxNumThreadForExec();
}

kuzu_query_result* kuzu_connection_query(kuzu_connection* connection, const char* query) {
auto query_result = static_cast<Connection*>(connection->_connection)->query(query).release();
if (query_result == nullptr) {
return nullptr;
}
auto* c_query_result = new kuzu_query_result;
c_query_result->_query_result = query_result;
return c_query_result;
}

kuzu_prepared_statement* kuzu_connection_prepare(kuzu_connection* connection, const char* query) {
auto connection_ptr = static_cast<Connection*>(connection->_connection);
auto prepared_statement = connection_ptr->prepare(query).release();
if (prepared_statement == nullptr) {
return nullptr;
}
auto* c_prepared_statement = new kuzu_prepared_statement;
c_prepared_statement->_prepared_statement = prepared_statement;
c_prepared_statement->_bound_values =
new std::unordered_map<std::string, std::shared_ptr<Value>>;
return c_prepared_statement;
}

kuzu_query_result* kuzu_connection_execute(
kuzu_connection* connection, kuzu_prepared_statement* prepared_statement) {
auto prepared_statement_ptr =
static_cast<PreparedStatement*>(prepared_statement->_prepared_statement);
auto bound_values = static_cast<std::unordered_map<std::string, std::shared_ptr<Value>>*>(
prepared_statement->_bound_values);
auto query_result = static_cast<Connection*>(connection->_connection)
->executeWithParams(prepared_statement_ptr, *bound_values)
.release();
if (query_result == nullptr) {
return nullptr;
}
auto* c_query_result = new kuzu_query_result;
c_query_result->_query_result = query_result;
return c_query_result;
}

char* kuzu_connection_get_node_table_names(kuzu_connection* connection) {
auto node_table_names = static_cast<Connection*>(connection->_connection)->getNodeTableNames();
char* node_table_names_c = (char*)malloc(node_table_names.size() + 1);
strcpy(node_table_names_c, node_table_names.c_str());
return node_table_names_c;
}

char* kuzu_connection_get_rel_table_names(kuzu_connection* connection) {
auto rel_table_names = static_cast<Connection*>(connection->_connection)->getRelTableNames();
char* rel_table_names_c = (char*)malloc(rel_table_names.size() + 1);
strcpy(rel_table_names_c, rel_table_names.c_str());
return rel_table_names_c;
}

char* kuzu_connection_get_node_property_names(kuzu_connection* connection, const char* table_name) {
auto node_property_names =
static_cast<Connection*>(connection->_connection)->getNodePropertyNames(table_name);
char* node_property_names_c = (char*)malloc(node_property_names.size() + 1);
strcpy(node_property_names_c, node_property_names.c_str());
return node_property_names_c;
}

char* kuzu_connection_get_rel_property_names(kuzu_connection* connection, const char* table_name) {
auto rel_property_names =
static_cast<Connection*>(connection->_connection)->getRelPropertyNames(table_name);
char* rel_property_names_c = (char*)malloc(rel_property_names.size() + 1);
strcpy(rel_property_names_c, rel_property_names.c_str());
return rel_property_names_c;
}

void kuzu_connection_interrupt(kuzu_connection* connection) {
static_cast<Connection*>(connection->_connection)->interrupt();
}

void kuzu_connection_set_query_timeout(kuzu_connection* connection, uint64_t timeout_in_ms) {
static_cast<Connection*>(connection->_connection)->setQueryTimeOut(timeout_in_ms);
}
81 changes: 81 additions & 0 deletions src/c_api/data_type.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "c_api/kuzu.h"
#include "common/types/types.h"
#include "main/kuzu.h"

using namespace kuzu::main;
using namespace kuzu::common;

kuzu_data_type* kuzu_data_type_create(
kuzu_data_type_id id, kuzu_data_type* child_type, uint64_t fixed_num_elements_in_list) {
auto* c_data_type = (kuzu_data_type*)malloc(sizeof(kuzu_data_type));
uint8_t data_type_id_u8 = id;
DataType* data_type;
if (child_type == nullptr) {
data_type = new DataType(static_cast<DataTypeID>(data_type_id_u8));
} else {
auto child_type_pty =
std::make_unique<DataType>(*static_cast<DataType*>(child_type->_data_type));
data_type =
fixed_num_elements_in_list > 0 ?
new DataType(static_cast<DataTypeID>(data_type_id_u8), std::move(child_type_pty),
fixed_num_elements_in_list) :
new DataType(static_cast<DataTypeID>(data_type_id_u8), std::move(child_type_pty));
}
c_data_type->_data_type = data_type;
return c_data_type;
}

kuzu_data_type* kuzu_data_type_clone(kuzu_data_type* data_type) {
auto* c_data_type = (kuzu_data_type*)malloc(sizeof(kuzu_data_type));
c_data_type->_data_type = new DataType(*static_cast<DataType*>(data_type->_data_type));
return c_data_type;
}

void kuzu_data_type_destroy(kuzu_data_type* data_type) {
if (data_type == nullptr) {
return;
}
if (data_type->_data_type != nullptr) {
delete static_cast<DataType*>(data_type->_data_type);
}
free(data_type);
}

bool kuzu_data_type_equals(kuzu_data_type* data_type1, kuzu_data_type* data_type2) {
return *static_cast<DataType*>(data_type1->_data_type) ==
*static_cast<DataType*>(data_type2->_data_type);
}

kuzu_data_type_id kuzu_data_type_get_id(kuzu_data_type* data_type) {
auto data_type_id_u8 =
static_cast<uint8_t>(static_cast<DataType*>(data_type->_data_type)->getTypeID());
return static_cast<kuzu_data_type_id>(data_type_id_u8);
}

kuzu_data_type* kuzu_data_type_get_child_type(kuzu_data_type* data_type) {
auto parent_type = static_cast<DataType*>(data_type->_data_type);
if (parent_type->getTypeID() != DataTypeID::FIXED_LIST &&
parent_type->getTypeID() != DataTypeID::VAR_LIST) {
return nullptr;
}
auto child_type = static_cast<DataType*>(data_type->_data_type)->getChildType();
if (child_type == nullptr) {
return nullptr;
}
auto* child_type_c = (kuzu_data_type*)malloc(sizeof(kuzu_data_type));
child_type_c->_data_type = new DataType(*child_type);
return child_type_c;
}

uint64_t kuzu_data_type_get_fixed_num_elements_in_list(kuzu_data_type* data_type) {
auto parent_type = static_cast<DataType*>(data_type->_data_type);
if (parent_type->getTypeID() != DataTypeID::FIXED_LIST) {
return 0;
}
auto extra_info = static_cast<DataType*>(data_type->_data_type)->getExtraTypeInfo();
if (extra_info == nullptr) {
return 0;
}
auto fixed_list_info = dynamic_cast<FixedListTypeInfo*>(extra_info);
return fixed_list_info->getFixedNumElementsInList();
}
34 changes: 34 additions & 0 deletions src/c_api/database.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "c_api/kuzu.h"
#include "common/exception.h"
#include "main/kuzu.h"

using namespace kuzu::main;
using namespace kuzu::common;

kuzu_database* kuzu_database_init(const char* database_path, uint64_t buffer_pool_size) {
auto database = (kuzu_database*)malloc(sizeof(kuzu_database));
std::string database_path_str = database_path;
try {
database->_database = buffer_pool_size == 0 ?
new Database(database_path_str) :
new Database(database_path_str, SystemConfig(buffer_pool_size));
} catch (Exception& e) {
free(database);
return nullptr;
}
return database;
}

void kuzu_database_destroy(kuzu_database* database) {
if (database == nullptr) {
return;
}
if (database->_database != nullptr) {
delete static_cast<Database*>(database->_database);
}
free(database);
}

void kuzu_database_set_logging_level(const char* logging_level) {
Database::setLoggingLevel(logging_level);
}
Loading