github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/scripting/client.go (about) 1 package scripting 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/ydb-platform/ydb-go-genproto/Ydb_Scripting_V1" 8 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 9 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Scripting" 10 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_TableStats" 11 "google.golang.org/grpc" 12 13 "github.com/ydb-platform/ydb-go-sdk/v3/internal/allocator" 14 "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" 15 "github.com/ydb-platform/ydb-go-sdk/v3/internal/params" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting/config" 17 "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" 18 "github.com/ydb-platform/ydb-go-sdk/v3/internal/table/scanner" 19 "github.com/ydb-platform/ydb-go-sdk/v3/internal/types" 20 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" 21 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 22 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 23 "github.com/ydb-platform/ydb-go-sdk/v3/scripting" 24 "github.com/ydb-platform/ydb-go-sdk/v3/table" 25 "github.com/ydb-platform/ydb-go-sdk/v3/table/result" 26 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 27 ) 28 29 //nolint:gofumpt 30 //nolint:nolintlint 31 var ( 32 errNilClient = xerrors.Wrap(errors.New("scripting client is not initialized")) 33 ) 34 35 type ( 36 Client struct { 37 config config.Config 38 service Ydb_Scripting_V1.ScriptingServiceClient 39 } 40 ) 41 42 func (c *Client) Execute( 43 ctx context.Context, 44 query string, 45 parameters *params.Parameters, 46 ) (r result.Result, err error) { 47 if c == nil { 48 return r, xerrors.WithStackTrace(errNilClient) 49 } 50 call := func(ctx context.Context) error { 51 r, err = c.execute(ctx, query, parameters) 52 53 return xerrors.WithStackTrace(err) 54 } 55 if !c.config.AutoRetry() { 56 err = call(ctx) 57 58 return 59 } 60 err = retry.Retry(ctx, call, 61 retry.WithStackTrace(), 62 retry.WithTrace(c.config.TraceRetry()), 63 retry.WithBudget(c.config.RetryBudget()), 64 ) 65 66 return r, xerrors.WithStackTrace(err) 67 } 68 69 func (c *Client) execute( 70 ctx context.Context, 71 query string, 72 parameters *params.Parameters, 73 ) (r result.Result, err error) { 74 var ( 75 onDone = trace.ScriptingOnExecute(c.config.Trace(), &ctx, 76 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting.(*Client).execute"), 77 query, parameters, 78 ) 79 a = allocator.New() 80 request = &Ydb_Scripting.ExecuteYqlRequest{ 81 Script: query, 82 Parameters: parameters.ToYDB(a), 83 OperationParams: operation.Params( 84 ctx, 85 c.config.OperationTimeout(), 86 c.config.OperationCancelAfter(), 87 operation.ModeSync, 88 ), 89 } 90 result = Ydb_Scripting.ExecuteYqlResult{} 91 response *Ydb_Scripting.ExecuteYqlResponse 92 ) 93 defer func() { 94 a.Free() 95 onDone(r, err) 96 }() 97 response, err = c.service.ExecuteYql(ctx, request) 98 if err != nil { 99 return nil, xerrors.WithStackTrace(err) 100 } 101 102 err = response.GetOperation().GetResult().UnmarshalTo(&result) 103 if err != nil { 104 return nil, xerrors.WithStackTrace(err) 105 } 106 107 return scanner.NewUnary(result.GetResultSets(), result.GetQueryStats()), nil 108 } 109 110 func mode2mode(mode scripting.ExplainMode) Ydb_Scripting.ExplainYqlRequest_Mode { 111 switch mode { 112 case scripting.ExplainModePlan: 113 return Ydb_Scripting.ExplainYqlRequest_PLAN 114 case scripting.ExplainModeValidate: 115 return Ydb_Scripting.ExplainYqlRequest_VALIDATE 116 default: 117 return Ydb_Scripting.ExplainYqlRequest_MODE_UNSPECIFIED 118 } 119 } 120 121 func (c *Client) Explain( 122 ctx context.Context, 123 query string, 124 mode scripting.ExplainMode, 125 ) (e table.ScriptingYQLExplanation, err error) { 126 if c == nil { 127 return e, xerrors.WithStackTrace(errNilClient) 128 } 129 call := func(ctx context.Context) error { 130 e, err = c.explain(ctx, query, mode) 131 132 return xerrors.WithStackTrace(err) 133 } 134 if !c.config.AutoRetry() { 135 err = call(ctx) 136 137 return 138 } 139 err = retry.Retry(ctx, call, 140 retry.WithStackTrace(), 141 retry.WithIdempotent(true), 142 retry.WithTrace(c.config.TraceRetry()), 143 retry.WithBudget(c.config.RetryBudget()), 144 ) 145 146 return e, xerrors.WithStackTrace(err) 147 } 148 149 func (c *Client) explain( 150 ctx context.Context, 151 query string, 152 mode scripting.ExplainMode, 153 ) (e table.ScriptingYQLExplanation, err error) { 154 var ( 155 onDone = trace.ScriptingOnExplain(c.config.Trace(), &ctx, 156 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting.(*Client).explain"), 157 query, 158 ) 159 request = &Ydb_Scripting.ExplainYqlRequest{ 160 Script: query, 161 Mode: mode2mode(mode), 162 OperationParams: operation.Params( 163 ctx, 164 c.config.OperationTimeout(), 165 c.config.OperationCancelAfter(), 166 operation.ModeSync, 167 ), 168 } 169 response *Ydb_Scripting.ExplainYqlResponse 170 result = Ydb_Scripting.ExplainYqlResult{} 171 ) 172 defer func() { 173 onDone(e.Explanation.Plan, err) 174 }() 175 response, err = c.service.ExplainYql(ctx, request) 176 if err != nil { 177 return e, err 178 } 179 err = response.GetOperation().GetResult().UnmarshalTo(&result) 180 if err != nil { 181 return e, err 182 } 183 result.GetParametersTypes() 184 e = table.ScriptingYQLExplanation{ 185 Explanation: table.Explanation{ 186 Plan: result.GetPlan(), 187 }, 188 ParameterTypes: make(map[string]types.Type, len(result.GetParametersTypes())), 189 } 190 for k, v := range result.GetParametersTypes() { 191 e.ParameterTypes[k] = types.TypeFromYDB(v) 192 } 193 194 return e, nil 195 } 196 197 func (c *Client) StreamExecute( 198 ctx context.Context, 199 query string, 200 params *params.Parameters, 201 ) (r result.StreamResult, err error) { 202 if c == nil { 203 return r, xerrors.WithStackTrace(errNilClient) 204 } 205 call := func(ctx context.Context) error { 206 r, err = c.streamExecute(ctx, query, params) 207 208 return xerrors.WithStackTrace(err) 209 } 210 if !c.config.AutoRetry() { 211 err = call(ctx) 212 213 return 214 } 215 err = retry.Retry(ctx, call, 216 retry.WithStackTrace(), 217 retry.WithTrace(c.config.TraceRetry()), 218 retry.WithBudget(c.config.RetryBudget()), 219 ) 220 221 return r, xerrors.WithStackTrace(err) 222 } 223 224 //nolint:funlen 225 func (c *Client) streamExecute( 226 ctx context.Context, 227 query string, 228 parameters *params.Parameters, 229 ) (r result.StreamResult, err error) { 230 var ( 231 onIntermediate = trace.ScriptingOnStreamExecute(c.config.Trace(), &ctx, 232 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting.(*Client).streamExecute"), 233 query, parameters, 234 ) 235 a = allocator.New() 236 request = &Ydb_Scripting.ExecuteYqlRequest{ 237 Script: query, 238 Parameters: parameters.ToYDB(a), 239 OperationParams: operation.Params( 240 ctx, 241 c.config.OperationTimeout(), 242 c.config.OperationCancelAfter(), 243 operation.ModeSync, 244 ), 245 } 246 ) 247 defer func() { 248 a.Free() 249 if err != nil { 250 onIntermediate(err)(err) 251 } 252 }() 253 254 ctx, cancel := xcontext.WithCancel(ctx) 255 256 stream, err := c.service.StreamExecuteYql(ctx, request) 257 if err != nil { 258 cancel() 259 260 return nil, xerrors.WithStackTrace(err) 261 } 262 263 return scanner.NewStream(ctx, 264 func(ctx context.Context) ( 265 set *Ydb.ResultSet, 266 stats *Ydb_TableStats.QueryStats, 267 err error, 268 ) { 269 defer func() { 270 onIntermediate(xerrors.HideEOF(err)) 271 }() 272 select { 273 case <-ctx.Done(): 274 return nil, nil, xerrors.WithStackTrace(ctx.Err()) 275 default: 276 var response *Ydb_Scripting.ExecuteYqlPartialResponse 277 response, err = stream.Recv() 278 result := response.GetResult() 279 if result == nil || err != nil { 280 return nil, nil, xerrors.WithStackTrace(err) 281 } 282 283 return result.GetResultSet(), result.GetQueryStats(), nil 284 } 285 }, 286 func(err error) error { 287 cancel() 288 onIntermediate(xerrors.HideEOF(err))(xerrors.HideEOF(err)) 289 290 return err 291 }, 292 ) 293 } 294 295 func (c *Client) Close(ctx context.Context) (err error) { 296 if c == nil { 297 return xerrors.WithStackTrace(errNilClient) 298 } 299 onDone := trace.ScriptingOnClose(c.config.Trace(), &ctx, 300 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/scripting.(*Client).Close"), 301 ) 302 defer func() { 303 onDone(err) 304 }() 305 306 return nil 307 } 308 309 func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client { 310 return &Client{ 311 config: config, 312 service: Ydb_Scripting_V1.NewScriptingServiceClient(cc), 313 } 314 }