亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

使用 pq 和 Postgres 對約束進行適當的錯誤處理

使用 pq 和 Postgres 對約束進行適當的錯誤處理

Go
開心每一天1111 2021-12-07 15:12:31
我目前正在使用pqGo的lib 與我的 PostgreSQL 數據庫進行通信。事實證明,錯誤檢查比預期的要困難一些。描述我的問題的最簡單方法是通過一個示例場景。想象一個網絡表單:Username  ________Email     ________Voucher   ________Password  ________一個粗略的模式:username VARCHAR(255) UNIQUE NOT NULL,email VARCHAR(255) UNIQUE NOT NULL,voucher VARCHAR(255) UNIQUE NOT NULL,password VARCHAR(255) NOT NULL暫時忽略假定的純文本密碼。如果有人提交表單,我可以進行所有驗證以驗證諸如長度/允許的字符/等約束?,F在要放到數據庫中了,所以我們寫一個準備好的語句并執行它。如果驗證正確完成,唯一可能真正出錯的是UNIQUE約束。如果有人試圖輸入現有的用戶名,database/sql 將觸發一個錯誤。我的問題是我不知道如何處理該錯誤并從(應該是)可恢復的錯誤中恢復。pq對此提供了一些支持,但返回的內容似乎仍然存在歧義。我可以看到兩種解決方案,這兩種解決方案對我來說都不是特別有吸引力:SERIALIZABLE在插入之前檢查每個表單值的事務?;蛘?,對 pq 錯誤結構進行某種形式的解析。是否有實現這樣一個系統的通用模式?我希望能夠對用戶說Sorry that username exists而不是Sorry something bad happened作為旁注,PostgreSQL 文檔指出:模式名稱、表名稱、列名稱、數據類型名稱和約束名稱的字段僅針對有限數量的錯誤類型提供;見 附錄 A。但是鏈接頁面對于數據庫對象中返回的值不是很有幫助。
查看完整描述

1 回答

?
幕布斯6054654

TA貢獻1876條經驗 獲得超7個贊

如果驗證正確完成,唯一可能真正出錯的是 UNIQUE 約束。


不,客戶可能缺乏足夠的權限,客戶可能輸入了不是正確密碼的有效密碼,客戶可能輸入了屬于不同客戶的有效憑證等。


使用“在插入之前檢查每個表單值的 SERIALIZABLE 事務”沒有意義。只需插入數據,并捕獲錯誤。


至少,您的代碼需要檢查并響應 C(代碼)字段,該字段始終存在于錯誤結構中。您不需要解析錯誤結構,但確實需要閱讀它。


如果違反了唯一約束,PostgreSQL 將在代碼字段中返回 SQL 狀態 23505。它還將返回違反的第一個約束的名稱。它不返回列名,可能是因為唯一約束可以包含多個列。


您可以通過查詢 information_schema 視圖來選擇約束引用的列。


這是您的表格的簡單版本。


create table test (

  username VARCHAR(255) UNIQUE NOT NULL,

  email VARCHAR(255) UNIQUE NOT NULL,

  voucher VARCHAR(255) UNIQUE NOT NULL,

  password VARCHAR(255) NOT NULL

);


insert into test values ('msherrill', '[email protected]', 'a', 'wibble');

這個快速而骯臟的 go 程序再次插入同一行。它違反了每一個唯一的約束。


package main


import (

    "github.com/lib/pq"

    "database/sql"

    "fmt"

    "log"

)


func main() {

    db, err := sql.Open("postgres", "host=localhost port=5435 user=postgres password=xxxxxxxx dbname=scratch sslmode=disable")

    if err != nil {

        log.Fatal(err)

    }


    rows, err := db.Exec("insert into public.test values ('msherrill', '[email protected]', 'a', 'wibble');")

    if err, ok := err.(*pq.Error); ok {

        fmt.Println("Severity:", err.Severity)

        fmt.Println("Code:", err.Code)

        fmt.Println("Message:", err.Message)

        fmt.Println("Detail:", err.Detail)

        fmt.Println("Hint:", err.Hint)

        fmt.Println("Position:", err.Position)

        fmt.Println("InternalPosition:", err.InternalPosition)

        fmt.Println("Where:", err.Where)

        fmt.Println("Schema:", err.Schema)

        fmt.Println("Table:", err.Table)

        fmt.Println("Column:", err.Column)

        fmt.Println("DataTypeName:", err.DataTypeName)

        fmt.Println("Constraint:", err.Constraint)

        fmt.Println("File:", err.File)

        fmt.Println("Line:", err.Line)

        fmt.Println("Routine:", err.Routine)

    }

   fmt.Println(rows)

}

這是輸出。


嚴重性:錯誤

代碼:23505

消息:重復鍵值違反唯一約束“test_username_key”

詳細信息:密鑰(用戶名)=(msherrill)已經存在。

暗示: 

位置: 

內部位置: 

在哪里: 

架構:公共

表:測試

柱子: 

數據類型名稱: 

約束:test_username_key

文件:nbtinsert.c

線路:406

例程:_bt_check_unique

您有架構、表和約束名稱。您大概也知道數據庫(目錄)名稱。使用這些值從 information_schema 視圖中選擇模式、表和列名稱。你很幸運;在這種情況下,您只需要一個視圖。


select table_catalog, table_schema, table_name, column_name 

from information_schema.key_column_usage

where 

    table_catalog = 'scratch' and          -- Database name

    table_schema = 'public' and            -- value returned by err.Schema

    table_name = 'test' and                -- value returned by err.Table

    constraint_name = 'test_username_key'  -- value returned by err.Constraint

order by constraint_catalog, constraint_schema, constraint_name, ordinal_position;


查看完整回答
反對 回復 2021-12-07
  • 1 回答
  • 0 關注
  • 196 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號