func adbcFromFlightStatusWithDetails()

in go/adbc/driver/flightsql/utils.go [38:141]


func adbcFromFlightStatusWithDetails(err error, header, trailer metadata.MD, context string, args ...any) error {
	if _, ok := err.(adbc.Error); ok {
		return err
	}

	var adbcCode adbc.Status
	// If not a status.Status, will return codes.Unknown
	grpcStatus := status.Convert(err)
	switch grpcStatus.Code() {
	case codes.OK:
		return nil
	case codes.Canceled:
		adbcCode = adbc.StatusCancelled
	case codes.Unknown:
		adbcCode = adbc.StatusUnknown
	case codes.InvalidArgument:
		adbcCode = adbc.StatusInvalidArgument
	case codes.DeadlineExceeded:
		adbcCode = adbc.StatusTimeout
	case codes.NotFound:
		adbcCode = adbc.StatusNotFound
	case codes.AlreadyExists:
		adbcCode = adbc.StatusAlreadyExists
	case codes.PermissionDenied:
		adbcCode = adbc.StatusUnauthorized
	case codes.ResourceExhausted:
		adbcCode = adbc.StatusInternal
	case codes.FailedPrecondition:
		adbcCode = adbc.StatusUnknown
	case codes.Aborted:
		adbcCode = adbc.StatusUnknown
	case codes.OutOfRange:
		adbcCode = adbc.StatusUnknown
	case codes.Unimplemented:
		adbcCode = adbc.StatusNotImplemented
	case codes.Internal:
		adbcCode = adbc.StatusInternal
	case codes.Unavailable:
		adbcCode = adbc.StatusIO
	case codes.DataLoss:
		adbcCode = adbc.StatusIO
	case codes.Unauthenticated:
		adbcCode = adbc.StatusUnauthenticated
	default:
		adbcCode = adbc.StatusUnknown
	}

	details := []adbc.ErrorDetail{}
	for _, detail := range grpcStatus.Proto().Details {
		details = append(details, &anyErrorDetail{name: "grpc-status-details-bin", message: detail})
	}

	// XXX(https://github.com/grpc/grpc-go/issues/5485): don't count on
	// grpc-status-details-bin since Google hardcodes it to only work with
	// Google Cloud
	// XXX: must check both headers and trailers because some implementations
	// (like gRPC-Java) will consolidate trailers into headers for failed RPCs
	for key, values := range header {
		switch {
		case key == "content-type":
			// Not useful info
			continue
		case key == "grpc-status-details-bin":
			// gRPC library parses this above via grpcStatus.Proto()
			continue
		case strings.HasSuffix(key, "-bin"):
			for _, value := range values {
				// that's right, gRPC stuffs binary data into a "string"
				details = append(details, &adbc.BinaryErrorDetail{Name: key, Detail: []byte(value)})
			}
		default:
			for _, value := range values {
				details = append(details, &adbc.TextErrorDetail{Name: key, Detail: value})
			}
		}
	}
	for key, values := range trailer {
		switch {
		case key == "content-type":
			// Not useful info
			continue
		case key == "grpc-status-details-bin":
			// gRPC library parses this above via grpcStatus.Proto()
			continue
		case strings.HasSuffix(key, "-bin"):
			for _, value := range values {
				// that's right, gRPC stuffs binary data into a "string"
				details = append(details, &adbc.BinaryErrorDetail{Name: key, Detail: []byte(value)})
			}
		default:
			for _, value := range values {
				details = append(details, &adbc.TextErrorDetail{Name: key, Detail: value})
			}
		}
	}

	return adbc.Error{
		// People don't read error messages, so backload the context and frontload the server error
		Msg:        fmt.Sprintf("[FlightSQL] %s (%s; %s)", grpcStatus.Message(), grpcStatus.Code(), fmt.Sprintf(context, args...)),
		Code:       adbcCode,
		VendorCode: int32(grpcStatus.Code()),
		Details:    details,
	}
}