前言
在执行数据库插入语句的时候,返回错误语句“ non-Value type UserRowStatus returned from Value”。在网络上查询大量资料后发现,目前对于此错误的记录比较少,特此分析该错误情况和分析过程
背景
mysql version:mysql Ver 14.14 Distrib 5.7.41, for Linux (x86_64) using EditLine wrapper
go version:go1.19.3 darwin/arm64
sqlc version:v1.16.0
sql driver version:github.com/go-sql-driver/mysql v1.7.1
本项目使用sqlc工具包,根据SQL语句,自动生成惯用的数据库操作代码
sqlc工具包链接:https://sqlc.dev
环境说明
数据库创建语句
CREATE TABLE `user`
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`created_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`row_status` ENUM ('NORMAL', 'ARCHIVED') NOT NULL DEFAULT "NORMAL",
`role` ENUM ('HOST', 'ADMIN', 'USER') NOT NULL DEFAULT "USER",
`username` varchar(255) UNIQUE NOT NULL,
`email` varchar(255) NOT NULL DEFAULT "",
`nickname` varchar(255) NOT NULL DEFAULT "",
`password_hash` varchar(255) NOT NULL DEFAULT "",
`open_id` varchar(255) UNIQUE NOT NULL,
`avatar_url` varchar(255) NOT NULL DEFAULT ""
);
数据库操作语句(sqlc根据操作语句,自动生成数据库操作代码)
-- name: PatchUser :exec
UPDATE user
SET updated_ts = COALESCE(sqlc.narg(updated_ts), updated_ts),
row_status = COALESCE(sqlc.narg(row_status), row_status),
username = COALESCE(sqlc.narg(username), username),
email = COALESCE(sqlc.narg(email), email),
nickname = COALESCE(sqlc.narg(nickname), nickname),
avatar_url = COALESCE(sqlc.narg(avatar_url), avatar_url),
password_hash = COALESCE(sqlc.narg(password_hash), password_hash),
open_id = COALESCE(sqlc.narg(open_id), open_id)
WHERE id = ?;
数据库操作代码(该代码由sqlc自动生成)
PatchUser:更新用户信息的数据库操作函数
PatchUserParams:更新用户信息的参数
NullUserRowStatus:更新用户信息中UserRowStatus枚举数据类型
type NullUserRowStatus struct {
UserRowStatus UserRowStatus
Valid bool // Valid is true if UserRowStatus is not NULL
}
type PatchUserParams struct {
UpdatedTs sql.NullTime
RowStatus NullUserRowStatus
Username sql.NullString
Email sql.NullString
Nickname sql.NullString
AvatarUrl sql.NullString
PasswordHash sql.NullString
OpenID sql.NullString
ID int32
}
func (q *Queries) PatchUser(ctx context.Context, arg PatchUserParams) error {
_, err := q.db.ExecContext(ctx, patchUser,
arg.UpdatedTs,
arg.RowStatus,
arg.Username,
arg.Email,
arg.Nickname,
arg.AvatarUrl,
arg.PasswordHash,
arg.OpenID,
arg.ID,
)
return err
}
问题分析
-
错误语句“ non-Value type UserRowStatus returned from Value”是在sql driver中打印出来的错误语句,所以我们需要去sql driver中寻找错误的出处
-
找到错误语句的出处,并分析上下环境。发现问题出现在IsValue函数的返回值上
-
深入解析IsValue函数
sql driver只支持六种数据类型:[]byte, bool, float64, int64, string, time.Time
显然UserRowStatus不在其中,所有返回错误
问题总结
由于在数据库创建语句中定义了枚举类型,然后sqlc生成数据库操作函数代码的时候,定义了新的结构体UserRowStatus来实现枚举。然而数据库操作中又不支持UserRowStatus的数据类型操作,所以返回错误语句“ non-Value type UserRowStatus returned from Value”
**接下来,**为了继续使用sqlc工具包(主要是因为快捷好用,不用自己去苦逼得写数据库操作函数),只能将数据库建表语句中的枚举类型去掉,改成从数据库操作函数层对数据内容进行约束
在网上也找到一个类似的案例:https://www.coder.work/article/210950