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

RFC: Support User Defined types #499

Open
Hoverbear opened this issue Mar 23, 2022 · 3 comments
Open

RFC: Support User Defined types #499

Hoverbear opened this issue Mar 23, 2022 · 3 comments
Labels
rfc rust+sql Interop between Rust and SQL

Comments

@Hoverbear
Copy link
Contributor

Hoverbear commented Mar 23, 2022

pgx should be able to handle user defined types (such as those defined via CREATE TYPE...) in function argument and return value position.

Since user defined types can be created, removed, or altered at runtime, the pgx extension itself cannot possibly know the structure of the type at build time, thus we cannot possibly create some equivalent Rust type and define a mapping between them.

Instead, pgx needs to determine that some type is a user defined type, and use some UserDefinedType type which provides some serde_json::Value-like interface with runtime safety. We can determine the runtime layout of a type via inspecting the result of a query like \dT+ example.type_name in psql, there are also methods via SPI through the pg_catalog.pg_type table.

So if the user defined something like:

#[pgx::pg_extern]
fn user_function(
    arg: pgx::UserDefinedType
) -> pgx::UserDefinedType {
    unimplemented!();
}

However this doesn't communicate enough information to the SQL generator to create a valid function signature (unless we fall back to something like any, which is undesirable).

In order to augment this information we could use something like a type!() macro that operates similar to the name!() macro. We could also consider having some trait pgx::UserDefinedType which users could do like:

mod pgx {
    pub trait UserDefinedType {
        const SQL_TYPE: &'static str;
    }
}

struct SomeUserType;

impl pgx::UserDefinedType for SomeUserType {
    const SQL_TYPE: &'static str = "MySqlType";
}

#[pg_extern]
fn user_function(
    arg: SomeUserType
) -> SomeUserType {
    unimplemented!();
}

It's important to note though that the struct does not (and cannot) map directly to the type, instead, the pgx::UserDefinedType trait would need to provide methods.

@Hoverbear
Copy link
Contributor Author

If we had rust-lang/rust#95174 we could do like this and then capture it with the SQL generator macros:

#![feature(adt_const_params)]

mod pgx {
    pub trait UserDefined<const SQL_TYPE: &'static str> {}
    pub struct UserDefinedType<const T: &'static str>;
}

struct SomeUserType;

impl pgx::UserDefined<"BEAN"> for SomeUserType { }


fn user_function(
    arg: SomeUserType
) -> SomeUserType {
    unimplemented!();
}

fn other_user_function(
    arg: pgx::UserDefinedType<"BEET">
) -> SomeUserType {
    unimplemented!();
}

@Hoverbear
Copy link
Contributor Author

Hoverbear commented Jun 1, 2022

From a "Correct Rust" level, this is what we should be doing, lacking adt_const_params:

const IMPORTANT_SQL_TYPE: &'static str = "Bean";
const UNIMPORTANT_SQL_TYPE: &'static str = "Beet";

#[pgx::pg_extern(return_sql_type = UNIMPORTANT_SQL_TYPE)]
fn user_function(
    arg: #[pgx(sql_type = IMPORTANT_SQL_TYPE)] pgx::UserDefinedType
) -> pgx::UserDefinedType {
    unimplemented!();
}

However the aesthetic of this isn't super desirable. So it might be preferable if we have something like

const IMPORTANT_SQL_TYPE: &'static str = "Bean";
const UNIMPORTANT_SQL_TYPE: &'static str = "Beet";

#[pgx::pg_extern]
fn user_function(
    arg: pgx::user_defined!(IMPORTANT_SQL_TYPE)
) -> pgx::user_defined!(UNIMPORTANT_SQL_TYPE) {
    unimplemented!();
}

Expand into the "correct" form.

@johnrballard
Copy link
Contributor

johnrballard commented Aug 18, 2022

@workingjubilee workingjubilee added fixed-by:0.5.0 rust+sql Interop between Rust and SQL and removed fixed-by:0.5.0 labels Aug 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfc rust+sql Interop between Rust and SQL
Projects
None yet
Development

No branches or pull requests

3 participants