Skip to content

Commit

Permalink
[skdb] Python bindings proof of concept.
Browse files Browse the repository at this point in the history
Build:
```
$ (cd sql && skargo build -r --lib)
$ cd pyskdb && pip install .
```

Run:
```
$ python -c "import skdb; skdb.exec('CREATE TABLE foo(a INTEGER, b \
TEXT, c FLOAT); INSERT INTO foo VALUES (1337, \'hello\', \
3.14159267), (42, \'foo\', 2e64)'); print(skdb.exec('SELECT * FROM \
foo'))"
[{'a': 42, 'b': 'foo', 'c': 2e+64}, {'a': 1337, 'b': 'hello', 'c': 3.14159267}]
```
  • Loading branch information
beauby committed May 22, 2024
1 parent d3231f5 commit d3df1ce
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 13 deletions.
64 changes: 51 additions & 13 deletions prelude/runtime/runtime64_specific.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,33 +601,71 @@ void SKIP_last_tick(uint32_t) {
void SKIP_switch_to(uint32_t) {
// Not implemented
}

void (*SKIP_clear_field_names_ptr)();
void SKIP_clear_field_names() {
// Not implemented
if (SKIP_clear_field_names_ptr) {
SKIP_clear_field_names_ptr();
}
}
void SKIP_push_field_name(char*) {
// Not implemented

void (*SKIP_push_field_name_ptr)(char*);
void SKIP_push_field_name(char* val) {
if (SKIP_push_field_name_ptr) {
SKIP_push_field_name_ptr(val);
}
}

void (*SKIP_clear_object_ptr)();
void SKIP_clear_object() {
// Not implemented
if (SKIP_clear_object_ptr) {
SKIP_clear_object_ptr();
}
}

void (*SKIP_push_object_field_null_ptr)();
void SKIP_push_object_field_null() {
// Not implemented
if (SKIP_push_object_field_null_ptr) {
SKIP_push_object_field_null_ptr();
}
}
void SKIP_push_object_field_int32(int32_t) {

void (*SKIP_push_object_field_int32_ptr)(int32_t);
void SKIP_push_object_field_int32(int32_t val) {
if (SKIP_push_object_field_int32_ptr) {
SKIP_push_object_field_int32_ptr(val);
}
// Not implemented
}
void SKIP_push_object_field_int64(char*) {
// Not implemented

void (*SKIP_push_object_field_int64_ptr)(char*);
void SKIP_push_object_field_int64(char* val) {
if (SKIP_push_object_field_int64_ptr) {
SKIP_push_object_field_int64_ptr(val);
}
}
void SKIP_push_object_field_float(char*) {
// Not implemented

void (*SKIP_push_object_field_float_ptr)(char*);
void SKIP_push_object_field_float(char* val) {
if (SKIP_push_object_field_float_ptr) {
SKIP_push_object_field_float_ptr(val);
}
}
void SKIP_push_object_field_string(char*) {
// Not implemented

void (*SKIP_push_object_field_string_ptr)(char*);
void SKIP_push_object_field_string(char* val) {
if (SKIP_push_object_field_string_ptr) {
SKIP_push_object_field_string_ptr(val);
}
}

void (*SKIP_push_object_ptr)();
void SKIP_push_object() {
// Not implemented
if (SKIP_push_object_ptr) {
SKIP_push_object_ptr();
}
}

void SKIP_js_delete_fun() {
// Not implemented
}
Expand Down
24 changes: 24 additions & 0 deletions pyskdb/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[build-system]
requires = [
"setuptools>=42",
"pybind11>=2.10.0",
]
build-backend = "setuptools.build_meta"


[tool.cibuildwheel]
test-command = "python {project}/tests/test.py"
test-skip = "*universal2:arm64"


[tool.ruff]
target-version = "py37"

[tool.ruff.lint]
extend-select = [
"B", # flake8-bugbear
"I", # isort
"PGH", # pygrep-hooks
"RUF", # Ruff-specific
"UP", # pyupgrade
]
44 changes: 44 additions & 0 deletions pyskdb/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from pybind11.setup_helpers import Pybind11Extension, build_ext
from setuptools import setup, Extension
from setuptools.command.install_lib import install_lib
from shutil import copyfile
import os

__version__ = "0.1.0"

libskdb_path = os.path.join("..", "sql", "target", "host", "release", "libskdb.so")
if not os.path.exists(libskdb_path):
raise Exception("libskdb.so not found: build it first (`cargo build -r --lib`)")

copyfile(libskdb_path, "libskdb.so")

ext_modules = [
Pybind11Extension(
"skdb",
sources=["src/skdb.cpp"],
libraries=["skdb"],
library_dirs=["."],
runtime_library_dirs=["."],
include_dirs=["../sql/ffi/include"],
),
]

setup(
name="skdb",
version=__version__,
author="SkipLabs",
author_email="contact@skiplabs.io",
url="https://github.com/skiplabs/skdb",
description="",
long_description="",
ext_modules=ext_modules,
extras_require={"test": "pytest"},
# Currently, build_ext only provides an optional "highest supported C++
# level" feature, but in the future it may provide more features.
cmdclass={"build_ext": build_ext},
packages=[''],
package_dir={'': '.'},
package_data={'': ["libskdb.so"]},
zip_safe=False,
python_requires=">=3.7",
)
104 changes: 104 additions & 0 deletions pyskdb/src/skdb.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include <dlfcn.h>
#include <pybind11/pybind11.h>
#include <skdb/skdb.h>

#include <cstdlib>
#include <vector>

namespace py = pybind11;

namespace pyskdb {
struct ResultBuilder {
std::vector<std::string> field_names;
int objectIdx = 0;
py::dict object;
py::list objects;
};

ResultBuilder* result = nullptr;

void pySKIP_clear_field_names() {
result->field_names.clear();
}

void pySKIP_push_field_name(char* field_name) {
result->field_names.push_back(std::string(field_name));
}

void pySKIP_clear_object() {
result->object = py::dict();
result->objectIdx = 0;
}

void pySKIP_push_object_field_null() {
result->object[py::str(result->field_names[result->objectIdx++])] =
py::none();
}

void pySKIP_push_object_field_int32(int32_t val) {
result->object[py::str(result->field_names[result->objectIdx++])] = val;
}

void pySKIP_push_object_field_int64(char* val) {
result->object[py::str(result->field_names[result->objectIdx++])] = atol(val);
}

void pySKIP_push_object_field_float(char* val) {
result->object[py::str(result->field_names[result->objectIdx++])] = atof(val);
}

void pySKIP_push_object_field_string(char* val) {
result->object[py::str(result->field_names[result->objectIdx++])] =
std::string(val);
}

void pySKIP_push_object() {
result->objects.append(result->object);
}
} // namespace pyskdb

PYBIND11_MODULE(skdb, m) {
SKIP_clear_field_names_ptr = &pyskdb::pySKIP_clear_field_names;
SKIP_push_field_name_ptr = &pyskdb::pySKIP_push_field_name;
SKIP_clear_object_ptr = &pyskdb::pySKIP_clear_object;
SKIP_push_object_field_null_ptr = &pyskdb::pySKIP_push_object_field_null;
SKIP_push_object_field_int32_ptr = &pyskdb::pySKIP_push_object_field_int32;
SKIP_push_object_field_int64_ptr = &pyskdb::pySKIP_push_object_field_int64;
SKIP_push_object_field_float_ptr = &pyskdb::pySKIP_push_object_field_float;
SKIP_push_object_field_string_ptr = &pyskdb::pySKIP_push_object_field_string;
SKIP_push_object_ptr = &pyskdb::pySKIP_push_object;

m.doc() = R"pbdoc(
Skdb -- The reactive database
-----------------------------
.. currentmodule:: skdb
.. autosummary::
:toctree: _generate
exec
)pbdoc";

m.def(
"exec",
[](std::string query) {
pyskdb::result = new pyskdb::ResultBuilder();
void* obstack = SKIP_new_Obstack();
char* sk_query = sk_string_create(query.c_str(), query.length());
skdb_exec(sk_query);
SKIP_destroy_Obstack(obstack);

auto res = std::move(pyskdb::result->objects);
delete pyskdb::result;
return res;
},
py::return_value_policy::copy,
R"pbdoc(
Execute a query
TODO
)pbdoc");

m.attr("__version__") = "TODO";
}
26 changes: 26 additions & 0 deletions sql/ffi/include/skdb/skdb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef __SKDB_H
#define __SKDB_H

#include <stdint.h>

extern "C" {
typedef int64_t SkipInt;

extern void (*SKIP_clear_field_names_ptr)();
extern void (*SKIP_push_field_name_ptr)(char*);
extern void (*SKIP_clear_object_ptr)();
extern void (*SKIP_push_object_field_null_ptr)();
extern void (*SKIP_push_object_field_int32_ptr)(int32_t);
extern void (*SKIP_push_object_field_int64_ptr)(char*);
extern void (*SKIP_push_object_field_float_ptr)(char*);
extern void (*SKIP_push_object_field_string_ptr)(char*);
extern void (*SKIP_push_object_ptr)();

char* sk_string_create(const char* buffer, uint32_t size);
void* SKIP_new_Obstack();
void SKIP_destroy_Obstack(void*);

SkipInt skdb_exec(char* query);
}

#endif // __SKDB_H
21 changes: 21 additions & 0 deletions sql/src/ffi.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module SKDBFFI;

@cpp_export("skdb_exec")
fun exec(query: String): Int {
SKDB.eval(
query,
SKDB.Options{
alwaysAllowJoins => true,
backtrace => true,
sync => true,
showUsedIndexes => false,
format => SKDB.OFK_JS(),
},
Map[],
) match {
| Success _ -> 0
| Failure _ -> 1
}
}

module end;

0 comments on commit d3df1ce

Please sign in to comment.