2025-11-17 09:31:58 +00:00
|
|
|
package cassandra
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
)
|
|
|
|
|
|
2025-11-18 09:45:38 +00:00
|
|
|
// ErrorCode 定義錯誤代碼
|
|
|
|
|
type ErrorCode string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// ErrCodeNotFound 表示記錄未找到
|
|
|
|
|
ErrCodeNotFound ErrorCode = "NOT_FOUND"
|
|
|
|
|
// ErrCodeConflict 表示衝突(如唯一鍵衝突)
|
|
|
|
|
ErrCodeConflict ErrorCode = "CONFLICT"
|
|
|
|
|
// ErrCodeInvalidInput 表示輸入參數無效
|
|
|
|
|
ErrCodeInvalidInput ErrorCode = "INVALID_INPUT"
|
|
|
|
|
// ErrCodeMissingPartition 表示缺少 Partition Key
|
|
|
|
|
ErrCodeMissingPartition ErrorCode = "MISSING_PARTITION_KEY"
|
|
|
|
|
// ErrCodeNoFieldsToUpdate 表示沒有欄位需要更新
|
|
|
|
|
ErrCodeNoFieldsToUpdate ErrorCode = "NO_FIELDS_TO_UPDATE"
|
|
|
|
|
// ErrCodeMissingTableName 表示缺少 TableName 方法
|
|
|
|
|
ErrCodeMissingTableName ErrorCode = "MISSING_TABLE_NAME"
|
|
|
|
|
// ErrCodeMissingWhereCondition 表示缺少 WHERE 條件
|
|
|
|
|
ErrCodeMissingWhereCondition ErrorCode = "MISSING_WHERE_CONDITION"
|
2025-11-19 05:33:06 +00:00
|
|
|
// ErrCodeSAINotSupported 表示不支援 SAI
|
|
|
|
|
ErrCodeSAINotSupported ErrorCode = "SAI_NOT_SUPPORTED"
|
2025-11-17 09:31:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Error 是統一的錯誤類型
|
|
|
|
|
type Error struct {
|
2025-11-18 09:45:38 +00:00
|
|
|
Code ErrorCode
|
|
|
|
|
Message string
|
|
|
|
|
Table string
|
|
|
|
|
Err error
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error 實現 error 介面
|
|
|
|
|
func (e *Error) Error() string {
|
|
|
|
|
if e.Table != "" {
|
|
|
|
|
if e.Err != nil {
|
2025-11-18 09:45:38 +00:00
|
|
|
return fmt.Sprintf("cassandra[%s] (table: %s): %s: %v", e.Code, e.Table, e.Message, e.Err)
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|
2025-11-18 09:45:38 +00:00
|
|
|
return fmt.Sprintf("cassandra[%s] (table: %s): %s", e.Code, e.Table, e.Message)
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|
|
|
|
|
if e.Err != nil {
|
2025-11-18 09:45:38 +00:00
|
|
|
return fmt.Sprintf("cassandra[%s]: %s: %v", e.Code, e.Message, e.Err)
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|
2025-11-18 09:45:38 +00:00
|
|
|
return fmt.Sprintf("cassandra[%s]: %s", e.Code, e.Message)
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unwrap 返回底層錯誤
|
|
|
|
|
func (e *Error) Unwrap() error {
|
|
|
|
|
return e.Err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithTable 為錯誤添加表名資訊
|
|
|
|
|
func (e *Error) WithTable(table string) *Error {
|
|
|
|
|
return &Error{
|
|
|
|
|
Code: e.Code,
|
|
|
|
|
Message: e.Message,
|
|
|
|
|
Table: table,
|
|
|
|
|
Err: e.Err,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WithError 為錯誤添加底層錯誤
|
|
|
|
|
func (e *Error) WithError(err error) *Error {
|
|
|
|
|
return &Error{
|
|
|
|
|
Code: e.Code,
|
|
|
|
|
Message: e.Message,
|
|
|
|
|
Table: e.Table,
|
|
|
|
|
Err: err,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewError 創建新的錯誤
|
2025-11-18 09:45:38 +00:00
|
|
|
func NewError(code ErrorCode, message string) *Error {
|
2025-11-17 09:31:58 +00:00
|
|
|
return &Error{
|
|
|
|
|
Code: code,
|
|
|
|
|
Message: message,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-18 09:45:38 +00:00
|
|
|
// 預定義錯誤
|
|
|
|
|
var (
|
|
|
|
|
// ErrNotFound 表示記錄未找到
|
|
|
|
|
ErrNotFound = &Error{
|
|
|
|
|
Code: ErrCodeNotFound,
|
|
|
|
|
Message: "record not found",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErrInvalidInput 表示輸入參數無效
|
|
|
|
|
ErrInvalidInput = &Error{
|
|
|
|
|
Code: ErrCodeInvalidInput,
|
|
|
|
|
Message: "invalid input parameter",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErrNoPartitionKey 表示缺少 Partition Key
|
|
|
|
|
ErrNoPartitionKey = &Error{
|
|
|
|
|
Code: ErrCodeMissingPartition,
|
|
|
|
|
Message: "no partition key defined in struct",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErrMissingTableName 表示缺少 TableName 方法
|
|
|
|
|
ErrMissingTableName = &Error{
|
|
|
|
|
Code: ErrCodeMissingTableName,
|
|
|
|
|
Message: "struct must implement TableName() method",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErrNoFieldsToUpdate 表示沒有欄位需要更新
|
|
|
|
|
ErrNoFieldsToUpdate = &Error{
|
|
|
|
|
Code: ErrCodeNoFieldsToUpdate,
|
|
|
|
|
Message: "no fields to update",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErrMissingWhereCondition 表示缺少 WHERE 條件
|
|
|
|
|
ErrMissingWhereCondition = &Error{
|
|
|
|
|
Code: ErrCodeMissingWhereCondition,
|
|
|
|
|
Message: "operation requires at least one WHERE condition for safety",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErrMissingPartitionKey 表示 WHERE 條件中缺少 Partition Key
|
|
|
|
|
ErrMissingPartitionKey = &Error{
|
|
|
|
|
Code: ErrCodeMissingPartition,
|
|
|
|
|
Message: "operation requires all partition keys in WHERE clause",
|
|
|
|
|
}
|
2025-11-19 05:33:06 +00:00
|
|
|
// ErrSAINotSupported 表示不支援 SAI
|
|
|
|
|
ErrSAINotSupported = &Error{
|
|
|
|
|
Code: ErrCodeSAINotSupported,
|
|
|
|
|
Message: "SAI (Storage-Attached Indexing) is not supported in this Cassandra version",
|
|
|
|
|
}
|
2025-11-18 09:45:38 +00:00
|
|
|
)
|
|
|
|
|
|
2025-11-17 09:31:58 +00:00
|
|
|
// IsNotFound 檢查錯誤是否為 NotFound
|
|
|
|
|
func IsNotFound(err error) bool {
|
2025-11-18 09:45:38 +00:00
|
|
|
var e *Error
|
|
|
|
|
if errors.As(err, &e) {
|
|
|
|
|
return e.Code == ErrCodeNotFound
|
|
|
|
|
}
|
|
|
|
|
return false
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-18 09:45:38 +00:00
|
|
|
// IsConflict 檢查錯誤是否為 Conflict
|
|
|
|
|
func IsConflict(err error) bool {
|
|
|
|
|
var e *Error
|
|
|
|
|
if errors.As(err, &e) {
|
|
|
|
|
return e.Code == ErrCodeConflict
|
|
|
|
|
}
|
|
|
|
|
return false
|
2025-11-17 09:31:58 +00:00
|
|
|
}
|