You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套查询导致sqlexp.ReturnMessage获取RaisError消息阻塞异常

无法捕获SQL存储过程中特定RAISERROR消息的Go代码问题

我用Go代码调用SQL存储过程,尝试捕获存储过程中用RAISERROR抛出的消息,但只能获取前两条,第三条消息无法获取,程序会在retmsg.Message(ctx)处阻塞。

Go调用代码

retmsg := &sqlexp.ReturnMessage{}
rows, err = db.QueryContext(ctx, query, retmsg)
if err != nil {
    return
}
active := true
for active {
    msg := retmsg.Message(ctx)
    switch m := msg.(type) {
    case sqlexp.MsgNotice:
        result = fmt.Sprintf("%s", m.Message)
        return
    case sqlexp.MsgNext:
        inresult := true
        for inresult {
            inresult = rows.Next()
            if inresult {
                var d interface{}
                if err = rows.Scan(&d); err == nil {
                    result = fmt.Sprintf("%s %v", result, d)
                }
            }
        }
    case sqlexp.MsgNextResultSet:
        active = rows.NextResultSet()
    case sqlexp.MsgError:
        result = fmt.Sprintf("%s", m.Error)
    }
}

SQL存储过程代码

declare @err_message nvarchar(255), 
        @err_count int = 0
BEGIN
    if (isnull(@InStorageID,0)=0 and isnull(@OutStorageID,0)=0)  
     begin
       SET @err_message = ' Message: empty storage '
       select @err_count=@err_count+1
       RAISERROR (@err_message,10, 1) 
     end

     if (@Quantity<0 or @Quantity=0) 
     begin
       SET @err_message = ' Message: items quantity less than zero '
       select @err_count=@err_count+1
       RAISERROR (@err_message,10, 1) 
     end

     if not exists (select 1 from [dbo].[tblQuantity] qt
                where qt.ConsignmentID=@ConsignmentID and 
                      qt.StorageID=@OutStorageID and
                      (qt.Quantity-@Quantity)>=0
                 ) and @OutStorageID>0
     begin
       SET @err_message = ' Message: the quantity of goods is more than what is in stock '
       select @err_count=@err_count+1
       RAISERROR (@err_message,10, 1) 
     end

     if (@err_count=0)...

问题细节

目前能获取到"Message: empty storage"和"Message: items quantity less than zero",但第三条消息无法获取,程序会卡在msg := retmsg.Message(ctx),进入sqlexp包的这段代码后阻塞:

func (m *ReturnMessage) Message(ctx context.Context) RawMessage {
    select {
    case <-ctx.Done():
        return MsgNextResultSet{}
    case raw := <-m.queue:
        return raw
    }
}

已经尝试修改上下文、给sqlexp代码添加default分支,均无效。怀疑是嵌套查询执行耗时过长,但不确定。


解决方案分析

1. 验证存储过程触发条件

第三条RAISERROR前的NOT EXISTS查询需要满足多个条件才会执行:@OutStorageID>0、@ConsignmentID对应记录存在、库存不足。先确认传入的参数是否完全符合触发逻辑,若参数不满足,这条RAISERROR根本不会执行,自然无消息抛出。

2. 修复Go代码的消息处理逻辑

现有代码捕获到第一条sqlexp.MsgNotice后直接return,导致后续消息被忽略:

case sqlexp.MsgNotice:
    result = fmt.Sprintf("%s", m.Message)
    return // 直接返回终止流程,后续消息无法处理

需改为追加消息并继续循环:

case sqlexp.MsgNotice:
    result += fmt.Sprintf("%s", m.Message)
    // 移除return,继续处理后续消息

3. 正确消费所有结果集

存储过程中每个RAISERROR后都有SELECT @err_count=@err_count+1,会产生独立结果集。若未正确消费所有结果集,驱动会阻塞等待处理。修改后的循环需加入错误检查:

active := true
for active {
    msg := retmsg.Message(ctx)
    switch m := msg.(type) {
    case sqlexp.MsgNotice:
        result += fmt.Sprintf("%s", m.Message)
    case sqlexp.MsgNext:
        inresult := true
        for inresult {
            inresult = rows.Next()
            if inresult {
                var d interface{}
                if err = rows.Scan(&d); err != nil {
                    result += fmt.Sprintf("Scan error: %v", err)
                    break
                }
                result = fmt.Sprintf("%s %v", result, d)
            }
        }
        // 检查结果集遍历后的错误
        if err = rows.Err(); err != nil {
            result += fmt.Sprintf("Result set error: %v", err)
            break
        }
    case sqlexp.MsgNextResultSet:
        active = rows.NextResultSet()
        // 切换结果集后检查错误
        if err = rows.Err(); err != nil {
            result += fmt.Sprintf("ResultSet switch error: %v", err)
            active = false
        }
    case sqlexp.MsgError:
        result += fmt.Sprintf("%s", m.Error)
    }
}

4. 设置上下文超时

若嵌套查询确实耗时过长,给ctx设置合理超时避免无限阻塞:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
rows, err = db.QueryContext(ctx, query, retmsg)

内容的提问来源于stack exchange,提问作者Spison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:37:51