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

feat: add aggregate example #187

Merged
merged 4 commits into from
Sep 8, 2021
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
2 changes: 1 addition & 1 deletion .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
matrix:
version: [10, 11, 12, 13]
os: ["ubuntu-latest"]
examples: ["arrays", "bad_ideas", "bgworker", "bytea", "custom_types", "errors", "operators", "schemas", "shmem", "spi", "srf", "strings"]
examples: ["aggregate", "arrays", "bad_ideas", "bgworker", "bytea", "custom_types", "errors", "operators", "schemas", "shmem", "spi", "srf", "strings"]

steps:
- uses: actions/checkout@v2
Expand Down
3 changes: 3 additions & 0 deletions pgx-examples/aggregate/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build]
# Postgres symbols won't ve available until runtime
rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"]
7 changes: 7 additions & 0 deletions pgx-examples/aggregate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
.idea/
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
/target
*.iml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of interest, what's an .iml file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dunno!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s a CLion project file

**/*.rs.bk
Cargo.lock
sql/*.generated.sql
36 changes: 36 additions & 0 deletions pgx-examples/aggregate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "aggregate"
version = "0.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

# remove this empty 'workspace' declaration if compiling outside of 'pgx'
[workspace]

[features]
default = ["pg13"]
pg10 = ["pgx/pg10", "pgx-tests/pg10" ]
pg11 = ["pgx/pg11", "pgx-tests/pg11" ]
pg12 = ["pgx/pg12", "pgx-tests/pg12" ]
pg13 = ["pgx/pg13", "pgx-tests/pg13" ]
pg_test = []

[dependencies]
pgx = "0.1.21"
pgx-macros = "0.1.21"
serde = "1.0.130"

[dev-dependencies]
pgx-tests = "0.1.21"

# uncomment these if compiling outside of 'pgx'
#[profile.dev]
#panic = "unwind"
#
#[profile.release]
#panic = "unwind"
#opt-level = 3
#lto = "fat"
#codegen-units = 1
5 changes: 5 additions & 0 deletions pgx-examples/aggregate/aggregate.control
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
comment = 'aggregate: Created by pgx'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought: perhaps we should have a URL that people can visit so they know what pgx is i they encounter it in their logs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest opening an issue if you feel passionate about this in a general case! I think most users would set this to what their Cargo.toml description is!

default_version = '1.0'
module_pathname = '$libdir/aggregate'
relocatable = false
superuser = false
1 change: 1 addition & 0 deletions pgx-examples/aggregate/sql/load-order.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib.generated.sql
116 changes: 116 additions & 0 deletions pgx-examples/aggregate/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use pgx::*;
use std::{
str::FromStr,
ffi::CStr,
};

pg_module_magic!();

#[derive(Copy, Clone, PostgresType)]
#[pgvarlena_inoutfuncs]
pub struct IntegerAvgState {
sum: i32,
n: i32,
}

impl PgVarlenaInOutFuncs for IntegerAvgState {
fn input(input: &CStr) -> PgVarlena<Self> {
let mut result = PgVarlena::<Self>::new();

let mut split = input.to_bytes().split(|b| *b == b',');
let sum = split.next().map(|value|
i32::from_str(unsafe { std::str::from_utf8_unchecked(value) }).expect("invalid i32")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the error message, should we match the Postgres type name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is important, it's an fairly small example. :)

).expect("expected sum");
let n = split.next().map(|value|
i32::from_str(unsafe { std::str::from_utf8_unchecked(value) }).expect("invalid i32")
).expect("expected n");

result.sum = sum;
result.n = n;

result
}
fn output(&self, buffer: &mut StringInfo) {
buffer.push_str(&format!("{},{}", self.sum, self.n));
}
}


impl Default for IntegerAvgState {
fn default() -> Self {
Self { sum: 0, n: 0 }
}
}

impl IntegerAvgState {
fn acc(&self, v: i32) -> PgVarlena<Self> {
let mut new = PgVarlena::<Self>::new();
new.sum = self.sum + v;
new.n = self.n + 1;
new
}
fn finalize(&self) -> i32 {
self.sum / self.n
}
}

#[pg_extern]
fn integer_avg_state_func(
internal_state: PgVarlena<IntegerAvgState>,
next_data_value: i32,
) -> PgVarlena<IntegerAvgState> {
internal_state.acc(next_data_value)
}

#[pg_extern]
fn integer_avg_final_func(internal_state: PgVarlena<IntegerAvgState>) -> i32 {
internal_state.finalize()
}

extension_sql!(
r#"
CREATE AGGREGATE DEMOAVG (integer)
(
sfunc = integer_avg_state_func,
stype = IntegerAvgState,
finalfunc = integer_avg_final_func,
initcond = '0,0'
);
"#
);

#[cfg(any(test, feature = "pg_test"))]
mod tests {
use pgx::*;
use crate::IntegerAvgState;

#[pg_test]
fn test_integer_avg_state() {
assert_eq!(
2,
IntegerAvgState::default().acc(1).acc(2).acc(3).finalize()
);
}

#[pg_test]
fn test_integer_avg_state_sql() {
Spi::run("CREATE TABLE demo_table (value INTEGER);");
Spi::run("INSERT INTO demo_table (value) VALUES (1), (2), (3);");
let retval =
Spi::get_one::<i32>("SELECT DEMOAVG(value) FROM demo_table;")
.expect("SQL select failed");
assert_eq!(retval, 2);
}
}

#[cfg(test)]
pub mod pg_test {
pub fn setup(_options: Vec<&str>) {
// perform one-off initialization when the pg_test framework starts
}

pub fn postgresql_conf_options() -> Vec<&'static str> {
// return any postgresql.conf settings that are required for your tests
vec![]
}
}
2 changes: 1 addition & 1 deletion pgx-examples/custom_types/src/fixed_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct FixedF32Array {

impl PgVarlenaInOutFuncs for FixedF32Array {
fn input(input: &CStr) -> PgVarlena<Self> {
let mut result = PgVarlena::<FixedF32Array>::new();
let mut result = PgVarlena::<Self>::new();

for (i, value) in input.to_bytes().split(|b| *b == b',').enumerate() {
result.array[i] =
Expand Down