rust Sqlx:将类型为RECORD[]的Postgresql列转换/检索为类型为Vec的结构字段< _>

j2cgzkjk  于 2023-10-20  发布在  PostgreSQL
关注(0)|答案(3)|浏览(170)

我有以下两个struct:

#[derive(Serialize, Deserialize)]
struct Login {
    email: String,
    oidc_id: String
}

#[derive(Serialize, Deserialize)]
struct Account {
    id: Uuid,
    created: NaiveDateTime,
    private_key: String,
    public_key: String,
    logins: Vec<Login>
}

我想使用它们通过Sqlx从Postgres读取数据,如下所示:

let result = sqlx::query_as!(
    Account,
    "select *, ARRAY(
        select ROW(email, oidc_id)
            from logins where logins.account_id = accounts.id
        ) as logins from accounts"
).fetch_all(pool).await?;

有没有可能在Postgres中不创建自定义记录类型的情况下让它工作?我尝试实现FromRow,但Sqlx仍然抱怨不支持RECORD[]类型。我也尝试了派生sqlx::Type,但没有成功。
像这样检索数据将非常方便,但如果我必须为每个可能的查询声明一个自定义记录类型,就不方便了。

bwleehnv

bwleehnv1#

我也遇到过类似的问题(见这里)。
另外,我不确定我是否理解你的查询的目的。因此,对于我的示例,让我们假设您只想检索所有帐户及其登录信息:

let accounts = sqlx::query_as!(
    Account,
    r#"
    SELECT
        A.id,
        A.created,
        A.private_key,
        A.public_key,
        array_agg((L.email, L.oidc_id)) as "logins!: Vec<Login>"
    FROM accounts A
    JOIN logins L ON A.account_id = L.account_id
    "#
)
.fetch_all(pool)
.await?;

但是,要做到这一点,您需要#[derive(sqlx::Type)]您的嵌套类型。
这是因为as "logins!: Vec<Login>为SQLx提供了额外的类型信息。请记住,由于限制,您只能通过一个嵌套您的日期。

6ojccjat

6ojccjat2#

试试这个:

#[derive(Serialize, Deserialize)]
struct RawAccount {
    id: Uuid,
    created: NaiveDateTime,
    private_key: String,
    public_key: String,
}

let raw_accounts: Vec<RawAccount> = sqlx::query_as!(
    RawAccount,
    "SELECT id, created, private_key, public_key FROM accounts"
)
.fetch_all(pool)
.await?;

let mut accounts: Vec<Account> = Vec::new();

for raw_account in raw_accounts {
    let logins: Vec<Login> = sqlx::query_as!(
        Login,
        "SELECT email, oidc_id FROM logins WHERE account_id = $1",
        raw_account.id
    )
    .fetch_all(pool)
    .await?;

    let account = Account {
        id: raw_account.id,
        created: raw_account.created,
        private_key: raw_account.private_key,
        public_key: raw_account.public_key,
        logins,
    };

    accounts.push(account);
}
rta7y2nd

rta7y2nd3#

根据我使用sqlx的经验,我可以说99%的问题都可以通过类型转换解决,包括这个问题。从PostGres文档:
默认情况下,ROW表达式创建的值是 * 匿名记录类型 *。如果需要,可以将其转换为命名的复合类型-表的行类型,或使用CREATE TYPE AS创建的 * 复合类型 *。可能需要显式强制转换以避免歧义。

CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);

SELECT CAST(ROW(11, 'this is a test', 2.5) AS myrowtype);

给它一个名称允许sqlx匹配rust端的类型,这样可能会起作用。另一种方法是转换为表类型,语法:ROW(...)::table

SELECT
  *,
  ARRAY(
    SELECT ROW(email, oidc_id)::logins
      FROM logins
      WHERE logins.account_id = accounts.id
  ) AS logins
FROM accounts;

相关问题