Skip to content

Commit 7633d56

Browse files
authored
fix(s3vectors): s3vec type upgrade issue (supabase#580)
* fix(s3vec): s3vec type upgrade issue * fix(build): conditionally generate s3vec SQL only if S3VECTORS_FDW feature is enabled
1 parent 56bffa9 commit 7633d56

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

wrappers/build.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use std::fs;
2+
use std::path::Path;
3+
4+
fn main() {
5+
if std::env::var("CARGO_FEATURE_S3VECTORS_FDW").is_ok() {
6+
generate_s3vec_type_sql();
7+
}
8+
}
9+
10+
/// Generates `s3vec_type_sql.rs` in OUT_DIR, which contains the `pgrx::extension_sql!` call
11+
/// for the S3Vec type with the correct versioned library name embedded as a string literal.
12+
///
13+
/// This indirection is necessary because `pgrx::extension_sql!` requires a string literal
14+
/// (not a macro expression like `concat!(env!(...), ...)`), but we need the library name
15+
/// (e.g. "wrappers-0.6.0") to be derived from the crate version at compile time.
16+
///
17+
/// The generated file is included in `s3vec.rs` via:
18+
/// `include!(concat!(env!("OUT_DIR"), "/s3vec_type_sql.rs"));`
19+
fn generate_s3vec_type_sql() {
20+
let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not set");
21+
let pkg_name = std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME not set");
22+
let pkg_version = std::env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set");
23+
let lib_name = format!("{pkg_name}-{pkg_version}");
24+
25+
// The outer r##"..."## delimiter allows r#"..."# to appear inside the format string.
26+
let content = format!(
27+
r##"// @generated by build.rs — do not edit by hand.
28+
// Library name "{lib_name}" is embedded at compile time from CARGO_PKG_NAME/VERSION.
29+
pgrx::extension_sql!(
30+
r#"DO $s3vec_upgrade$
31+
BEGIN
32+
-- 1. Shell type (must exist before the in/out functions reference it)
33+
BEGIN
34+
CREATE TYPE S3Vec;
35+
EXCEPTION WHEN duplicate_object THEN NULL;
36+
END;
37+
38+
-- 2. Input function
39+
BEGIN
40+
CREATE FUNCTION "s3vec_in"("input" cstring)
41+
RETURNS S3Vec
42+
IMMUTABLE PARALLEL SAFE
43+
LANGUAGE c
44+
AS '{lib_name}', 's3vec_in_wrapper';
45+
EXCEPTION WHEN duplicate_object THEN NULL;
46+
END;
47+
48+
-- 3. Output function
49+
BEGIN
50+
CREATE FUNCTION "s3vec_out"("input" S3Vec)
51+
RETURNS cstring
52+
IMMUTABLE STRICT PARALLEL SAFE
53+
LANGUAGE c
54+
AS '{lib_name}', 's3vec_out_wrapper';
55+
EXCEPTION WHEN duplicate_object THEN NULL;
56+
END;
57+
58+
-- 4. Complete type definition (must come after in/out functions)
59+
BEGIN
60+
CREATE TYPE S3Vec (
61+
INTERNALLENGTH = variable,
62+
INPUT = s3vec_in,
63+
OUTPUT = s3vec_out,
64+
STORAGE = extended
65+
);
66+
EXCEPTION WHEN duplicate_object THEN NULL;
67+
END;
68+
END
69+
$s3vec_upgrade$;
70+
"#,
71+
name = "s3vec_type",
72+
creates = [Type(S3Vec)],
73+
);
74+
"##
75+
);
76+
77+
let out_path = Path::new(&out_dir).join("s3vec_type_sql.rs");
78+
fs::write(&out_path, content)
79+
.unwrap_or_else(|e| panic!("failed to write {}: {e}", out_path.display()));
80+
}

wrappers/src/fdw/s3vectors_fdw/s3vec.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::ffi::CStr;
88

99
#[derive(Debug, Default, PostgresType, Serialize, Deserialize)]
1010
#[inoutfuncs]
11+
#[pgrx(sql = false)] // SQL is written manually below to support idempotent upgrades
1112
pub(super) struct S3Vec {
1213
pub key: String,
1314
pub data: Vec<f32>,
@@ -39,6 +40,19 @@ impl InOutFuncs for S3Vec {
3940
}
4041
}
4142

43+
// Idempotent SQL for S3Vec type and its in/out functions.
44+
//
45+
// pgrx generates plain CREATE TYPE / CREATE FUNCTION (no OR REPLACE / IF NOT EXISTS),
46+
// which fails when upgrading from a version that already has S3Vec. By setting
47+
// sql = false above and writing the SQL here we wrap every statement in a nested
48+
// BEGIN/EXCEPTION WHEN duplicate_object THEN NULL block so the whole block
49+
// succeeds whether or not the objects already exist.
50+
//
51+
// pgrx::extension_sql! requires a string literal, so build.rs generates
52+
// s3vec_type_sql.rs with the versioned library name (e.g. "wrappers-0.6.0")
53+
// embedded as a literal from CARGO_PKG_NAME/VERSION at compile time.
54+
include!(concat!(env!("OUT_DIR"), "/s3vec_type_sql.rs"));
55+
4256
impl From<&ListOutputVector> for S3Vec {
4357
fn from(v: &ListOutputVector) -> Self {
4458
let data = if let Some(VectorData::Float32(vector_data)) = &v.data {

0 commit comments

Comments
 (0)