Skip to content
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
3 changes: 3 additions & 0 deletions grnpy/column.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ from grnpy.object cimport Object
cdef class Column(Object):
def set(self, grn_id id, value):
self._set_value(id, value)

def get(self, grn_id id):
return self._get_value(id)
36 changes: 36 additions & 0 deletions grnpy/grn_type.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (C) 2026 Abe Tomoaki <abe@clear-code.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program. If not, see
# <http://www.gnu.org/licenses/>.

# cython: language_level = 3

from grnpy.grn_id cimport grn_id

cdef extern from "groonga.h":
cdef grn_id VOID "GRN_DB_VOID"
cdef grn_id BOOL "GRN_DB_BOOL"
cdef grn_id INT8 "GRN_DB_INT8"
cdef grn_id UINT8 "GRN_DB_UINT8"
cdef grn_id INT16 "GRN_DB_INT16"
cdef grn_id UINT16 "GRN_DB_UINT16"
cdef grn_id INT32 "GRN_DB_INT32"
cdef grn_id UINT32 "GRN_DB_UINT32"
cdef grn_id INT64 "GRN_DB_INT64"
cdef grn_id UINT64 "GRN_DB_UINT64"
cdef grn_id FLOAT "GRN_DB_FLOAT"
cdef grn_id FLOAT32 "GRN_DB_FLOAT32"
cdef grn_id SHORT_TEXT "GRN_DB_SHORT_TEXT"
cdef grn_id TEXT "GRN_DB_TEXT"
cdef grn_id LONG_TEXT "GRN_DB_LONG_TEXT"
43 changes: 43 additions & 0 deletions grnpy/grnpy_bulk.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright (C) 2026 Abe Tomoaki <abe@clear-code.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this program. If not, see
<http://www.gnu.org/licenses/>.
*/

#include <groonga.h>

void
grnpy_bulk_init_void(grn_obj *bulk)
{
GRN_VOID_INIT(bulk);
}

void
grnpy_bulk_rewind(grn_obj *bulk)
{
GRN_BULK_REWIND(bulk);
}

const char *
grnpy_bulk_get_head(grn_obj *bulk)
{
return GRN_BULK_HEAD(bulk);
}

size_t
grnpy_bulk_get_size(grn_obj *bulk)
{
return GRN_BULK_VSIZE(bulk);
}
27 changes: 27 additions & 0 deletions grnpy/grnpy_bulk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright (C) 2026 Abe Tomoaki <abe@clear-code.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this program. If not, see
<http://www.gnu.org/licenses/>.
*/

#pragma once

#include <groonga.h>

void grnpy_bulk_init_void(grn_obj *bulk);
void grnpy_bulk_rewind(grn_obj *bulk);

const char *grnpy_bulk_get_head(grn_obj *bulk);
size_t grnpy_bulk_get_size(grn_obj *bulk);
6 changes: 6 additions & 0 deletions grnpy/grnpy_obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ grnpy_obj_get_type(grn_obj *obj)
return obj->header.type;
}

grn_id
grnpy_obj_get_domain(grn_obj *obj)
{
return obj->header.domain;
}

grn_rc
grnpy_obj_set_bool(grn_ctx *ctx, grn_obj *obj, grn_id id, int value)
{
Expand Down
2 changes: 2 additions & 0 deletions grnpy/grnpy_obj.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <groonga.h>

uint8_t grnpy_obj_get_type(grn_obj *obj);
grn_id grnpy_obj_get_domain(grn_obj *obj);

grn_rc grnpy_obj_set_bool(grn_ctx *ctx, grn_obj *obj, grn_id id, int value);
grn_rc grnpy_obj_set_int64(grn_ctx *ctx, grn_obj *obj, grn_id id, int64_t value);
grn_rc grnpy_obj_set_float(grn_ctx *ctx, grn_obj *obj, grn_id id, double value);
Expand Down
3 changes: 3 additions & 0 deletions grnpy/object.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ from grnpy.context cimport Context
cdef class Object:
cdef object _context
cdef grn_obj *_obj
cdef grn_obj _value_buffer

cdef grn_obj *unwrap(self)
cdef _set_value(self, grn_id id, value)
cdef _bulk_to_python(self, grn_obj *bulk)
cdef _get_value(self, grn_id id)

cdef build_object(Context context, grn_obj *obj)
74 changes: 71 additions & 3 deletions grnpy/object.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,27 @@
# <http://www.gnu.org/licenses/>.

# cython: language_level = 3
# distutils: sources = grnpy/grnpy_obj.c

from libc.stdint cimport int64_t, uint8_t
# distutils: sources = grnpy/grnpy_bulk.c grnpy/grnpy_obj.c

from libc.stdint cimport (
int8_t,
int16_t,
int32_t,
int64_t,
uint8_t,
uint16_t,
uint32_t,
uint64_t,
)
from libcpp cimport bool as c_bool

from grnpy.grn_ctx cimport grn_ctx
from grnpy.grn_error cimport grn_rc
from grnpy.grn_id cimport grn_id
from grnpy.grn_obj cimport grn_obj

cimport grnpy.grn_obj
cimport grnpy.grn_type

from grnpy.context cimport Context

Expand All @@ -43,19 +54,31 @@ cdef extern from "groonga.h":
grn_rc grn_obj_close(grn_ctx *ctx, grn_obj *obj)
void grn_obj_unref(grn_ctx *ctx, grn_obj *obj)
int grn_obj_name(grn_ctx *ctx, grn_obj *obj, char *buffer, int buffer_size)
grn_obj *grn_obj_get_value(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value)

cdef extern from "grnpy_bulk.h":
void grnpy_bulk_init_void(grn_obj *bulk)
void grnpy_bulk_rewind(grn_obj *bulk)
const char *grnpy_bulk_get_head(grn_obj *bulk)
size_t grnpy_bulk_get_size(grn_obj *bulk)

cdef extern from "grnpy_obj.h":
uint8_t grnpy_obj_get_type(grn_obj *obj)
grn_id grnpy_obj_get_domain(grn_obj *obj)
grn_rc grnpy_obj_set_bool(grn_ctx *ctx, grn_obj *obj, grn_id id, int value)
grn_rc grnpy_obj_set_int64(grn_ctx *ctx, grn_obj *obj, grn_id id, int64_t value)
grn_rc grnpy_obj_set_float(grn_ctx *ctx, grn_obj *obj, grn_id id, double value)
grn_rc grnpy_obj_set_text(grn_ctx *ctx, grn_obj *obj, grn_id id, const char *value, unsigned int length)

cdef class Object:
def __cinit__(self, *args, **kwargs):
grnpy_bulk_init_void(&(self._value_buffer))

def __dealloc__(self):
cdef Context context
if self._obj is not NULL:
context = self._context
grn_obj_close(context.unwrap(), &(self._value_buffer))
# TODO: This may be not worked when a database is closed
# and a table in the database isn't deleted yet.
grn_obj_unref(context.unwrap(), self._obj)
Expand Down Expand Up @@ -95,6 +118,50 @@ cdef class Object:
raise TypeError(f"unsupported value type: <{type(value)}>")
Error.check(rc, f"failed to set a value: <{value}>", context.error_message())

cdef _bulk_to_python(self, grn_obj *bulk):
cdef Context context = self._context
cdef grn_id domain = grnpy_obj_get_domain(bulk)
cdef const char *head = grnpy_bulk_get_head(bulk)
cdef size_t size = grnpy_bulk_get_size(bulk)
if size == 0:
return None
if domain == grnpy.grn_type.BOOL:
return (<const c_bool *>head)[0]
elif domain == grnpy.grn_type.INT8:
return (<const int8_t *>head)[0]
elif domain == grnpy.grn_type.UINT8:
return (<const uint8_t *>head)[0]
elif domain == grnpy.grn_type.INT16:
return (<const int16_t *>head)[0]
elif domain == grnpy.grn_type.UINT16:
return (<const uint16_t *>head)[0]
elif domain == grnpy.grn_type.INT32:
return (<const int32_t *>head)[0]
elif domain == grnpy.grn_type.UINT32:
return (<const uint32_t *>head)[0]
elif domain == grnpy.grn_type.INT64:
return (<const int64_t *>head)[0]
elif domain == grnpy.grn_type.UINT64:
return (<const uint64_t *>head)[0]
elif domain == grnpy.grn_type.FLOAT32:
return (<const float *>head)[0]
elif domain == grnpy.grn_type.FLOAT:
return (<const double *>head)[0]
elif domain in (grnpy.grn_type.SHORT_TEXT,
grnpy.grn_type.TEXT,
grnpy.grn_type.LONG_TEXT):
return head[:size].decode(context.encoding_name())
else:
raise NotImplementedError(f"unsupported value type: <{domain}>")

cdef _get_value(self, grn_id id):
cdef Context context = self._context
cdef grn_ctx *ctx = context.unwrap()
grnpy_bulk_rewind(&(self._value_buffer))
grn_obj_get_value(ctx, self.unwrap(), id, &(self._value_buffer))
context.check(f"failed to get a value: <{id}>")
return self._bulk_to_python(&(self._value_buffer))

def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
Expand All @@ -107,6 +174,7 @@ cdef class Object:
return
if context is None:
return
grn_obj_close(context.unwrap(), &(self._value_buffer))
grn_obj_close(context.unwrap(), self._obj)
self._obj = NULL
self._context = None
Expand Down
3 changes: 3 additions & 0 deletions grnpy/table.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,8 @@ cdef class Table(Object):
def set(self, grn_id id, value):
self._set_value(id, value)

def get(self, grn_id id):
return self._get_value(id)

def open_cursor(self, limit=-1, offset=0):
return TableCursor(self, offset, limit)
21 changes: 20 additions & 1 deletion testing/test_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,23 @@ def test_set_value(tmpdir):
active.set(id, True)
address.set(id, 'Tokyo')

# todo: Add a test for the set value
assert age.get(id) == 20
assert weight.get(id) == 70.5
assert active.get(id) is True
assert address.get(id) == 'Tokyo'

def test_get_no_data(tmpdir):
db_path = tmpdir.join('db')
with grnpy.Database.create(db_path):
users = grnpy.PatriciaTrie.create('ShortText', 'Users')
age = users.create_scalar_column('age', 'Int32')
weight = users.create_scalar_column('weight', 'Float')
address = users.create_scalar_column('address', 'ShortText')
active = users.create_scalar_column('active', 'Bool')

id = users.add('Groonga')

assert age.get(id) == 0
assert weight.get(id) == 0.0
assert active.get(id) is False
assert address.get(id) is None
2 changes: 1 addition & 1 deletion testing/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_set(tmpdir):
id = users.add()
users.set(id, 29)

# todo: Add a test for the set value
assert users.get(id) == 29

def test_cursor_all(tmpdir):
db_path = tmpdir.join('db')
Expand Down