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
132 changes: 132 additions & 0 deletions bindings/dart/lib/src/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,138 @@ class Database {
}
}

/// Reads all int sets for a set attribute from a collection.
List<List<int>> readSetInts(String collection, String attribute) {
_ensureNotClosed();

final arena = Arena();
try {
final outSets = arena<Pointer<Pointer<Int64>>>();
final outSizes = arena<Pointer<Size>>();
final outCount = arena<Size>();

final err = bindings.psr_database_read_set_ints(
_ptr,
collection.toNativeUtf8(allocator: arena).cast(),
attribute.toNativeUtf8(allocator: arena).cast(),
outSets,
outSizes,
outCount,
);

if (err != psr_error_t.PSR_OK) {
throw DatabaseException.fromError(err, "Failed to read set ints from '$collection.$attribute'");
}

final count = outCount.value;
if (count == 0 || outSets.value == nullptr) {
return [];
}

final result = <List<int>>[];
for (var i = 0; i < count; i++) {
final size = outSizes.value[i];
if (size == 0 || outSets.value[i] == nullptr) {
result.add([]);
} else {
result.add(List<int>.generate(size, (j) => outSets.value[i][j]));
}
}
bindings.psr_free_int_vectors(outSets.value, outSizes.value, count);
return result;
} finally {
arena.releaseAll();
}
}

/// Reads all double sets for a set attribute from a collection.
List<List<double>> readSetDoubles(String collection, String attribute) {
_ensureNotClosed();

final arena = Arena();
try {
final outSets = arena<Pointer<Pointer<Double>>>();
final outSizes = arena<Pointer<Size>>();
final outCount = arena<Size>();

final err = bindings.psr_database_read_set_doubles(
_ptr,
collection.toNativeUtf8(allocator: arena).cast(),
attribute.toNativeUtf8(allocator: arena).cast(),
outSets,
outSizes,
outCount,
);

if (err != psr_error_t.PSR_OK) {
throw DatabaseException.fromError(err, "Failed to read set doubles from '$collection.$attribute'");
}

final count = outCount.value;
if (count == 0 || outSets.value == nullptr) {
return [];
}

final result = <List<double>>[];
for (var i = 0; i < count; i++) {
final size = outSizes.value[i];
if (size == 0 || outSets.value[i] == nullptr) {
result.add([]);
} else {
result.add(List<double>.generate(size, (j) => outSets.value[i][j]));
}
}
bindings.psr_free_double_vectors(outSets.value, outSizes.value, count);
return result;
} finally {
arena.releaseAll();
}
}

/// Reads all string sets for a set attribute from a collection.
List<List<String>> readSetStrings(String collection, String attribute) {
_ensureNotClosed();

final arena = Arena();
try {
final outSets = arena<Pointer<Pointer<Pointer<Char>>>>();
final outSizes = arena<Pointer<Size>>();
final outCount = arena<Size>();

final err = bindings.psr_database_read_set_strings(
_ptr,
collection.toNativeUtf8(allocator: arena).cast(),
attribute.toNativeUtf8(allocator: arena).cast(),
outSets,
outSizes,
outCount,
);

if (err != psr_error_t.PSR_OK) {
throw DatabaseException.fromError(err, "Failed to read set strings from '$collection.$attribute'");
}

final count = outCount.value;
if (count == 0 || outSets.value == nullptr) {
return [];
}

final result = <List<String>>[];
for (var i = 0; i < count; i++) {
final size = outSizes.value[i];
if (size == 0 || outSets.value[i] == nullptr) {
result.add([]);
} else {
result.add(List<String>.generate(size, (j) => outSets.value[i][j].cast<Utf8>().toDartString()));
}
}
bindings.psr_free_string_vectors(outSets.value, outSizes.value, count);
return result;
} finally {
arena.releaseAll();
}
}

/// Closes the database and frees native resources.
void close() {
if (_isClosed) return;
Expand Down
111 changes: 111 additions & 0 deletions bindings/dart/lib/src/ffi/bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,117 @@ class PsrDatabaseBindings {
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>();

int psr_database_read_set_ints(
ffi.Pointer<psr_database_t> db,
ffi.Pointer<ffi.Char> collection,
ffi.Pointer<ffi.Char> attribute,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Int64>>> out_sets,
ffi.Pointer<ffi.Pointer<ffi.Size>> out_sizes,
ffi.Pointer<ffi.Size> out_count,
) {
return _psr_database_read_set_ints(
db,
collection,
attribute,
out_sets,
out_sizes,
out_count,
);
}

late final _psr_database_read_set_intsPtr = _lookup<
ffi.NativeFunction<
ffi.Int32 Function(
ffi.Pointer<psr_database_t>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Int64>>>,
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>>('psr_database_read_set_ints');
late final _psr_database_read_set_ints =
_psr_database_read_set_intsPtr.asFunction<
int Function(
ffi.Pointer<psr_database_t>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Int64>>>,
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>();

int psr_database_read_set_doubles(
ffi.Pointer<psr_database_t> db,
ffi.Pointer<ffi.Char> collection,
ffi.Pointer<ffi.Char> attribute,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Double>>> out_sets,
ffi.Pointer<ffi.Pointer<ffi.Size>> out_sizes,
ffi.Pointer<ffi.Size> out_count,
) {
return _psr_database_read_set_doubles(
db,
collection,
attribute,
out_sets,
out_sizes,
out_count,
);
}

late final _psr_database_read_set_doublesPtr = _lookup<
ffi.NativeFunction<
ffi.Int32 Function(
ffi.Pointer<psr_database_t>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Double>>>,
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>>('psr_database_read_set_doubles');
late final _psr_database_read_set_doubles =
_psr_database_read_set_doublesPtr.asFunction<
int Function(
ffi.Pointer<psr_database_t>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Double>>>,
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>();

int psr_database_read_set_strings(
ffi.Pointer<psr_database_t> db,
ffi.Pointer<ffi.Char> collection,
ffi.Pointer<ffi.Char> attribute,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Char>>>> out_sets,
ffi.Pointer<ffi.Pointer<ffi.Size>> out_sizes,
ffi.Pointer<ffi.Size> out_count,
) {
return _psr_database_read_set_strings(
db,
collection,
attribute,
out_sets,
out_sizes,
out_count,
);
}

late final _psr_database_read_set_stringsPtr = _lookup<
ffi.NativeFunction<
ffi.Int32 Function(
ffi.Pointer<psr_database_t>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Char>>>>,
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>>('psr_database_read_set_strings');
late final _psr_database_read_set_strings =
_psr_database_read_set_stringsPtr.asFunction<
int Function(
ffi.Pointer<psr_database_t>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Char>>>>,
ffi.Pointer<ffi.Pointer<ffi.Size>>,
ffi.Pointer<ffi.Size>)>();

void psr_free_int_array(
ffi.Pointer<ffi.Int64> values,
) {
Expand Down
74 changes: 74 additions & 0 deletions bindings/dart/test/read_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,78 @@ void main() {
}
});
});

group('Read Set Attributes', () {
test('reads string sets from Collection', () {
final db = Database.fromSchema(
':memory:',
path.join(testsPath, 'schemas', 'valid', 'collections.sql'),
);
try {
db.createElement('Configuration', {'label': 'Test Config'});
db.createElement('Collection', {
'label': 'Item 1',
'tag': ['important', 'urgent'],
});
db.createElement('Collection', {
'label': 'Item 2',
'tag': ['review'],
});

final result = db.readSetStrings('Collection', 'tag');
expect(result.length, equals(2));
// Sets are unordered, so sort before comparison
expect(result[0]..sort(), equals(['important', 'urgent']));
expect(result[1], equals(['review']));
} finally {
db.close();
}
});
});

group('Read Set Empty Result', () {
test('returns empty list when no elements', () {
final db = Database.fromSchema(
':memory:',
path.join(testsPath, 'schemas', 'valid', 'collections.sql'),
);
try {
db.createElement('Configuration', {'label': 'Test Config'});

expect(db.readSetStrings('Collection', 'tag'), isEmpty);
} finally {
db.close();
}
});
});

group('Read Set Only Returns Elements With Data', () {
test('only returns sets for elements with data', () {
final db = Database.fromSchema(
':memory:',
path.join(testsPath, 'schemas', 'valid', 'collections.sql'),
);
try {
db.createElement('Configuration', {'label': 'Test Config'});
db.createElement('Collection', {
'label': 'Item 1',
'tag': ['important'],
});
db.createElement('Collection', {
'label': 'Item 2',
// No set data
});
db.createElement('Collection', {
'label': 'Item 3',
'tag': ['urgent', 'review'],
});

// Only elements with set data are returned
final result = db.readSetStrings('Collection', 'tag');
expect(result.length, equals(2));
} finally {
db.close();
}
});
});
}
12 changes: 12 additions & 0 deletions bindings/julia/src/c_api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ function psr_database_read_vector_strings(db, collection, attribute, out_vectors
@ccall libpsr_database_c.psr_database_read_vector_strings(db::Ptr{psr_database_t}, collection::Ptr{Cchar}, attribute::Ptr{Cchar}, out_vectors::Ptr{Ptr{Ptr{Ptr{Cchar}}}}, out_sizes::Ptr{Ptr{Csize_t}}, out_count::Ptr{Csize_t})::psr_error_t
end

function psr_database_read_set_ints(db, collection, attribute, out_sets, out_sizes, out_count)
@ccall libpsr_database_c.psr_database_read_set_ints(db::Ptr{psr_database_t}, collection::Ptr{Cchar}, attribute::Ptr{Cchar}, out_sets::Ptr{Ptr{Ptr{Int64}}}, out_sizes::Ptr{Ptr{Csize_t}}, out_count::Ptr{Csize_t})::psr_error_t
end

function psr_database_read_set_doubles(db, collection, attribute, out_sets, out_sizes, out_count)
@ccall libpsr_database_c.psr_database_read_set_doubles(db::Ptr{psr_database_t}, collection::Ptr{Cchar}, attribute::Ptr{Cchar}, out_sets::Ptr{Ptr{Ptr{Cdouble}}}, out_sizes::Ptr{Ptr{Csize_t}}, out_count::Ptr{Csize_t})::psr_error_t
end

function psr_database_read_set_strings(db, collection, attribute, out_sets, out_sizes, out_count)
@ccall libpsr_database_c.psr_database_read_set_strings(db::Ptr{psr_database_t}, collection::Ptr{Cchar}, attribute::Ptr{Cchar}, out_sets::Ptr{Ptr{Ptr{Ptr{Cchar}}}}, out_sizes::Ptr{Ptr{Csize_t}}, out_count::Ptr{Csize_t})::psr_error_t
end

function psr_free_int_array(values)
@ccall libpsr_database_c.psr_free_int_array(values::Ptr{Int64})::Cvoid
end
Expand Down
Loading
Loading