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

Low-level interop for PG arrays #636

Merged
merged 37 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e8650f7
c-shims for PG array functions
mhov Aug 17, 2022
39566c0
suggested changes for shims
mhov Aug 19, 2022
98196df
Merge branch 'develop' into array-shims
workingjubilee Aug 23, 2022
4eea77c
Fix cshim exports
workingjubilee Aug 24, 2022
9cffc1b
Introduce RawArray abstraction
workingjubilee Aug 24, 2022
48176a2
Port ARR_HASNULL to RawArray
workingjubilee Aug 24, 2022
82790eb
Clarify comment
workingjubilee Aug 24, 2022
d67dbf0
Introduce fat pointer fns into RawArray
workingjubilee Aug 24, 2022
431e89e
Port ARR_DATA_PTR to RawArray::data_slice
workingjubilee Aug 24, 2022
a60a6dd
Clarify safety requirements for RawArray::dims
workingjubilee Aug 24, 2022
dfcfbb6
Clarify safety requirements for RawArray::data
workingjubilee Aug 24, 2022
e5fc2f5
Cleanup
workingjubilee Aug 24, 2022
48ccccb
Fix RawArray::data_offset signature
workingjubilee Aug 24, 2022
11c5a63
Fix testing
workingjubilee Aug 24, 2022
d74719c
Port ARR_NULLBITMAP to RawArray::nulls
workingjubilee Aug 24, 2022
6900356
Document test safety remarks
workingjubilee Aug 24, 2022
bbad726
sub pub from pgx::array extern fn
workingjubilee Aug 24, 2022
133b44d
Explain Rust type init requirements
workingjubilee Aug 25, 2022
59dac25
Expand on RawArray description
workingjubilee Aug 25, 2022
0da6f1c
Note reborrow in test
workingjubilee Aug 25, 2022
7bfa2e4
Remove extra comment in pgx::array
workingjubilee Aug 25, 2022
55f62ac
Introduce even-lower-level ArrayPtr
workingjubilee Aug 26, 2022
b4a7364
Reformat
workingjubilee Aug 26, 2022
f763c0a
Rough draft of ArrayPtr and RawArray
workingjubilee Aug 26, 2022
b0b3477
Format again
workingjubilee Aug 26, 2022
639d0b4
Merge back into RawArray
workingjubilee Aug 26, 2022
55deefe
Cleanup fn that might be dupes or confusing
workingjubilee Aug 26, 2022
1785c07
Clean up RawArray docs and comments
workingjubilee Aug 26, 2022
16cf849
Add dims_mut, to see if it works
workingjubilee Aug 26, 2022
e2a3509
Cleanup and explanations for RawArray::{dims_mut, nulls}
workingjubilee Aug 26, 2022
9979f77
Format
workingjubilee Aug 26, 2022
f15eabd
Add hint about lens
workingjubilee Aug 26, 2022
b2c9508
Remove unnecessary unsafe on test
workingjubilee Aug 26, 2022
4ba1b0d
Lift remarks into public docs
workingjubilee Aug 26, 2022
5ab4ba8
One last format
workingjubilee Aug 26, 2022
fd3c21d
One last cleanup
workingjubilee Aug 26, 2022
1dd9155
Remove dubious dims_mut function
workingjubilee Aug 26, 2022
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
31 changes: 31 additions & 0 deletions pgx-pg-sys/cshim/pgx-cshim.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Use of this source code is governed by the MIT license that can be found in the
#include "parser/parsetree.h"
#include "utils/memutils.h"
#include "utils/builtins.h"
#include "utils/array.h"


PGDLLEXPORT MemoryContext pgx_GetMemoryContextChunk(void *ptr);
Expand Down Expand Up @@ -110,3 +111,33 @@ PGDLLEXPORT char *pgx_GETSTRUCT(HeapTuple tuple);
char *pgx_GETSTRUCT(HeapTuple tuple) {
return GETSTRUCT(tuple);
}

PGDLLEXPORT char *pgx_ARR_DATA_PTR(ArrayType *arr);
char *pgx_ARR_DATA_PTR(ArrayType *arr) {
return ARR_DATA_PTR(arr);
}

PGDLLEXPORT int pgx_ARR_NELEMS(ArrayType *arr);
int pgx_ARR_NELEMS(ArrayType *arr) {
return ArrayGetNItems(arr->ndim, ARR_DIMS(arr));
}

PGDLLEXPORT bits8 *pgx_ARR_NULLBITMAP(ArrayType *arr);
bits8 *pgx_ARR_NULLBITMAP(ArrayType *arr) {
return ARR_NULLBITMAP(arr);
}

PGDLLEXPORT int pgx_ARR_NDIM(ArrayType *arr);
int pgx_ARR_NDIM(ArrayType *arr) {
return ARR_NDIM(arr);
}

PGDLLEXPORT bool pgx_ARR_HASNULL(ArrayType *arr);
bool pgx_ARR_HASNULL(ArrayType *arr) {
return ARR_HASNULL(arr);
}

PGDLLEXPORT int *pgx_ARR_DIMS(ArrayType *arr);
int *pgx_ARR_DIMS(ArrayType *arr){
return ARR_DIMS(arr);
}
84 changes: 84 additions & 0 deletions pgx-tests/src/tests/array_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All rights reserved.
Use of this source code is governed by the MIT license that can be found in the LICENSE file.
*/

use pgx::array::RawArray;
use pgx::*;
use serde_json::*;

Expand Down Expand Up @@ -99,6 +100,44 @@ fn return_zero_length_vec() -> Vec<i32> {
Vec::new()
}

#[pg_extern]
fn get_arr_nelems(arr: Array<i32>) -> libc::c_int {
// SAFETY: Eh it's fine, it's just a len check.
unsafe { RawArray::from_array(arr) }.unwrap().len() as _
}

#[pg_extern]
fn get_arr_data_ptr_nth_elem(arr: Array<i32>, elem: i32) -> Option<i32> {
// SAFETY: this is Known to be an Array from ArrayType,
// and it's valid-ish to see any bitpattern of an i32 inbounds of a slice.
unsafe {
let raw = RawArray::from_array(arr).unwrap().data::<i32>();
let slice = &(*raw.as_ptr());
slice.get(elem as usize).copied()
}
}

#[pg_extern]
fn display_get_arr_nullbitmap(arr: Array<i32>) -> String {
let raw = unsafe { RawArray::from_array(arr) }.unwrap();

if let Some(slice) = raw.nulls() {
// SAFETY: If the test has gotten this far, the ptr is good for 0+ bytes,
// so reborrow NonNull<[u8]> as &[u8] for the hot second we're looking at it.
let slice = unsafe { &*slice.as_ptr() };
BradyBonnette marked this conversation as resolved.
Show resolved Hide resolved
// might panic if the array is len 0
format!("{:#010b}", slice[0])
} else {
String::from("")
}
}

#[pg_extern]
fn get_arr_ndim(arr: Array<i32>) -> libc::c_int {
// SAFETY: This is a valid ArrayType and it's just a field access.
unsafe { RawArray::from_array(arr) }.unwrap().dims().len() as _
}

#[pg_extern]
fn over_implicit_drop() -> Vec<i64> {
// Create an array of exactly Datum-sized numbers.
Expand Down Expand Up @@ -255,6 +294,51 @@ mod tests {
assert_eq!(json.0, json! {{"values": [1, 2, 3, null, 4]}});
}

#[pg_test]
fn test_arr_data_ptr() {
let len = Spi::get_one::<i32>("SELECT get_arr_nelems('{1,2,3,4,5}'::int[])")
.expect("failed to get SPI result");

assert_eq!(len, 5);
}

#[pg_test]
fn test_get_arr_data_ptr_nth_elem() {
let nth = Spi::get_one::<i32>("SELECT get_arr_data_ptr_nth_elem('{1,2,3,4,5}'::int[], 2)")
.expect("failed to get SPI result");

assert_eq!(nth, 3);
}

#[pg_test]
fn test_display_get_arr_nullbitmap() {
let bitmap_str = Spi::get_one::<String>(
"SELECT display_get_arr_nullbitmap(ARRAY[1,NULL,3,NULL,5]::int[])",
)
.expect("failed to get SPI result");

assert_eq!(bitmap_str, "0b00010101");
workingjubilee marked this conversation as resolved.
Show resolved Hide resolved

let bitmap_str =
Spi::get_one::<String>("SELECT display_get_arr_nullbitmap(ARRAY[1,2,3,4,5]::int[])")
.expect("failed to get SPI result");

assert_eq!(bitmap_str, "");
}

#[pg_test]
fn test_get_arr_ndim() {
let ndim = Spi::get_one::<i32>("SELECT get_arr_ndim(ARRAY[1,2,3,4,5]::int[])")
.expect("failed to get SPI result");

assert_eq!(ndim, 1);

let ndim = Spi::get_one::<i32>("SELECT get_arr_ndim('{{1,2,3},{4,5,6}}'::int[])")
.expect("failed to get SPI result");

assert_eq!(ndim, 2);
}

#[pg_test]
fn test_array_over_direct() {
let vals = crate::tests::array_tests::over_implicit_drop();
Expand Down
Loading